diff options
Diffstat (limited to 'libmailutils')
29 files changed, 1899 insertions, 672 deletions
diff --git a/libmailutils/Makefile.am b/libmailutils/Makefile.am index ed1aeb019..5aef3382d 100644 --- a/libmailutils/Makefile.am +++ b/libmailutils/Makefile.am @@ -16,7 +16,7 @@ # Public License along with this library. If not, see # <http://www.gnu.org/licenses/>. -SUBDIRS = auth base address cfg diag filter mailbox mailer mime\ +SUBDIRS = auth base address sockaddr cidr cfg diag filter mailbox mailer mime\ server string stream stdstream property url . tests lib_LTLIBRARIES = libmailutils.la @@ -28,6 +28,8 @@ libmailutils_la_LIBADD = \ auth/libauth.la\ base/libbase.la\ address/libaddress.la\ + sockaddr/libsockaddr.la\ + cidr/libcidr.la\ cfg/libcfg.la\ diag/libdiag.la\ filter/libfilter.la\ diff --git a/libmailutils/cidr/Makefile.am b/libmailutils/cidr/Makefile.am new file mode 100644 index 000000000..408806f79 --- /dev/null +++ b/libmailutils/cidr/Makefile.am @@ -0,0 +1,27 @@ +# GNU Mailutils -- a suite of utilities for electronic mail +# Copyright (C) 2011 Free Software Foundation, Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 3 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General +# Public License along with this library. If not, see +# <http://www.gnu.org/licenses/>. + +noinst_LTLIBRARIES = libcidr.la + +libcidr_la_SOURCES = \ + fromsa.c\ + fromstr.c\ + match.c\ + tosa.c\ + tostr.c + +INCLUDES = @MU_LIB_COMMON_INCLUDES@ -I/libmailutils diff --git a/libmailutils/cidr/fromsa.c b/libmailutils/cidr/fromsa.c new file mode 100644 index 000000000..a2fda6680 --- /dev/null +++ b/libmailutils/cidr/fromsa.c @@ -0,0 +1,101 @@ +/* GNU Mailutils -- a suite of utilities for electronic mail + Copyright (C) 2011 Free Software Foundation, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library. If not, see + <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <string.h> +#include <stdlib.h> +#include <mailutils/cidr.h> +#include <mailutils/errno.h> + +static void +uint32_to_bytes (unsigned char *bytes, uint32_t u) +{ + int i; + + for (i = 0; i < 4; i++) + { + bytes[i] = u & 0xff; + u >>= 8; + } +} + +int +_mu_inaddr_to_bytes (int af, void *buf, unsigned char *bytes) +{ + uint32_t u; + + switch (af) + { + case AF_INET: + memcpy (&u, buf, sizeof u); + uint32_to_bytes (bytes, u); + return 4; + +#ifdef MAILUTILS_IPV6 + case AF_INET6: + memcpy (bytes, buf, 16); + return 16; +#endif + } + return 0; +} + +int +_mu_sockaddr_to_bytes (unsigned char *bytes, struct sockaddr const *sa) +{ + switch (sa->sa_family) + { + case AF_INET: + uint32_to_bytes (bytes, ((struct sockaddr_in*)sa)->sin_addr.s_addr); + return 4; + +#ifdef MAILUTILS_IPV6 + case AF_INET6: + memcpy (bytes, &((struct sockaddr_in6*)sa)->sin6_addr, 16); + return 16; +#endif + } + return 0; +} + +int +mu_cidr_from_sockaddr (struct mu_cidr *cidr, const struct sockaddr *sa) +{ + unsigned char address[MU_INADDR_BYTES]; + int len; + int i; + + len = _mu_sockaddr_to_bytes (address, sa); + if (len == 0) + return MU_ERR_FAMILY; + cidr->family = sa->sa_family; + cidr->len = len; + memcpy (cidr->address, address, sizeof (cidr->address)); + for (i = 0; i < MU_INADDR_BYTES; i++) + cidr->netmask[i] = 0xff; + return 0; +} + + diff --git a/libmailutils/cidr/fromstr.c b/libmailutils/cidr/fromstr.c new file mode 100644 index 000000000..4e7a56f80 --- /dev/null +++ b/libmailutils/cidr/fromstr.c @@ -0,0 +1,128 @@ +/* GNU Mailutils -- a suite of utilities for electronic mail + Copyright (C) 2011 Free Software Foundation, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library. If not, see + <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +#include <string.h> +#include <stdlib.h> +#include <arpa/inet.h> +#include <mailutils/cidr.h> +#include <mailutils/errno.h> +#include <mailutils/sockaddr.h> + +static void +masklen_to_netmask (unsigned char *buf, size_t len, size_t masklen) +{ + int i, cnt; + + cnt = masklen / 8; + for (i = 0; i < cnt; i++) + buf[i] = 0xff; + if (i == MU_INADDR_BYTES) + return; + cnt = 8 - masklen % 8; + buf[i++] = (0xff >> cnt) << cnt; + for (; i < MU_INADDR_BYTES; i++) + buf[i] = 0; +} + +int +mu_cidr_from_string (struct mu_cidr *pcidr, const char *str) +{ + int rc; + char ipbuf[41]; + struct mu_cidr cidr; + char *p; + size_t len; + union + { + struct in_addr in; +#ifdef MAILUTILS_IPV6 + struct in6_addr in6; +#endif + } inaddr; + + p = strchr (str, '/'); + if (p) + len = p - str; + else + len = strlen (str); + + if (len > sizeof (ipbuf)) + return MU_ERR_BUFSPACE; + + memcpy (ipbuf, str, len); + ipbuf[len] = 0; + + if (mu_str_is_ipv4 (ipbuf)) + cidr.family = AF_INET; +#ifdef MAILUTILS_IPV6 + else if (mu_str_is_ipv6 (ipbuf)) + cidr.family = AF_INET6; +#endif + else + return MU_ERR_FAMILY; + + rc = inet_pton (cidr.family, ipbuf, &inaddr); + if (rc == -1) + return MU_ERR_FAMILY; + else if (rc == 0) + return MU_ERR_NONAME; + else if (rc != 1) + return MU_ERR_FAILURE; + + cidr.len = _mu_inaddr_to_bytes (cidr.family, &inaddr, cidr.address); + if (cidr.len == 0) + return MU_ERR_FAMILY; + + if (p) + { + char *end; + unsigned long masklen; + + p++; + + masklen = strtoul (p, &end, 10); + if (*end == 0) + masklen_to_netmask (cidr.netmask, cidr.len, masklen); + else if ((cidr.family == AF_INET && mu_str_is_ipv4 (p)) +#ifdef MAILUTILS_IPV6 + || (cidr.family == AF_INET6 && mu_str_is_ipv6 (ipbuf)) +#endif + ) + { + rc = inet_pton (cidr.family, p, &inaddr); + if (rc == -1) + return MU_ERR_FAMILY; + else if (rc == 0) + return MU_ERR_NONAME; + else if (rc != 1) + return MU_ERR_FAILURE; + + _mu_inaddr_to_bytes (cidr.family, &inaddr, cidr.netmask); + } + else + return MU_ERR_FAMILY; + } + else + masklen_to_netmask (cidr.netmask, cidr.len, cidr.len * 8); + + memcpy (pcidr, &cidr, sizeof (*pcidr)); + return 0; +} + diff --git a/libmailutils/cidr/match.c b/libmailutils/cidr/match.c new file mode 100644 index 000000000..dff9194ee --- /dev/null +++ b/libmailutils/cidr/match.c @@ -0,0 +1,38 @@ +/* GNU Mailutils -- a suite of utilities for electronic mail + Copyright (C) 2011 Free Software Foundation, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library. If not, see + <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +#include <mailutils/cidr.h> +#include <mailutils/errno.h> + +int +mu_cidr_match (struct mu_cidr *a, struct mu_cidr *b) +{ + int i; + + if (a->family != b->family) + return 1; + for (i = 0; i < a->len; i++) + { + if (a->address[i] != (b->address[i] & a->netmask[i])) + return 1; + } + return 0; +} + diff --git a/libmailutils/cidr/tosa.c b/libmailutils/cidr/tosa.c new file mode 100644 index 000000000..c14a995da --- /dev/null +++ b/libmailutils/cidr/tosa.c @@ -0,0 +1,78 @@ +/* GNU Mailutils -- a suite of utilities for electronic mail + Copyright (C) 2011 Free Software Foundation, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library. If not, see + <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <string.h> +#include <stdlib.h> +#include <mailutils/cidr.h> +#include <mailutils/errno.h> + +int +mu_cidr_to_sockaddr (struct mu_cidr *cidr, struct sockaddr **psa) +{ + union + { + struct sockaddr sa; + struct sockaddr_in s_in; +#ifdef MAILUTILS_IPV6 + struct sockaddr_in6 s_in6; +#endif + } addr; + struct sockaddr *sa; + int socklen; + int i; + + memset (&addr, 0, sizeof (addr)); + addr.sa.sa_family = cidr->family; + switch (cidr->family) + { + case AF_INET: + socklen = sizeof (addr.s_in); + for (i = 0; i < cidr->len; i++) + { + addr.s_in.sin_addr.s_addr <<= 8; + addr.s_in.sin_addr.s_addr |= cidr->address[i]; + } + break; + +#ifdef MAILUTILS_IPV6 + case AF_INET6: + socklen = sizeof (addr.s_in6); + memcpy (&addr.s_in6.sin6_addr, cidr->address, 16); + break; +#endif + + default: + return MU_ERR_FAMILY; + } + + sa = malloc (socklen); + if (!sa) + return ENOMEM; + memcpy (sa, &addr, socklen); + *psa = sa; + return 0; +} + diff --git a/libmailutils/cidr/tostr.c b/libmailutils/cidr/tostr.c new file mode 100644 index 000000000..3d1ee5f49 --- /dev/null +++ b/libmailutils/cidr/tostr.c @@ -0,0 +1,159 @@ +/* GNU Mailutils -- a suite of utilities for electronic mail + Copyright (C) 2011 Free Software Foundation, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library. If not, see + <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +#include <string.h> +#include <stdlib.h> +#include <mailutils/cidr.h> +#include <mailutils/errno.h> + +static int +to_xdig (unsigned char b) +{ + if (b >= 0xa) + return 'A' + b - 0xa; + else + return '0' + b; +} + +static size_t +format_ipv6_bytes (const unsigned char *bytes, int len, + char *buf, size_t size) +{ + size_t total = 0; + int i; + + for (i = 0; i < len; i += 2) + { + if (i) + { + if (total++ < size) + *buf++ = ':'; + } + if (total++ < size) + *buf++ = to_xdig (*bytes >> 4); + if (total++ < size) + *buf++ = to_xdig (*bytes & 0xf); + bytes++; + if (total++ < size) + *buf++ = to_xdig (*bytes >> 4); + if (total++ < size) + *buf++ = to_xdig (*bytes & 0xf); + bytes++; + } + return total; +} + +static size_t +format_ipv4_bytes (const unsigned char *bytes, int len, + char *buf, size_t size) +{ + int i; + size_t total = 0; + + for (i = 0; i < len; i++) + { + unsigned char b = *bytes++; + char nbuf[3]; + int j; + + if (i) + { + if (total++ < size) + *buf++ = '.'; + } + + j = 0; + do + { + nbuf[j++] = b % 10 + '0'; + b /= 10; + } + while (b); + + for (; j; j--) + { + if (total++ < size) + *buf++ = nbuf[j - 1]; + } + } + return total; +} + +int +mu_cidr_to_string (struct mu_cidr *cidr, int flags, + char *buf, size_t size, size_t *pret) +{ + size_t (*fmt) (const unsigned char *bytes, int len, char *buf, size_t size); + size_t n, total = 0; + + if (size == 0) + return MU_ERR_BUFSPACE; + size--; + switch (cidr->family) + { + case AF_INET: + fmt = format_ipv4_bytes; + break; + +#ifdef MAILUTILS_IPV6 + case AF_INET6: + fmt = format_ipv6_bytes; + break; +#endif + + default: + return MU_ERR_FAMILY; + } + + n = fmt (cidr->address, cidr->len, buf, size); + if (buf) + buf += n; + total += n; + + if (!(flags & MU_CIDR_FMT_ADDRONLY)) + { + if (total++ < size) + *buf++ = '/'; + n = fmt (cidr->netmask, cidr->len, buf, size - total); + if (buf) + buf += n; + total += n; + } + + if (buf) + *buf++ = 0; + if (pret) + *pret = total; + return 0; +} + +int +mu_cidr_format (struct mu_cidr *cidr, int flags, char **pbuf) +{ + char buf[MU_CIDR_MAXBUFSIZE]; + int rc = mu_cidr_to_string (cidr, flags, buf, sizeof (buf), NULL); + if (rc) + return rc; + *pbuf = strdup (buf); + if (!*buf) + return ENOMEM; + return 0; +} + diff --git a/libmailutils/diag/debug.c b/libmailutils/diag/debug.c index 124311bd8..aa10e41ba 100644 --- a/libmailutils/diag/debug.c +++ b/libmailutils/diag/debug.c @@ -689,6 +689,7 @@ mu_debug_log_begin (const char *fmt, ...) va_list ap; mu_diag_init (); + mu_stream_flush (mu_strerr); va_start (ap, fmt); mu_stream_printf (mu_strerr, "\033s<%d>", MU_LOG_DEBUG); mu_stream_vprintf (mu_strerr, fmt, ap); diff --git a/libmailutils/diag/errors b/libmailutils/diag/errors index 869e4146f..03f71b715 100644 --- a/libmailutils/diag/errors +++ b/libmailutils/diag/errors @@ -1,3 +1,4 @@ + # Error messages for GNU Mailutils # Copyright (C) 2005, 2006, 2007, 2010, 2011 Free Software Foundation, # Inc. @@ -98,3 +99,10 @@ MU_ERR_URL_MISS_PARTS _("URL missing required parts") MU_ERR_URL_EXTRA_PARTS _("URL has parts not allowed by its scheme") MU_ERR_INFO_UNAVAILABLE _("Information is not yet available") + +# The following are mapped to the corresponding EAI_ errors +MU_ERR_NONAME _("Name or service not known") +MU_ERR_BADFLAGS _("Bad value for flags") +MU_ERR_SOCKTYPE _("Socket type not supported") +MU_ERR_FAMILY _("Address family not supported") +MU_ERR_SERVICE _("Requested service not supported") diff --git a/libmailutils/server/acl.c b/libmailutils/server/acl.c index 0b7a507e0..ba1a4b86b 100644 --- a/libmailutils/server/acl.c +++ b/libmailutils/server/acl.c @@ -40,14 +40,20 @@ #include <mailutils/kwd.h> #include <mailutils/io.h> #include <mailutils/util.h> +#include <mailutils/sockaddr.h> +#include <mailutils/cidr.h> +#include <mailutils/stream.h> +#include <mailutils/stdstream.h> + +#ifndef MU_INADDR_BYTES +#define MU_INADDR_BYTES 16 +#endif struct _mu_acl_entry { mu_acl_action_t action; void *arg; - unsigned netmask; - int salen; - struct sockaddr sa[1]; + struct mu_cidr cidr; }; struct _mu_acl @@ -64,52 +70,19 @@ _destroy_acl_entry (void *item) /* FIXME: free arg? */ } -static size_t -mu_acl_entry_size (int salen) -{ - return sizeof (struct _mu_acl_entry) + salen - sizeof (struct sockaddr); -} - -static int -prepare_sa (struct sockaddr *sa) -{ - switch (sa->sa_family) - { - case AF_INET: - { - struct sockaddr_in *s_in = (struct sockaddr_in *)sa; - s_in->sin_addr.s_addr = ntohl (s_in->sin_addr.s_addr); - break; - } - - case AF_UNIX: - break; - - default: - return 1; - } - return 0; -} - int mu_acl_entry_create (struct _mu_acl_entry **pent, mu_acl_action_t action, void *data, - struct sockaddr *sa, int salen, unsigned long netmask) + struct mu_cidr *cidr) { - struct _mu_acl_entry *p = malloc (mu_acl_entry_size (salen)); + struct _mu_acl_entry *p = malloc (sizeof (*p)); if (!p) return EINVAL; p->action = action; p->arg = data; - p->netmask = ntohl (netmask); - p->salen = salen; - memcpy (p->sa, sa, salen); - if (prepare_sa (p->sa)) - { - free (p); - return EINVAL; - } + memcpy (&p->cidr, cidr, sizeof (p->cidr)); + *pent = p; return 0; } @@ -165,15 +138,14 @@ mu_acl_get_iterator (mu_acl_t acl, mu_iterator_t *pitr) int mu_acl_append (mu_acl_t acl, mu_acl_action_t act, - void *data, struct sockaddr *sa, int salen, - unsigned long netmask) + void *data, struct mu_cidr *cidr) { int rc; struct _mu_acl_entry *ent; if (!acl) return EINVAL; - rc = mu_acl_entry_create (&ent, act, data, sa, salen, netmask); + rc = mu_acl_entry_create (&ent, act, data, cidr); if (rc) { mu_debug (MU_DEBCAT_ACL, MU_DEBUG_ERROR, @@ -193,14 +165,14 @@ mu_acl_append (mu_acl_t acl, mu_acl_action_t act, int mu_acl_prepend (mu_acl_t acl, mu_acl_action_t act, void *data, - struct sockaddr *sa, int salen, unsigned long netmask) + struct mu_cidr *cidr) { int rc; struct _mu_acl_entry *ent; if (!acl) return EINVAL; - rc = mu_acl_entry_create (&ent, act, data, sa, salen, netmask); + rc = mu_acl_entry_create (&ent, act, data, cidr); if (rc) { mu_debug (MU_DEBCAT_ACL, MU_DEBUG_ERROR, @@ -219,8 +191,7 @@ mu_acl_prepend (mu_acl_t acl, mu_acl_action_t act, void *data, int mu_acl_insert (mu_acl_t acl, size_t pos, int before, - mu_acl_action_t act, void *data, - struct sockaddr *sa, int salen, unsigned long netmask) + mu_acl_action_t act, void *data, struct mu_cidr *cidr) { int rc; void *ptr; @@ -236,7 +207,7 @@ mu_acl_insert (mu_acl_t acl, size_t pos, int before, ("No such entry %lu", (unsigned long) pos)); return rc; } - rc = mu_acl_entry_create (&ent, act, data, sa, salen, netmask); + rc = mu_acl_entry_create (&ent, act, data, cidr); if (!ent) { mu_debug (MU_DEBCAT_ACL, MU_DEBUG_ERROR, @@ -279,124 +250,19 @@ mu_acl_string_to_action (const char *str, mu_acl_action_t *pres) return rc; } -#define MU_S_UN_NAME(sa, salen) \ - ((salen < mu_offsetof (struct sockaddr_un,sun_path)) ? "" : (sa)->sun_path) - -static void -debug_sockaddr (struct sockaddr *sa, int salen) -{ - switch (sa->sa_family) - { - case AF_INET: - { - struct sockaddr_in s_in = *(struct sockaddr_in *)sa; - s_in.sin_addr.s_addr = htonl (s_in.sin_addr.s_addr); - mu_debug_log_cont ("{AF_INET %s:%d}", - inet_ntoa (s_in.sin_addr), ntohs (s_in.sin_port)); - break; - } - - case AF_UNIX: - { - struct sockaddr_un *s_un = (struct sockaddr_un *)sa; - if (MU_S_UN_NAME(s_un, salen)[0] == 0) - mu_debug_log_cont ("{AF_UNIX}"); - else - mu_debug_log_cont ("{AF_UNIX %s}", s_un->sun_path); - break; - } - - default: - mu_debug_log_cont ("{Unsupported family: %d}", sa->sa_family); - } -} - -size_t -mu_stpcpy (char **pbuf, size_t *psize, const char *src) -{ - size_t slen = strlen (src); - if (pbuf == NULL || *pbuf == NULL) - return slen; - else - { - char *buf = *pbuf; - size_t size = *psize; - if (size > slen) - size = slen; - memcpy (buf, src, size); - *psize -= size; - *pbuf += size; - if (*psize) - **pbuf = 0; - else - (*pbuf)[-1] = 0; - return size; - } -} - -void -mu_sockaddr_to_str (const struct sockaddr *sa, int salen, - char *bufptr, size_t buflen, - size_t *plen) +struct run_closure { - char *nbuf; - size_t len = 0; - switch (sa->sa_family) - { - case AF_INET: - { - struct sockaddr_in s_in = *(struct sockaddr_in *)sa; - len += mu_stpcpy (&bufptr, &buflen, inet_ntoa (s_in.sin_addr)); - len += mu_stpcpy (&bufptr, &buflen, ":"); - if (mu_asprintf (&nbuf, "%hu", ntohs (s_in.sin_port)) == 0) - { - len += mu_stpcpy (&bufptr, &buflen, nbuf); - free (nbuf); - } - break; - } - - case AF_UNIX: - { - struct sockaddr_un *s_un = (struct sockaddr_un *)sa; - if (MU_S_UN_NAME(s_un, salen)[0] == 0) - len += mu_stpcpy (&bufptr, &buflen, "anonymous socket"); - else - { - len += mu_stpcpy (&bufptr, &buflen, "socket "); - len += mu_stpcpy (&bufptr, &buflen, s_un->sun_path); - } - break; - } - - default: - len += mu_stpcpy (&bufptr, &buflen, "{Unsupported family"); - if (mu_asprintf (&nbuf, ": %d", sa->sa_family) == 0) - { - len += mu_stpcpy (&bufptr, &buflen, nbuf); - free (nbuf); - } - len += mu_stpcpy (&bufptr, &buflen, "}"); - } - if (plen) - *plen = len + 1; -} + unsigned idx; + struct mu_cidr addr; -char * -mu_sockaddr_to_astr (const struct sockaddr *sa, int salen) -{ - size_t size; - char *p; - - mu_sockaddr_to_str (sa, salen, NULL, 0, &size); - p = malloc (size); - if (p) - mu_sockaddr_to_str (sa, salen, p, size, NULL); - return p; -} + char ipstr[40]; + char *addrstr; + char *numbuf; + mu_acl_result_t *result; +}; int -_acl_match (struct _mu_acl_entry *ent, struct sockaddr *sa, int salen) +_acl_match (struct _mu_acl_entry *ent, struct run_closure *rp) { #define RESMATCH(word) \ if (mu_debug_level_p (MU_DEBCAT_ACL, MU_DEBUG_TRACE9)) \ @@ -404,76 +270,26 @@ _acl_match (struct _mu_acl_entry *ent, struct sockaddr *sa, int salen) if (mu_debug_level_p (MU_DEBCAT_ACL, MU_DEBUG_TRACE9)) { - struct in_addr a; - - mu_debug_log_begin ("Does "); - debug_sockaddr (sa, salen); - mu_debug_log_cont (" match "); - debug_sockaddr (ent->sa, salen); - a.s_addr = ent->netmask; - a.s_addr = htonl (a.s_addr); - mu_debug_log_cont (" netmask %s? ", inet_ntoa (a)); + char *s; + + if (ent->cidr.len == 0) + s = strdup ("any"); + mu_cidr_format (&ent->cidr, 0, &s); + if (!rp->addrstr) + mu_cidr_format (&rp->addr, MU_CIDR_FMT_ADDRONLY, &rp->addrstr); + mu_debug_log_begin ("Does %s match %s? ", s, rp->addrstr); + free (s); } - if (ent->sa->sa_family != sa->sa_family) + if (ent->cidr.len > 0 && mu_cidr_match (&ent->cidr, &rp->addr)) { RESMATCH ("no"); return 1; } - - switch (ent->sa->sa_family) - { - case AF_INET: - { - struct sockaddr_in *sin_ent = (struct sockaddr_in *)ent->sa; - struct sockaddr_in *sin_item = (struct sockaddr_in *)sa; - - if (sin_ent->sin_addr.s_addr != - (sin_item->sin_addr.s_addr & ent->netmask)) - { - RESMATCH ("no (address differs)"); - return 1; - } - - if (sin_ent->sin_port && sin_item->sin_port - && sin_ent->sin_port != sin_item->sin_port) - { - RESMATCH ("no (port differs)"); - return 1; - } - break; - } - - case AF_UNIX: - { - struct sockaddr_un *sun_ent = (struct sockaddr_un *)ent->sa; - struct sockaddr_un *sun_item = (struct sockaddr_un *)sa; - - if (MU_S_UN_NAME (sun_ent, ent->salen)[0] - && MU_S_UN_NAME (sun_item, salen)[0] - && strcmp (sun_ent->sun_path, sun_item->sun_path)) - { - RESMATCH ("no"); - return 1; - } - break; - } - } - RESMATCH ("yes"); return 0; } -struct run_closure -{ - unsigned idx; - struct sockaddr *sa; - char *numbuf; - char *portbuf; - int salen; - mu_acl_result_t *result; -}; - #define SEQ(s, n, l) \ (((l) == (sizeof(s) - 1)) && memcmp (s, n, l) == 0) @@ -489,40 +305,24 @@ acl_getvar (const char *name, size_t nlen, void *data) return rp->numbuf; } - switch (rp->sa->sa_family) + if (SEQ ("address", name, nlen)) { - case AF_INET: - { - struct sockaddr_in *s_in = (struct sockaddr_in *)rp->sa; - - if (SEQ ("address", name, nlen)) - { - struct in_addr addr = s_in->sin_addr; - addr.s_addr = htonl (addr.s_addr); - return inet_ntoa (addr); - } + if (!rp->addrstr) + mu_cidr_format (&rp->addr, MU_CIDR_FMT_ADDRONLY, &rp->addrstr); + return rp->addrstr; + } - if (SEQ ("port", name, nlen)) - { - if (!rp->portbuf && - mu_asprintf (&rp->portbuf, "%hu", ntohs (s_in->sin_port))) - return NULL; - return rp->portbuf; - } - break; - - case AF_UNIX: - if (SEQ ("address", name, nlen)) - { - struct sockaddr_un *s_un = (struct sockaddr_un *)rp->sa; - if (rp->salen == sizeof (s_un->sun_family)) - return NULL; - else - return s_un->sun_path; - } - } - break; +#if 0 + /* FIXME?: */ + if (SEQ ("port", name, nlen)) + { + if (!rp->portbuf && + mu_asprintf (&rp->portbuf, "%hu", ntohs (s_in->sin_port))) + return NULL; + return rp->portbuf; } +#endif + return NULL; } @@ -535,12 +335,18 @@ expand_arg (const char *cmdline, struct run_closure *rp, char **s) mu_debug (MU_DEBCAT_ACL, MU_DEBUG_TRACE, ("Expanding \"%s\"", cmdline)); env[0] = "family"; - switch (rp->sa->sa_family) + switch (rp->addr.family) { case AF_INET: env[1] = "AF_INET"; break; +#ifdef MAILUTILS_IPV6 + case AF_INET6: + env[1] = "AF_INET6"; + break; +#endif + case AF_UNIX: env[1] = "AF_UNIX"; break; @@ -654,7 +460,7 @@ _run_entry (void *item, void *data) mu_debug_log_begin ("%d:%s: ", rp->idx, s); } - if (_acl_match (ent, rp->sa, rp->salen) == 0) + if (_acl_match (ent, rp) == 0) { switch (ent->action) { @@ -678,8 +484,10 @@ _run_entry (void *item, void *data) } else { - debug_sockaddr (rp->sa, rp->salen); - mu_debug_log_nl (); + if (!rp->addrstr) + mu_cidr_format (&rp->addr, MU_CIDR_FMT_ADDRONLY, + &rp->addrstr); + mu_diag_output (MU_DIAG_INFO, "%s", rp->addrstr); } } break; @@ -712,7 +520,7 @@ _run_entry (void *item, void *data) } if (mu_debug_level_p (MU_DEBCAT_ACL, MU_DEBUG_TRACE9)) - mu_debug_log_nl (); + mu_stream_flush (mu_strerr); return status; } @@ -721,40 +529,39 @@ int mu_acl_check_sockaddr (mu_acl_t acl, const struct sockaddr *sa, int salen, mu_acl_result_t *pres) { + int rc; struct run_closure r; if (!acl) return EINVAL; - r.sa = malloc (salen); - if (!r.sa) - return ENOMEM; - memcpy (r.sa, sa, salen); - if (prepare_sa (r.sa)) + memset (&r, 0, sizeof (r)); + if (sa->sa_family == AF_UNIX) { - free (r.sa); - return EINVAL; + *pres = mu_acl_result_accept; + return 0; } - r.salen = salen; + rc = mu_cidr_from_sockaddr (&r.addr, sa); + if (rc) + return rc; if (mu_debug_level_p (MU_DEBCAT_ACL, MU_DEBUG_TRACE9)) { - mu_debug_log_begin ("Checking sockaddr "); - debug_sockaddr (r.sa, r.salen); + mu_cidr_format (&r.addr, MU_CIDR_FMT_ADDRONLY, &r.addrstr); + mu_debug_log_begin ("Checking sockaddr %s", r.addrstr); mu_debug_log_nl (); } r.idx = 0; r.result = pres; *r.result = mu_acl_result_undefined; - r.numbuf = r.portbuf = NULL; + r.numbuf = NULL; mu_list_do (acl->aclist, _run_entry, &r); free (r.numbuf); - free (r.portbuf); - free (r.sa); + free (r.addrstr); return 0; } - + int mu_acl_check_inaddr (mu_acl_t acl, const struct in_addr *inp, mu_acl_result_t *pres) @@ -780,10 +587,17 @@ mu_acl_check_ipv4 (mu_acl_t acl, unsigned int addr, mu_acl_result_t *pres) int mu_acl_check_fd (mu_acl_t acl, int fd, mu_acl_result_t *pres) { - struct sockaddr_in cs; - socklen_t len = sizeof cs; + union + { + struct sockaddr sa; + struct sockaddr_in in; +#ifdef MAILUTILS_IPV6 + struct sockaddr_in6 in6; +#endif + } addr; + socklen_t len = sizeof addr; - if (getpeername (fd, (struct sockaddr *) &cs, &len) < 0) + if (getpeername (fd, &addr.sa, &len) < 0) { mu_debug (MU_DEBCAT_ACL, MU_DEBUG_ERROR, ("Cannot obtain IP address of client: %s", @@ -791,6 +605,6 @@ mu_acl_check_fd (mu_acl_t acl, int fd, mu_acl_result_t *pres) return MU_ERR_FAILURE; } - return mu_acl_check_sockaddr (acl, (struct sockaddr *) &cs, len, pres); + return mu_acl_check_sockaddr (acl, &addr.sa, len, pres); } diff --git a/libmailutils/server/ipsrv.c b/libmailutils/server/ipsrv.c index a80290834..73b2e361e 100644 --- a/libmailutils/server/ipsrv.c +++ b/libmailutils/server/ipsrv.c @@ -34,13 +34,13 @@ #include <mailutils/diag.h> #include <mailutils/errno.h> #include <mailutils/nls.h> +#include <mailutils/sockaddr.h> struct _mu_ip_server { char *ident; - struct sockaddr *addr; - int addrlen; + struct mu_sockaddr *addr; int fd; int type; mu_acl_t acl; @@ -66,8 +66,7 @@ struct _mu_ip_server #define IDENTSTR(s) ((s)->ident ? (s)->ident : "default") int -mu_ip_server_create (mu_ip_server_t *psrv, struct sockaddr *addr, - int addrlen, int type) +mu_ip_server_create (mu_ip_server_t *psrv, struct mu_sockaddr *addr, int type) { struct _mu_ip_server *srv; @@ -84,14 +83,7 @@ mu_ip_server_create (mu_ip_server_t *psrv, struct sockaddr *addr, srv = calloc (1, sizeof *srv); if (!srv) return ENOMEM; - srv->addr = calloc (1, addrlen); - if (!srv->addr) - { - free (srv); - return ENOMEM; - } - memcpy (srv->addr, addr, addrlen); - srv->addrlen = addrlen; + srv->addr = addr; srv->type = type; srv->fd = -1; switch (type) @@ -120,7 +112,7 @@ mu_ip_server_destroy (mu_ip_server_t *psrv) if (srv->f_free) srv->f_free (srv->data); close (srv->fd); - free (srv->addr); + mu_sockaddr_free (srv->addr); free (srv->ident); if (srv->type == MU_IP_UDP && srv->v.udp_data.buf) free (srv->v.udp_data.buf); @@ -234,6 +226,11 @@ mu_address_family_to_domain (int family) case AF_INET: return PF_INET; +#ifdef MAILUTILS_IPV6 + case AF_INET6: + return PF_INET6; +#endif + default: abort (); } @@ -247,14 +244,11 @@ mu_ip_server_open (mu_ip_server_t srv) if (!srv || srv->fd != -1) return EINVAL; - if (mu_debug_level_p (MU_DEBCAT_SERVER, MU_DEBUG_TRACE0)) - { - char *p = mu_sockaddr_to_astr (srv->addr, srv->addrlen); - mu_debug_log ("opening server \"%s\" %s", IDENTSTR (srv), p); - free (p); - } + mu_debug (MU_DEBCAT_SERVER, MU_DEBUG_TRACE0, + ("opening server \"%s\" %s", IDENTSTR (srv), + mu_sockaddr_str (srv->addr))); - fd = socket (mu_address_family_to_domain (srv->addr->sa_family), + fd = socket (mu_address_family_to_domain (srv->addr->addr->sa_family), ((srv->type == MU_IP_UDP) ? SOCK_DGRAM : SOCK_STREAM), 0); if (fd == -1) { @@ -263,7 +257,7 @@ mu_ip_server_open (mu_ip_server_t srv) return errno; } - switch (srv->addr->sa_family) + switch (srv->addr->addr->sa_family) { case AF_UNIX: { @@ -299,7 +293,7 @@ mu_ip_server_open (mu_ip_server_t srv) } break; - case AF_INET: + default: { int t; @@ -308,7 +302,7 @@ mu_ip_server_open (mu_ip_server_t srv) } } - if (bind (fd, srv->addr, srv->addrlen) == -1) + if (bind (fd, srv->addr->addr, srv->addr->addrlen) == -1) { mu_debug (MU_DEBCAT_SERVER, MU_DEBUG_ERROR, ("%s: bind: %s", IDENTSTR (srv), mu_strerror (errno))); @@ -336,12 +330,9 @@ mu_ip_server_shutdown (mu_ip_server_t srv) { if (!srv || srv->fd != -1) return EINVAL; - if (mu_debug_level_p (MU_DEBCAT_SERVER, MU_DEBUG_TRACE0)) - { - char *p = mu_sockaddr_to_astr (srv->addr, srv->addrlen); - mu_debug_log ("closing server \"%s\" %s", IDENTSTR (srv), p); - free (p); - } + mu_debug (MU_DEBCAT_SERVER, MU_DEBUG_TRACE0, + ("closing server \"%s\" %s", IDENTSTR (srv), + mu_sockaddr_str (srv->addr))); close (srv->fd); return 0; } @@ -356,6 +347,9 @@ mu_ip_tcp_accept (mu_ip_server_t srv, void *call_data) struct sockaddr sa; struct sockaddr_in s_in; struct sockaddr_un s_un; +#ifdef MAILUTILS_IPV6 + struct sockaddr_in6 s_in6; +#endif } client; socklen_t size = sizeof (client); @@ -407,6 +401,9 @@ mu_ip_udp_accept (mu_ip_server_t srv, void *call_data) struct sockaddr sa; struct sockaddr_in s_in; struct sockaddr_un s_un; +#ifdef MAILUTILS_IPV6 + struct sockaddr_in6 s_in6; +#endif } client; fd_set rdset; @@ -463,7 +460,7 @@ mu_ip_udp_accept (mu_ip_server_t srv, void *call_data) IDENTSTR (srv), strerror (rc))); if (res == mu_acl_result_deny) { - char *p = mu_sockaddr_to_astr (srv->addr, srv->addrlen); + char *p = mu_sockaddr_to_astr (&client.sa, salen); mu_diag_output (MU_DIAG_INFO, "Denying connection from %s", p); free (p); return 0; @@ -528,22 +525,16 @@ mu_udp_server_get_rdata (mu_ip_server_t srv, char **pbuf, size_t *pbufsize) } int -mu_ip_server_get_sockaddr (mu_ip_server_t srv, struct sockaddr *s, int *size) +mu_ip_server_get_sockaddr (mu_ip_server_t srv, struct mu_sockaddr **psa) { - int len; - - if (!srv || !s) + if (!srv || !psa) return EINVAL; - if (s == 0) - len = srv->addrlen; - else - { - len = srv->addrlen; - if (*size < len) - return MU_ERR_BUFSPACE; - memcpy (s, srv->addr, len); - } - *size = len; - return 0; + return mu_sockaddr_copy (psa, srv->addr); } - + +const char * +mu_ip_server_addrstr (mu_ip_server_t srv) +{ + return mu_sockaddr_str (srv->addr); +} + diff --git a/libmailutils/server/msrv.c b/libmailutils/server/msrv.c index 5c46e9d70..d5ceb8a39 100644 --- a/libmailutils/server/msrv.c +++ b/libmailutils/server/msrv.c @@ -41,6 +41,8 @@ #include <mailutils/nls.h> #include <mailutils/daemon.h> #include <mailutils/acl.h> +#include <mailutils/sockaddr.h> +#include <mailutils/url.h> typedef RETSIGTYPE (*mu_sig_handler_t) (int); @@ -65,19 +67,6 @@ set_signal (int sig, mu_sig_handler_t handler) # define NSIG 64 #endif -union m_sockaddr -{ - struct sockaddr s_sa; - struct sockaddr_in s_in; - struct sockaddr_un s_un; -}; - -struct m_default_address -{ - union m_sockaddr s; - int len; -}; - struct _mu_m_server { char *ident; /* Server identifier, for logging purposes.*/ @@ -98,7 +87,7 @@ struct _mu_m_server size_t num_children; /* Current number of running sub-processes. */ pid_t *child_pid; char *pidfile; /* Name of a PID-file. */ - struct m_default_address defaddr; /* Default address. */ + struct mu_sockaddr_hints hints; /* Default address hints. */ time_t timeout; /* Default idle timeout. */ mu_acl_t acl; /* Global access control list. */ @@ -112,7 +101,7 @@ struct _mu_m_server struct m_srv_config /* Configuration data for a single TCP server. */ { mu_m_server_t msrv; /* Parent m-server. */ - mu_ip_server_t tcpsrv; /* TCP server these data are for. */ + mu_ip_server_t tcpsrv; /* TCP server these data are for. */ mu_acl_t acl; /* Access control list for this server. */ int single_process; /* Should it run as a single process? */ int transcript; /* Enable session transcript. */ @@ -395,33 +384,20 @@ mu_m_server_pidfile (mu_m_server_t srv) } void -mu_m_server_set_default_address (mu_m_server_t srv, struct sockaddr *sa, - int salen) +mu_m_server_set_hints (mu_m_server_t srv, struct mu_sockaddr_hints *hints) { - if (salen > sizeof srv->defaddr.s) - { - mu_error (_("unhandled sockaddr size")); - abort (); - } - memcpy (&srv->defaddr.s.s_sa, sa, salen); - srv->defaddr.len = salen; + if (!hints) + memset (&srv->hints, 0, sizeof (srv->hints)); + else + memcpy (&srv->hints, hints, sizeof (srv->hints)); } int -mu_m_server_get_default_address (mu_m_server_t srv, struct sockaddr *sa, - int *salen) +mu_m_server_get_hints (mu_m_server_t srv, struct mu_sockaddr_hints *hints) { - int len; - - if (!sa) + if (!hints) return EINVAL; - len = srv->defaddr.len; - if (sa) - { - if (*salen < len) - return MU_ERR_BUFSPACE; - memcpy (sa, &srv->defaddr.s.s_sa, len); - } + memcpy (hints, &srv->hints, sizeof (hints)); return 0; } @@ -429,11 +405,7 @@ mu_m_server_get_default_address (mu_m_server_t srv, struct sockaddr *sa, void mu_m_server_set_default_port (mu_m_server_t srv, int num) { - struct sockaddr_in s_in; - s_in.sin_family = AF_INET; - s_in.sin_addr.s_addr = htonl (INADDR_ANY); - s_in.sin_port = htons (num); - mu_m_server_set_default_address (srv, (struct sockaddr*) &s_in, sizeof s_in); + srv->hints.port = num; } void @@ -473,12 +445,12 @@ static int m_srv_conn (int fd, struct sockaddr *sa, int salen, mu_ip_server_t srv); static struct m_srv_config * -add_server (mu_m_server_t msrv, struct sockaddr *s, int slen, int type) +add_server (mu_m_server_t msrv, struct mu_sockaddr *s, int type) { mu_ip_server_t tcpsrv; struct m_srv_config *pconf; - MU_ASSERT (mu_ip_server_create (&tcpsrv, s, slen, type)); /* FIXME: type */ + MU_ASSERT (mu_ip_server_create (&tcpsrv, s, type)); /* FIXME: type */ MU_ASSERT (mu_ip_server_set_conn (tcpsrv, m_srv_conn)); pconf = calloc (1, sizeof (*pconf)); if (!pconf) @@ -513,8 +485,21 @@ mu_m_server_begin (mu_m_server_t msrv) alloc_children (msrv); mu_list_count (msrv->srvlist, &count); - if (count == 0 && msrv->defaddr.len) - add_server (msrv, &msrv->defaddr.s.s_sa, msrv->defaddr.len, msrv->deftype); + if (count == 0) + { + struct mu_sockaddr *ta; + + msrv->hints.flags = MU_AH_PASSIVE; + rc = mu_sockaddr_from_node (&ta, NULL, NULL, &msrv->hints); + if (rc == 0) + while (ta) + { + struct mu_sockaddr *next = ta->next; + ta->next = ta->prev = NULL; + add_server (msrv, ta, msrv->deftype); + ta = next; + } + } if (!msrv->foreground) { @@ -599,23 +584,13 @@ tcp_conn_free (void *conn_data, void *server_data) static int _open_conn (void *item, void *data) { - union - { - struct sockaddr sa; - char pad[512]; - } - addr; - int addrlen = sizeof addr; - char *p; mu_ip_server_t tcpsrv = item; mu_m_server_t msrv = data; int rc = mu_ip_server_open (tcpsrv); if (rc) { - mu_ip_server_get_sockaddr (tcpsrv, &addr.sa, &addrlen); - p = mu_sockaddr_to_astr (&addr.sa, addrlen); - mu_error (_("cannot open connection on %s: %s"), p, mu_strerror (rc)); - free (p); + mu_error (_("cannot open connection on %s: %s"), + mu_ip_server_addrstr (tcpsrv), mu_strerror (rc)); return 0; } rc = mu_server_add_connection (msrv->server, @@ -624,10 +599,8 @@ _open_conn (void *item, void *data) tcp_conn_handler, tcp_conn_free); if (rc) { - mu_ip_server_get_sockaddr (tcpsrv, &addr.sa, &addrlen); - p = mu_sockaddr_to_astr (&addr.sa, addrlen); - mu_error (_("cannot add connection %s: %s"), p, mu_strerror (rc)); - free (p); + mu_error (_("cannot add connection %s: %s"), + mu_ip_server_addrstr (tcpsrv), mu_strerror (rc)); mu_ip_server_shutdown (tcpsrv); mu_ip_server_destroy (&tcpsrv); } @@ -756,199 +729,55 @@ m_srv_conn (int fd, struct sockaddr *sa, int salen, } - -unsigned short -get_port (const char *p) -{ - if (p) - { - char *q; - unsigned long n = strtoul (p, &q, 0); - if (*q == 0) - { - if (n > USHRT_MAX) - { - mu_error (_("invalid port number: %s"), p); - return 1; - } - - return htons (n); - } - else - { - struct servent *sp = getservbyname (p, "tcp"); - if (!sp) - return 0; - return sp->s_port; - } - } - return 0; -} - -static int -get_family (const char **pstr, sa_family_t *pfamily) -{ - static struct family_tab - { - int len; - char *pfx; - int family; - } ftab[] = { -#define S(s,f) { sizeof (#s":") - 1, #s":", f } - S (file, AF_UNIX), - S (unix, AF_UNIX), - S (local, AF_UNIX), - S (socket, AF_UNIX), - S (inet, AF_INET), - S (tcp, AF_INET), -#undef S - { 0 } - }; - struct family_tab *fp; - - const char *str = *pstr; - int len = strlen (str); - for (fp = ftab; fp->len; fp++) - { - if (len > fp->len && memcmp (str, fp->pfx, fp->len) == 0) - { - str += fp->len; - if (str[0] == '/' && str[1] == '/') - str += 2; - *pstr = str; - *pfamily = fp->family; - return 0; - } - } - return 1; -} - -static int -is_ip_addr (const char *arg) -{ - int dot_count; - int digit_count; - - dot_count = 0; - digit_count = 0; - for (; *arg != 0 && *arg != ':'; arg++) - { - if (*arg == '.') - { - if (++dot_count > 3) - break; - digit_count = 0; - } - else if (!(mu_isdigit (*arg) && ++digit_count <= 3)) - return 0; - } - return dot_count == 3; -} - int -_mu_m_server_parse_url (const char *arg, union m_sockaddr *s, - int *psalen, struct sockaddr *defsa) +mu_m_server_parse_url (mu_m_server_t msrv, const char *arg, + struct mu_sockaddr **psa) { - char *p; - unsigned short n; - int len; - - if (is_ip_addr (arg)) - s->s_sa.sa_family = AF_INET; - else if (get_family (&arg, &s->s_sa.sa_family)) + int rc; + mu_url_t url, url_hint; + + if (arg[0] == '/') + url_hint = NULL; + else { - mu_error (_("invalid family")); - return EINVAL; + rc = mu_url_create (&url_hint, "inet://"); + if (rc) + return rc; } - - switch (s->s_sa.sa_family) + rc = mu_url_create_hint (&url, arg, MU_URL_PARSE_DEFAULT, url_hint); + mu_url_destroy (&url_hint); + if (rc) { - case AF_INET: - *psalen = sizeof (s->s_in); - if ((n = get_port (arg))) - { - s->s_in.sin_addr.s_addr = htonl (INADDR_ANY); - s->s_in.sin_port = htons (n); - } - else - { - p = strchr (arg, ':'); - if (p) - *p++ = 0; - if (inet_aton (arg, &s->s_in.sin_addr) == 0) - { - struct hostent *hp = gethostbyname (arg); - if (hp) - s->s_in.sin_addr.s_addr = *(unsigned long *)hp->h_addr; - else - { - mu_error (_("invalid IP address: %s"), arg); - return EINVAL; - } - } - if (p) - { - n = get_port (p); - if (!n) - { - mu_error (_("invalid port number: %s"), p); - return EINVAL; - } - s->s_in.sin_port = n; - } - else if (defsa && defsa->sa_family == AF_INET) - s->s_in.sin_port = ((struct sockaddr_in*)defsa)->sin_port; - else - { - mu_error (_("missing port number")); - return EINVAL; - } - } - break; - - case AF_UNIX: - *psalen = sizeof (s->s_un); - len = strlen (arg); - if (len > sizeof s->s_un.sun_path - 1) - { - mu_error (_("%s: file name too long"), arg); - return EINVAL; - } - strcpy (s->s_un.sun_path, arg); - break; + mu_error (_("cannot parse URL `%s': %s"), arg, mu_strerror (rc)); + return rc; } - return 0; -} - -int -mu_m_server_parse_url (mu_m_server_t msrv, char *arg, - struct sockaddr *sa, int *psalen) -{ - int rc; - union m_sockaddr s; - int salen; - rc = _mu_m_server_parse_url (arg, &s, &salen, &msrv->defaddr.s.s_sa); + msrv->hints.flags = MU_AH_PASSIVE; + rc = mu_sockaddr_from_url (psa, url, &msrv->hints); if (rc) - return rc; - if (sa) - { - if (*psalen < salen) - return MU_ERR_BUFSPACE; - memcpy (sa, &s.s_sa, salen); - } - *psalen = salen; - return 0; + mu_error (_("cannot create sockaddr for URL `%s': %s"), arg, + mu_strerror (rc)); + mu_url_destroy (&url); + return rc; } static int server_block_begin (const char *arg, mu_m_server_t msrv, void **pdata) { - union m_sockaddr s; - int salen; - if (_mu_m_server_parse_url (arg, &s, &salen, &msrv->defaddr.s.s_sa)) + struct mu_sockaddr *s; + if (mu_m_server_parse_url (msrv, arg, &s)) return 1; - *pdata = add_server (msrv, &s.s_sa, salen, msrv->deftype); + if (s->next) + { + /* FIXME: (1) Find a way to handle all addresses. + (2) Print which address is being used. + */ + mu_diag_output (MU_DIAG_WARNING, + _("%s resolves to several addresses, " + "only the first is used"), arg); + mu_sockaddr_free (s->next); + } + *pdata = add_server (msrv, s, msrv->deftype); return 0; } @@ -1002,21 +831,46 @@ _cb_daemon_mode (void *data, mu_config_value_t *val) return 0; } +unsigned short +get_port (const char *p) +{ + if (p) + { + char *q; + unsigned long n = strtoul (p, &q, 0); + if (*q == 0) + { + if (n > USHRT_MAX) + { + mu_error (_("invalid port number: %s"), p); + return 1; + } + + return htons (n); + } + else + { + struct servent *sp = getservbyname (p, "tcp"); + if (!sp) + return 0; + return sp->s_port; + } + } + return 0; +} + static int _cb_port (void *data, mu_config_value_t *val) { - struct m_default_address *ap = data; + struct mu_sockaddr_hints *hp = data; unsigned short num; - + if (mu_cfg_assert_value_type (val, MU_CFG_STRING)) return 1; num = get_port (val->v.string); if (!num) return 1; - ap->s.s_in.sin_family = AF_INET; - ap->s.s_in.sin_addr.s_addr = htonl (INADDR_ANY); - ap->s.s_in.sin_port = num; - ap->len = sizeof ap->s.s_in; + hp->port = num; return 0; } @@ -1036,7 +890,7 @@ static struct mu_cfg_param dot_server_cfg_param[] = { N_("Store PID of the master process in this file."), N_("file") }, { "port", mu_cfg_callback, - NULL, mu_offsetof (struct _mu_m_server,defaddr), _cb_port, + NULL, mu_offsetof (struct _mu_m_server, hints), _cb_port, N_("Default port number.") }, { "timeout", mu_cfg_time, NULL, mu_offsetof (struct _mu_m_server,timeout), NULL, diff --git a/libmailutils/sockaddr/Makefile.am b/libmailutils/sockaddr/Makefile.am new file mode 100644 index 000000000..dcfb18446 --- /dev/null +++ b/libmailutils/sockaddr/Makefile.am @@ -0,0 +1,31 @@ +# GNU Mailutils -- a suite of utilities for electronic mail +# Copyright (C) 2011 Free Software Foundation, Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 3 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General +# Public License along with this library. If not, see +# <http://www.gnu.org/licenses/>. + +noinst_LTLIBRARIES = libsockaddr.la + +libsockaddr_la_SOURCES = \ + copy.c\ + create.c\ + free.c\ + fromnode.c\ + insert.c\ + ipaddr.c\ + str.c\ + unlink.c\ + url.c + +INCLUDES = @MU_LIB_COMMON_INCLUDES@ -I/libmailutils diff --git a/libmailutils/sockaddr/copy.c b/libmailutils/sockaddr/copy.c new file mode 100644 index 000000000..5e28e7857 --- /dev/null +++ b/libmailutils/sockaddr/copy.c @@ -0,0 +1,36 @@ +/* GNU Mailutils -- a suite of utilities for electronic mail + Copyright (C) 2011 Free Software Foundation, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library. If not, see + <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdlib.h> +#include <mailutils/sockaddr.h> + +int +mu_sockaddr_copy (struct mu_sockaddr **pnew, struct mu_sockaddr *old) +{ + if (!old) + { + *pnew = NULL; + return 0; + } + return mu_sockaddr_create (pnew, old->addr, old->addrlen); +} + + diff --git a/libmailutils/sockaddr/create.c b/libmailutils/sockaddr/create.c new file mode 100644 index 000000000..5b49471e6 --- /dev/null +++ b/libmailutils/sockaddr/create.c @@ -0,0 +1,47 @@ +/* GNU Mailutils -- a suite of utilities for electronic mail + Copyright (C) 2011 Free Software Foundation, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library. If not, see + <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <mailutils/sockaddr.h> + +int +mu_sockaddr_create (struct mu_sockaddr **res, + struct sockaddr *addr, socklen_t len) +{ + struct mu_sockaddr *sa; + + sa = calloc (1, sizeof (*sa)); + if (!sa) + return ENOMEM; + sa->addr = malloc (len); + if (!sa->addr) + { + free (sa); + return ENOMEM; + } + memcpy (sa->addr, addr, len); + sa->addrlen = len; + *res = sa; + return 0; +} + diff --git a/libmailutils/sockaddr/free.c b/libmailutils/sockaddr/free.c new file mode 100644 index 000000000..2a31ec36d --- /dev/null +++ b/libmailutils/sockaddr/free.c @@ -0,0 +1,50 @@ +/* GNU Mailutils -- a suite of utilities for electronic mail + Copyright (C) 2011 Free Software Foundation, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library. If not, see + <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdlib.h> +#include <mailutils/sockaddr.h> + +void +mu_sockaddr_free (struct mu_sockaddr *addr) +{ + if (!addr) + return; + free (addr->addr); + free (addr->str); +} + +void +mu_sockaddr_free_list (struct mu_sockaddr *addr) +{ + if (!addr) + return; + if (addr->prev) + addr->prev->next = NULL; + while (addr) + { + struct mu_sockaddr *next = addr->next; + mu_sockaddr_free (addr); + addr = next; + } +} + + + diff --git a/libmailutils/sockaddr/fromnode.c b/libmailutils/sockaddr/fromnode.c new file mode 100644 index 000000000..d79b9523a --- /dev/null +++ b/libmailutils/sockaddr/fromnode.c @@ -0,0 +1,253 @@ +/* GNU Mailutils -- a suite of utilities for electronic mail + Copyright (C) 2011 Free Software Foundation, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library. If not, see + <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/stat.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <mailutils/sockaddr.h> +#include <mailutils/url.h> +#include <mailutils/io.h> +#include <mailutils/errno.h> +#include <mailutils/error.h> +#include <mailutils/nls.h> + +static struct mu_sockaddr * +match_sa (struct mu_sockaddr *list, struct sockaddr *sa, socklen_t len) +{ + for (; list; list = list->next) + if (len == list->addrlen && memcmp (list->addr, sa, len) == 0) + break; + return list; +} + +int +mu_sockaddr_from_node (struct mu_sockaddr **retval, const char *node, + const char *serv, struct mu_sockaddr_hints *mh) +{ + int rc; + + if (!mh) + { + static struct mu_sockaddr_hints nullhints = { 0, AF_UNSPEC }; + mh = &nullhints; + } + + if (mh->family == AF_UNIX) + { + size_t slen; + struct sockaddr_un s_un; + + if (!node) + return MU_ERR_NONAME; + slen = strlen (node); + if (slen >= sizeof s_un.sun_path) + return MU_ERR_BUFSPACE; + + s_un.sun_family = AF_UNIX; + strcpy(s_un.sun_path, node); + return mu_sockaddr_create (retval, (struct sockaddr*) &s_un, + sizeof (s_un)); + } + else +#ifdef MAILUTILS_IPV6 + { + struct addrinfo hints; + struct addrinfo *res, *ap; + char portbuf[64]; + struct mu_sockaddr *tail = NULL; + + memset (&hints, 0, sizeof (hints)); + hints.ai_family = mh->family; + hints.ai_socktype = mh->socktype; + hints.ai_protocol = mh->protocol; + + if (!node) + { + if (mh->flags & MU_AH_PASSIVE) + hints.ai_flags |= AI_PASSIVE; + else + return MU_ERR_NONAME; + } + if (!serv && mh->port) + { + snprintf (portbuf, sizeof portbuf, "%hu", mh->port); + serv = portbuf; + } + + rc = getaddrinfo (node, serv, &hints, &res); + + switch (rc) + { + case 0: + break; + + case EAI_FAIL: + return MU_ERR_GETHOSTBYNAME; + + case EAI_FAMILY: + return MU_ERR_FAMILY; + + case EAI_NONAME: + return MU_ERR_NONAME; + + case EAI_SERVICE: + return MU_ERR_SERVICE; + + case EAI_SYSTEM: + mu_error (_("%s:%s: cannot parse address: %s"), + node, serv, mu_strerror (errno)); + return errno; + + case EAI_BADFLAGS: + return MU_ERR_BADFLAGS; + + case EAI_SOCKTYPE: + return MU_ERR_SOCKTYPE; + + case EAI_MEMORY: + return ENOMEM; + + default: + mu_error ("%s:%s: %s", node, serv, gai_strerror (rc)); + return MU_ERR_FAILURE; + } + + *retval = NULL; + for (ap = res; ap; ap = ap->ai_next) + if (mh->family == AF_UNSPEC || ap->ai_addr->sa_family == mh->family) + { + struct mu_sockaddr *sa; + + if (match_sa (*retval, ap->ai_addr, ap->ai_addrlen)) + continue; + rc = mu_sockaddr_create (&sa, ap->ai_addr, ap->ai_addrlen); + if (rc) + { + mu_sockaddr_free_list (*retval); + freeaddrinfo (res); + return rc; + } + if (tail) + mu_sockaddr_insert (tail, sa, 0); + else + *retval = sa; + tail = sa; + } + freeaddrinfo (res); + } +#else + if (mh->family == AF_INET) + { + short port; + struct hostent *hp; + struct mu_sockaddr *tail = NULL; + char **p; + + if (serv) + { + char *end; + unsigned long n = strtoul (serv, &end, 10); + + if (*end) + { + struct servent *sp; + const char *proto; + + if (mh->protocol) + { + struct protoent *pp = getprotobynumber (mh->protocol); + if (!pp) + return EINVAL; + proto = pp->p_name; + } + else + proto = NULL; + + sp = getservbyname (serv, proto); + if (!sp) + return MU_ERR_SERVICE; + port = sp->s_port; + } + else if (n == 0 || (port = n) != n) + return MU_ERR_PARSE; /* FIXME: need MU_ERR_RANGE? */ + } + else if (mh->port) + port = htons (mh->port); + else + return MU_ERR_NONAME; + + if (!node) + { + struct sockaddr_in s_in; + + if (!(mh->flags & MU_AH_PASSIVE)) + return MU_ERR_NONAME; + + s_in.sin_family = AF_INET; + s_in.sin_addr.s_addr = INADDR_ANY; + s_in.sin_port = port; + return mu_sockaddr_create (retval, (struct sockaddr*)&s_in, + sizeof (s_in)); + } + + hp = gethostbyname (node); + if (!hp) + return MU_ERR_GETHOSTBYNAME; + + if (hp->h_addrtype != AF_INET || hp->h_length != 4) + return MU_ERR_FAMILY; + + for (p = hp->h_addr_list; *p; p++) + { + struct mu_sockaddr *sa; + struct sockaddr_in s_in; + + s_in.sin_family = AF_INET; + memcpy(&s_in.sin_addr, *p, 4); + s_in.sin_port = port; + + rc = mu_sockaddr_create (&sa, (struct sockaddr*)&s_in, + sizeof (s_in)); + if (rc) + { + mu_sockaddr_free_list (*retval); + return rc; + } + if (tail) + mu_sockaddr_insert (tail, sa, 0); + else + *retval = sa; + tail = sa; + } + } + else + return MU_ERR_FAMILY; +#endif + return 0; +} diff --git a/libmailutils/sockaddr/insert.c b/libmailutils/sockaddr/insert.c new file mode 100644 index 000000000..dbac151c1 --- /dev/null +++ b/libmailutils/sockaddr/insert.c @@ -0,0 +1,64 @@ +/* GNU Mailutils -- a suite of utilities for electronic mail + Copyright (C) 2011 Free Software Foundation, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library. If not, see + <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdlib.h> +#include <mailutils/sockaddr.h> + +static void +set_next (struct mu_sockaddr *sp, struct mu_sockaddr *tgt) +{ + for (; sp->next; sp = sp->next) + ; + sp->next = tgt; + if (tgt) + tgt->prev = sp; +} + +struct mu_sockaddr * +mu_sockaddr_insert (struct mu_sockaddr *anchor, struct mu_sockaddr *addr, + int before) +{ + struct mu_sockaddr *ret = anchor; + + if (!anchor) + { + addr->prev = NULL; + set_next (addr, NULL); + return addr; + } + + if (before) + { + if (anchor->prev) + anchor = anchor->prev; + else + { + addr->prev = NULL; + set_next (addr, anchor); + return addr; + } + } + + set_next (addr, anchor->next); + anchor->next = addr; + addr->prev = anchor; + return ret; +} diff --git a/libmailutils/sockaddr/ipaddr.c b/libmailutils/sockaddr/ipaddr.c new file mode 100644 index 000000000..6b14b4c29 --- /dev/null +++ b/libmailutils/sockaddr/ipaddr.c @@ -0,0 +1,88 @@ +/* GNU Mailutils -- a suite of utilities for electronic mail + Copyright (C) 2011 Free Software Foundation, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library. If not, see + <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <string.h> +#include <mailutils/sockaddr.h> +#include <mailutils/cctype.h> + +int +mu_str_is_ipv4 (const char *addr) +{ + int dot_count = 0; + int digit_count = 0; + + for (; *addr; addr++) + { + if (!mu_isascii (*addr)) + return 0; + if (*addr == '.') + { + if (++dot_count > 3) + break; + digit_count = 0; + } + else if (!(mu_isdigit (*addr) && ++digit_count <= 3)) + return 0; + } + + return (dot_count == 3); +} + +int +mu_str_is_ipv6 (const char *addr) +{ + int col_count = 0; /* Number of colons */ + int dcol = 0; /* Did we encounter a double-colon? */ + int dig_count = 0; /* Number of digits in the last group */ + + for (; *addr; addr++) + { + if (!mu_isascii (*addr)) + return 0; + else if (mu_isxdigit (*addr)) + { + if (++dig_count > 4) + return 0; + } + else if (*addr == ':') + { + if (col_count && dig_count == 0 && ++dcol > 1) + return 0; + if (++col_count > 7) + return 0; + dig_count = 0; + } + else + return 0; + } + + return (col_count == 7 || dcol); +} + +int +mu_str_is_ipaddr (const char *addr) +{ + if (strchr (addr, '.')) + return mu_str_is_ipv4(addr); + else if (strchr (addr, ':')) + return mu_str_is_ipv6(addr); + return 0; +} diff --git a/libmailutils/sockaddr/str.c b/libmailutils/sockaddr/str.c new file mode 100644 index 000000000..949f21ef9 --- /dev/null +++ b/libmailutils/sockaddr/str.c @@ -0,0 +1,109 @@ +/* GNU Mailutils -- a suite of utilities for electronic mail + Copyright (C) 2011 Free Software Foundation, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library. If not, see + <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/stat.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> + +#include <mailutils/sockaddr.h> +#include <mailutils/errno.h> +#include <mailutils/io.h> + +static char *default_sockaddr_text = "[not enogh memory]"; + +#define S_UN_NAME(sa, salen) \ + ((salen < mu_offsetof(struct sockaddr_un,sun_path)) ? \ + "" : (sa)->sun_path) + +int +mu_sockaddr_format (char **pbuf, const struct sockaddr *sa, socklen_t salen) +{ + int rc = MU_ERR_FAILURE; + + switch (sa->sa_family) + { +#ifdef MAILUTILS_IPV6 + case AF_INET: + case AF_INET6: + { + char host[NI_MAXHOST]; + char srv[NI_MAXSERV]; + if (getnameinfo (sa, salen, + host, sizeof (host), srv, sizeof (srv), + NI_NUMERICHOST|NI_NUMERICSERV) == 0) + { + if (sa->sa_family == AF_INET6) + rc = mu_asprintf (pbuf, "inet6://[%s]:%s", host, srv); + else + rc = mu_asprintf (pbuf, "inet://%s:%s", host, srv); + } + else + rc = mu_asprintf (pbuf, "%s://[getnameinfo failed]", + sa->sa_family == AF_INET ? + "inet" : "inet6"); + break; + } +#else + case AF_INET: + { + struct sockaddr_in *s_in = (struct sockaddr_in *)sa; + rc = mu_asprintf (pbuf, "inet://%s:%hu", + inet_ntoa (s_in->sin_addr), s_in->sin_port); + break; + } +#endif + + case AF_UNIX: + { + struct sockaddr_un *s_un = (struct sockaddr_un *)sa; + if (S_UN_NAME (s_un, salen)[0] == 0) + rc = mu_asprintf (pbuf, "unix://[anonymous socket]"); + else + rc = mu_asprintf (pbuf, "unix://%s", s_un->sun_path); + break; + } + + default: + rc = mu_asprintf (pbuf, "family:%d", sa->sa_family); + } + return rc; +} + +char * +mu_sockaddr_to_astr (const struct sockaddr *sa, int salen) +{ + char *buf = NULL; + mu_sockaddr_format (&buf, sa, salen); + return buf; +} + +const char * +mu_sockaddr_str (struct mu_sockaddr *sa) +{ + if (!sa->str && mu_sockaddr_format (&sa->str, sa->addr, sa->addrlen)) + return default_sockaddr_text; + return sa->str; +} + + diff --git a/libmailutils/sockaddr/unlink.c b/libmailutils/sockaddr/unlink.c new file mode 100644 index 000000000..65808f568 --- /dev/null +++ b/libmailutils/sockaddr/unlink.c @@ -0,0 +1,45 @@ +/* GNU Mailutils -- a suite of utilities for electronic mail + Copyright (C) 2011 Free Software Foundation, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library. If not, see + <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdlib.h> +#include <mailutils/sockaddr.h> + +struct mu_sockaddr * +mu_sockaddr_unlink (struct mu_sockaddr *addr) +{ + struct mu_sockaddr *p; + + if (!addr) + return NULL; + + p = addr->prev; + if (p) + p->next = addr->next; + + p = addr->next; + if (p) + p->prev = addr->prev; + + addr->prev = addr->next = NULL; + + return p; +} + diff --git a/libmailutils/sockaddr/url.c b/libmailutils/sockaddr/url.c new file mode 100644 index 000000000..0eaf870be --- /dev/null +++ b/libmailutils/sockaddr/url.c @@ -0,0 +1,130 @@ +/* GNU Mailutils -- a suite of utilities for electronic mail + Copyright (C) 2011 Free Software Foundation, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library. If not, see + <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/stat.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <mailutils/sockaddr.h> +#include <mailutils/url.h> +#include <mailutils/io.h> +#include <mailutils/errno.h> +#include <mailutils/nls.h> +#include <mailutils/kwd.h> + +static struct mu_kwd famtab[] = { + { "unix", AF_UNIX }, + { "local", AF_UNIX }, + { "inet4", AF_INET }, +#ifdef MAILUTILS_IPV6 + { "inet6", AF_INET6 }, + { "inet", AF_UNSPEC }, +#else + { "inet", AF_INET }, +#endif + { NULL } +}; + +int +mu_sockaddr_from_url (struct mu_sockaddr **retval, mu_url_t url, + struct mu_sockaddr_hints *mh) +{ + int rc; + const char *scheme; + const char *node = NULL, *serv = NULL; + struct mu_sockaddr_hints hints; + + if (mh) + memcpy (&hints, mh, sizeof (hints)); + else + { + memset (&hints, sizeof(hints), 0); + hints.family = AF_UNSPEC; + hints.socktype = SOCK_STREAM; + hints.protocol = IPPROTO_TCP; + } + + if (hints.family == AF_UNSPEC) + { + rc = mu_url_sget_scheme (url, &scheme); + if (rc) + return rc; + + if (mu_kwd_xlat_name (famtab, scheme, &hints.family)) + { + if (hints.flags & MU_AH_DETECT_FAMILY) + { + int flags = 0; + + mu_url_get_flags (url, &flags); +#ifdef MAILUTILS_IPV6 + if (flags & MU_URL_IPV6) + hints.family = AF_INET6; + else +#endif + if (flags & (MU_URL_HOST|MU_URL_PORT)) + hints.family = AF_INET; + else if (flags & MU_URL_PATH) + hints.family = AF_UNIX; + else + return MU_ERR_FAMILY; + } + else + return MU_ERR_FAMILY; + } + } + + if (hints.family == AF_UNIX) + { + rc = mu_url_sget_path (url, &node); + if (rc) + { + if (rc == MU_ERR_NOENT) + { + rc = mu_url_sget_host (url, &node); + if (rc == MU_ERR_NOENT) + return MU_ERR_NONAME; + } + if (rc) + return rc; + } + } + else + { +#ifdef MAILUTILS_IPV6 + if (hints.family == AF_UNSPEC) + hints.family = mu_url_has_flag (url, MU_URL_IPV6) ? AF_INET6 : AF_INET; +#endif + rc = mu_url_sget_host (url, &node); + if (rc && rc != MU_ERR_NOENT) + return MU_ERR_NONAME; + rc = mu_url_sget_portstr (url, &serv); + if (rc && rc != MU_ERR_NOENT) + return MU_ERR_NONAME; + } + return mu_sockaddr_from_node (retval, node, serv, &hints); +} diff --git a/libmailutils/stream/tcp.c b/libmailutils/stream/tcp.c index 40b37124d..f383f1b5a 100644 --- a/libmailutils/stream/tcp.c +++ b/libmailutils/stream/tcp.c @@ -39,7 +39,7 @@ #include <mailutils/errno.h> #include <mailutils/stream.h> #include <mailutils/util.h> - +#include <mailutils/sockaddr.h> #include <mailutils/sys/stream.h> #define TCP_STATE_INIT 1 @@ -52,18 +52,11 @@ struct _tcp_instance { struct _mu_stream stream; int fd; - char *host; - unsigned short port; int state; - unsigned long address; - unsigned long source_addr; + struct mu_sockaddr *remote_addr; + struct mu_sockaddr *source_addr; }; -/* On solaris inet_addr() return -1. */ -#ifndef INADDR_NONE -# define INADDR_NONE (unsigned long)-1 -#endif - static int _tcp_close (mu_stream_t stream) { @@ -83,28 +76,12 @@ _tcp_close (mu_stream_t stream) } static int -resolve_hostname (const char *host, unsigned long *ip) -{ - unsigned long address = inet_addr (host); - if (address == INADDR_NONE) - { - struct hostent *phe = gethostbyname (host); - if (!phe) - return MU_ERR_GETHOSTBYNAME; - address = *(((unsigned long **) phe->h_addr_list)[0]); - } - *ip = address; - return 0; -} - -static int _tcp_open (mu_stream_t stream) { struct _tcp_instance *tcp = (struct _tcp_instance *)stream; int flgs, ret; socklen_t namelen; struct sockaddr_in peer_addr; - struct sockaddr_in soc_addr; int flags; mu_stream_get_flags (stream, &flags); @@ -114,7 +91,8 @@ _tcp_open (mu_stream_t stream) case TCP_STATE_INIT: if (tcp->fd == -1) { - if ((tcp->fd = socket (PF_INET, SOCK_STREAM, 0)) == -1) + tcp->fd = socket (tcp->remote_addr->addr->sa_family, SOCK_STREAM, 0); + if (tcp->fd == -1) return errno; } if (flags & MU_STREAM_NONBLOCK) @@ -124,13 +102,10 @@ _tcp_open (mu_stream_t stream) fcntl (tcp->fd, F_SETFL, flgs); mu_stream_set_flags (stream, MU_STREAM_NONBLOCK); } - if (tcp->source_addr != INADDR_ANY) + if (tcp->source_addr) { - struct sockaddr_in s; - s.sin_family = AF_INET; - s.sin_addr.s_addr = tcp->source_addr; - s.sin_port = 0; - if (bind (tcp->fd, (struct sockaddr*) &s, sizeof(s)) < 0) + if (bind (tcp->fd, tcp->source_addr->addr, + tcp->source_addr->addrlen) < 0) { int e = errno; close (tcp->fd); @@ -142,27 +117,11 @@ _tcp_open (mu_stream_t stream) tcp->state = TCP_STATE_RESOLVING; case TCP_STATE_RESOLVING: - if (!(tcp->host != NULL && tcp->port > 0)) - { - _tcp_close (stream); - return EINVAL; - } - - if ((ret = resolve_hostname (tcp->host, &tcp->address))) - { - _tcp_close (stream); - return ret; - } tcp->state = TCP_STATE_RESOLVE; case TCP_STATE_RESOLVE: - memset (&soc_addr, 0, sizeof (soc_addr)); - soc_addr.sin_family = AF_INET; - soc_addr.sin_port = htons (tcp->port); - soc_addr.sin_addr.s_addr = tcp->address; - - if ((connect (tcp->fd, - (struct sockaddr *) &soc_addr, sizeof (soc_addr))) == -1) + if (connect (tcp->fd, tcp->remote_addr->addr, + tcp->remote_addr->addrlen) == -1) { ret = errno; if (ret == EINPROGRESS || ret == EAGAIN) @@ -254,10 +213,8 @@ _tcp_done (mu_stream_t stream) { struct _tcp_instance *tcp = (struct _tcp_instance *)stream; - if (tcp->host) - free (tcp->host); - if (tcp->fd != -1) - close (tcp->fd); + mu_sockaddr_free (tcp->remote_addr); + mu_sockaddr_free (tcp->source_addr); } int @@ -315,33 +272,21 @@ _create_tcp_stream (int flags) } int -mu_tcp_stream_create_with_source_ip (mu_stream_t *pstream, - const char *host, unsigned port, - unsigned long source_ip, - int flags) +mu_tcp_stream_create_from_sa (mu_stream_t *pstream, + struct mu_sockaddr *remote_addr, + struct mu_sockaddr *source_addr, int flags) { int rc; mu_stream_t stream; struct _tcp_instance *tcp; - if (host == NULL) - return MU_ERR_TCP_NO_HOST; - - if (port > USHRT_MAX) - return MU_ERR_TCP_NO_PORT; - tcp = _create_tcp_stream (flags | MU_STREAM_RDWR); if (!tcp) return ENOMEM; - tcp->host = strdup (host); - if (!tcp->host) - { - free (tcp); - return ENOMEM; - } - tcp->port = port; - tcp->state = TCP_STATE_INIT; - tcp->source_addr = source_ip; + + tcp->remote_addr = remote_addr; + tcp->source_addr = source_addr; + stream = (mu_stream_t) tcp; rc = mu_stream_open (stream); if (rc == 0 || rc == EAGAIN || rc == EINPROGRESS) @@ -351,24 +296,93 @@ mu_tcp_stream_create_with_source_ip (mu_stream_t *pstream, return rc; } + +int +mu_tcp_stream_create_with_source_ip (mu_stream_t *pstream, + const char *host, unsigned port, + unsigned long source_ip, + int flags) +{ + int rc; + struct mu_sockaddr *remote_addr, *source_addr = NULL; + struct mu_sockaddr_hints hints; + + memset (&hints, 0, sizeof hints); + hints.family = AF_INET; + hints.socktype = SOCK_STREAM; + hints.protocol = IPPROTO_TCP; + hints.port = port; + rc = mu_sockaddr_from_node (&remote_addr, host, NULL, &hints); + if (rc) + return rc; + + if (source_ip) + { + struct sockaddr_in s; + s.sin_family = AF_INET; + s.sin_addr.s_addr = source_ip; + s.sin_port = 0; + rc = mu_sockaddr_create (&source_addr, (struct sockaddr*)&s, + sizeof (s)); + if (rc) + { + mu_sockaddr_free (remote_addr); + return 0; + } + } + + rc = mu_tcp_stream_create_from_sa (pstream, remote_addr, source_addr, flags); + if (rc) + { + mu_sockaddr_free (remote_addr); + mu_sockaddr_free (source_addr); + } + return rc; +} + int mu_tcp_stream_create_with_source_host (mu_stream_t *stream, const char *host, unsigned port, const char *source_host, int flags) { - unsigned long source_addr; - int ret = resolve_hostname (source_host, &source_addr); - if (ret == 0) - ret = mu_tcp_stream_create_with_source_ip (stream, host, port, - source_addr, flags); - return ret; + int rc; + struct mu_sockaddr *remote_addr, *source_addr = NULL; + struct mu_sockaddr_hints hints; + + memset (&hints, 0, sizeof hints); + hints.family = AF_INET; + hints.socktype = SOCK_STREAM; + hints.port = port; + rc = mu_sockaddr_from_node (&remote_addr, host, NULL, &hints); + if (rc) + return rc; + + if (source_host) + { + hints.flags = MU_AH_PASSIVE; + hints.port = 0; + rc = mu_sockaddr_from_node (&source_addr, source_host, NULL, &hints); + if (rc) + { + mu_sockaddr_free (remote_addr); + return 0; + } + } + + rc = mu_tcp_stream_create_from_sa (stream, remote_addr, source_addr, flags); + if (rc) + { + mu_sockaddr_free (remote_addr); + mu_sockaddr_free (source_addr); + } + return rc; } int mu_tcp_stream_create (mu_stream_t *stream, const char *host, unsigned port, int flags) { - return mu_tcp_stream_create_with_source_ip (stream, host, port, - INADDR_ANY, flags); + return mu_tcp_stream_create_with_source_host (stream, host, port, + NULL, flags); } diff --git a/libmailutils/tests/.gitignore b/libmailutils/tests/.gitignore index d205efe63..d01d4ad93 100644 --- a/libmailutils/tests/.gitignore +++ b/libmailutils/tests/.gitignore @@ -1,5 +1,6 @@ atconfig atlocal +cidr package.m4 testsuite testsuite.dir diff --git a/libmailutils/tests/Makefile.am b/libmailutils/tests/Makefile.am index 68618a1df..5c0087398 100644 --- a/libmailutils/tests/Makefile.am +++ b/libmailutils/tests/Makefile.am @@ -41,6 +41,7 @@ $(srcdir)/package.m4: $(top_srcdir)/configure.ac INCLUDES = @MU_LIB_COMMON_INCLUDES@ noinst_PROGRAMS = \ addr\ + cidr\ debugspec\ decode2047\ encode2047\ diff --git a/libmailutils/tests/cidr.c b/libmailutils/tests/cidr.c new file mode 100644 index 000000000..797fb248e --- /dev/null +++ b/libmailutils/tests/cidr.c @@ -0,0 +1,77 @@ +/* GNU Mailutils -- a suite of utilities for electronic mail + Copyright (C) 2011 Free Software Foundation, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library. If not, see + <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +#include <stdlib.h> +#include <stdio.h> +#include <mailutils/mailutils.h> + +static void +print_bytes (unsigned char *b, size_t l) +{ + for (; l; l--, b++) + printf (" %02x", *b); + printf ("\n"); +} + +int +main (int argc, char **argv) +{ + mu_set_program_name (argv[0]); + if (argc < 2) + { + mu_error ("usage: %s CIDR [CIDR...]", argv[0]); + return 1; + } + + while (--argc) + { + char *arg = *++argv; + struct mu_cidr cidr; + int rc; + char *str; + + rc = mu_cidr_from_string (&cidr, arg); + if (rc) + { + mu_error ("%s: %s", arg, mu_strerror (rc)); + continue; + } + + printf ("%s:\n", arg); + printf ("family = %d\n", cidr.family); + printf ("len = %d\n", cidr.len); + printf ("address ="); + print_bytes (cidr.address, cidr.len); + printf ("netmask ="); + print_bytes (cidr.netmask, cidr.len); + rc = mu_cidr_format (&cidr, 0, &str); + if (rc) + { + mu_error ("cannot covert to string: %s", mu_strerror (rc)); + return 2; + } + + printf ("string = %s\n", str); + free (str); + } + return 0; +} + + diff --git a/libmailutils/tests/url-parse.c b/libmailutils/tests/url-parse.c index 86c917315..48dd40f84 100644 --- a/libmailutils/tests/url-parse.c +++ b/libmailutils/tests/url-parse.c @@ -25,6 +25,7 @@ #include <mailutils/errno.h> #include <mailutils/url.h> #include <mailutils/secret.h> +#include <mailutils/kwd.h> #define CAT2(a,b) a ## b @@ -73,12 +74,52 @@ print_query (mu_url_t url) printf ("query[%lu] <%s>\n", (unsigned long) i, qargv[i]); } +struct mu_kwd parse_kwtab[] = { + { "hexcode", MU_URL_PARSE_HEXCODE }, + { "hidepass", MU_URL_PARSE_HIDEPASS }, + { "portsrv", MU_URL_PARSE_PORTSRV }, + { "portwc", MU_URL_PARSE_PORTWC }, + { "pipe", MU_URL_PARSE_PIPE }, + { "slash", MU_URL_PARSE_SLASH }, + { "dslash_optional", MU_URL_PARSE_DSLASH_OPTIONAL }, + { "default", MU_URL_PARSE_DEFAULT }, + { "all", MU_URL_PARSE_ALL }, + { NULL } +}; + + int -main () +main (int argc, char **argv) { char str[1024]; unsigned port = 0; - mu_url_t u = NULL; + mu_url_t u = NULL, uhint = NULL; + int i; + int parse_flags = 0; + int rc; + + mu_set_program_name (argv[0]); + for (i = 1; i < argc; i++) + { + int flag; + + if (strncmp (argv[i], "hint=", 5) == 0) + { + rc = mu_url_create (&uhint, argv[i] + 5); + if (rc) + { + mu_error ("cannot create hints: %s", mu_strerror (rc)); + exit (1); + } + } + else if (mu_kwd_xlat_name_ci (parse_kwtab, argv[i], &flag) == 0) + parse_flags |= flag; + else + { + mu_error ("%s: unknown flag %s", argv[0], argv[i]); + exit (1); + } + } while (fgets (str, sizeof (str), stdin) != NULL) { @@ -89,9 +130,9 @@ main () str[strlen (str) - 1] = '\0'; /* chop newline */ if (strspn (str, " \t") == strlen (str)) continue; /* skip empty lines */ - if ((rc = mu_url_create (&u, str)) != 0) + if ((rc = mu_url_create_hint (&u, str, parse_flags, uhint)) != 0) { - fprintf (stderr, "mu_url_create %s ERROR: [%d] %s", + fprintf (stderr, "mu_url_create %s ERROR: [%d] %s\n", str, rc, mu_strerror (rc)); exit (1); } diff --git a/libmailutils/tests/url.at b/libmailutils/tests/url.at index 8c392ea0e..c0669e2aa 100644 --- a/libmailutils/tests/url.at +++ b/libmailutils/tests/url.at @@ -20,10 +20,11 @@ dnl TESTURL([NAME], [KW = `'], [INPUT], [STDOUT = `'], dnl [STDERR = `'], [RUN-IF-FAIL], [RUN-IF-PASS]) dnl +m4_pushdef([URL_PARSE_OPTIONS]) m4_pushdef([TESTURL],[ m4_pushdef([MU_TEST_GROUP],[Url]) m4_pushdef([MU_TEST_KEYWORDS],[url]) -m4_pushdef([MU_TEST_COMMAND],[url-parse]) +m4_pushdef([MU_TEST_COMMAND],[url-parse URL_PARSE_OPTIONS]) MU_GENERIC_TEST([$1],[$2 url-m4_translit($3,[ ],[_])],[$3],[],[$4],[$5]) m4_popdef([MU_TEST_COMMAND]) m4_popdef([MU_TEST_KEYWORDS]) @@ -32,6 +33,8 @@ m4_popdef([MU_TEST_GROUP]) dnl ------------------------------------------------------------ +m4_define([URL_PARSE_OPTIONS],[default dslash_optional]) + TESTURL([],[], [scheme:], [scheme <scheme> @@ -76,6 +79,10 @@ port 0 path </absolute/path> ]) +dnl ------------------------------------------------------------ + +m4_define([URL_PARSE_OPTIONS],[default]) + TESTURL([],[], [scheme://%75%73%65%72:%70%61%73%73@%68%6f%73%74], [scheme <scheme> @@ -198,7 +205,7 @@ path </a/path> ]) TESTURL([],[], -[ftp:/a/path], +[ftp:///a/path], [scheme <ftp> user <> passwd <> @@ -716,6 +723,7 @@ path <mbox/user@host> param[0] <type=pass> ]]) +m4_pushdef([URL_PARSE_OPTIONS],[default dslash_optional]) TESTURL([],[], [mbox:/var/spool/mail;type=index;param=2;user=gray], [[scheme <mbox> @@ -729,6 +737,7 @@ param[0] <type=index> param[1] <param=2> param[2] <user=gray> ]]) +m4_popdef([URL_PARSE_OPTIONS]) TESTURL([],[], [mbox:///var/spool/mail;type=index;param=2;user=gray], @@ -823,3 +832,4 @@ query[1] <list@dom> ]]) m4_popdef([TESTURL]) +m4_popdef([URL_PARSE_OPTIONS]) diff --git a/libmailutils/url/create.c b/libmailutils/url/create.c index e58064081..130b7a2bc 100644 --- a/libmailutils/url/create.c +++ b/libmailutils/url/create.c @@ -59,9 +59,9 @@ getkn (struct mu_url_ctx *ctx, char *delim) size_t n; if (*ctx->cur == 0) - return -1; + return MU_ERR_PARSE; n = strcspn (ctx->cur, delim); - if (n > ctx->toksize) + if (n + 1 > ctx->toksize) { char *p = realloc (ctx->tokbuf, n + 1); if (!p) @@ -220,11 +220,28 @@ _mu_url_ctx_parse_host (struct mu_url_ctx *ctx, int has_host) int rc; mu_url_t url = ctx->url; - rc = getkn (ctx, ":/;?"); + rc = getkn (ctx, "[:/;?"); if (rc) return rc; - if (ctx->toklen) + if (*ctx->cur == '[') + { + /* Possibly IPv6 address */ + rc = getkn (ctx, "]/;?"); + if (rc) + return rc; + if (*ctx->cur == ']') + { + ctx->cur++; + rc = str_assign (&url->host, ctx->tokbuf + 1); + if (rc) + return rc; + url->flags |= MU_URL_HOST | MU_URL_IPV6; + has_host = 1; + } + } + + if (!(url->flags & MU_URL_HOST) && ctx->toklen) { rc = str_assign (&url->host, ctx->tokbuf); if (rc) @@ -232,22 +249,20 @@ _mu_url_ctx_parse_host (struct mu_url_ctx *ctx, int has_host) url->flags |= MU_URL_HOST; has_host = 1; } - + if (*ctx->cur == ':') { - ctx->cur++; has_host = 1; - - rc = getkn (ctx, "/;?"); + ctx->cur++; + rc = getkn (ctx, ":/;?"); if (rc) return rc; - rc = str_assign (&url->portstr, ctx->tokbuf); if (rc) return rc; url->flags |= MU_URL_PORT; } - + if (*ctx->cur == '/') { if (has_host) @@ -269,7 +284,9 @@ _mu_url_ctx_parse_cred (struct mu_url_ctx *ctx) int rc, has_cred; mu_url_t url = ctx->url; const char *save = ctx->cur; - + + if (*ctx->cur == 0) + return 0; rc = getkn (ctx, "@"); if (rc) return rc; @@ -343,12 +360,18 @@ _mu_url_ctx_parse (struct mu_url_ctx *ctx) { int rc; mu_url_t url = ctx->url; + const char *save = ctx->cur; /* Parse the scheme part */ + if (*ctx->cur == ':') + return _mu_url_ctx_parse_cred (ctx); + rc = getkn (ctx, ":/"); if (rc) return rc; - if (*ctx->cur == ':') + if (*ctx->cur == ':' + && ((ctx->flags & MU_URL_PARSE_DSLASH_OPTIONAL) + || (ctx->cur[1] == '/' && ctx->cur[2] == '/'))) { rc = str_assign (&url->scheme, ctx->tokbuf); if (rc) @@ -356,7 +379,12 @@ _mu_url_ctx_parse (struct mu_url_ctx *ctx) url->flags |= MU_URL_SCHEME; ctx->cur++; } - + else + { + ctx->cur = save; + return _mu_url_ctx_parse_cred (ctx); + } + if (*ctx->cur == 0) return 0; @@ -535,5 +563,6 @@ mu_url_create (mu_url_t *purl, const char *str) MU_URL_PARSE_HIDEPASS | MU_URL_PARSE_PORTSRV | MU_URL_PARSE_PIPE | - MU_URL_PARSE_SLASH, NULL); + MU_URL_PARSE_SLASH | + MU_URL_PARSE_DSLASH_OPTIONAL, NULL); } |