]> arthur.barton.de Git - netatalk.git/commitdiff
Fix parsing of address[:port] strings for IPv6 addresses, bug #515
authorRalph Boehme <sloowfranklin@gmail.com>
Tue, 16 Jul 2013 15:02:46 +0000 (17:02 +0200)
committerRalph Boehme <sloowfranklin@gmail.com>
Tue, 23 Jul 2013 13:30:53 +0000 (15:30 +0200)
NEWS
doc/manpages/man5/afp.conf.5.xml
include/atalk/util.h
libatalk/dsi/dsi_tcp.c
libatalk/util/socket.c
man/man5/afp.conf.5.in

diff --git a/NEWS b/NEWS
index 101fc7516f1f3dbeb1cde65b0d574214749e94bc..225c29d05d0f777ae3e66764e64c215673d5742a 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,7 @@ Changes in 3.0.5
 ================
 * FIX: Fix a crash when using pam_winbind. Fixes bug #516.
 * NEW: New global/volume option "ignored attributes"
+* FIX: "afp listen" option failed to take IPv6 addresses. Bug #515.
 
 Changes in 3.0.4
 ================
index 0115b8971080cb6568224b29a3c036d7c148a0b1..e67d651c360c626b4b11f9b51216829232af1010 100644 (file)
             any incoming request. The network address may be specified either
             in dotted-decimal format for IPv4 or in hexadecimal format for
             IPv6.</para>
+            <para>IPv6 address + port combination must use URL the format
+            using square brackets [IPv6]:port</para>
           </listitem>
         </varlistentry>
 
index facc77636a476fdd05c9fffdbe92d0b8c9554c8a..7000499ba68198ed1943e983bcbde0747b06cef2 100644 (file)
@@ -147,6 +147,7 @@ extern const char *getip_string(const struct sockaddr *sa);
 extern unsigned int getip_port(const struct sockaddr *sa);
 extern void apply_ip_mask(struct sockaddr *ai, int maskbits);
 extern int compare_ip(const struct sockaddr *sa1, const struct sockaddr *sa2);
+extern int tokenize_ip_port(const char *ipurl, char **address, char **port);
 
 /* Structures and functions dealing with dynamic pollfd arrays */
 enum fdtype {IPC_FD, LISTEN_FD};
index 731252e6b13314ae11e8bf2e2b57304764c696f3..ad8a3a23715683d0c97b2fdc8e983aba72f27694 100644 (file)
@@ -304,25 +304,45 @@ iflist_done:
 #define AI_NUMERICSERV 0
 #endif
 
-/* this needs to accept passed in addresses */
+/*!
+ * Initialize DSI over TCP
+ *
+ * @param dsi        (rw) DSI handle
+ * @param hostname   (r)  pointer to hostname string
+ * @param inaddress  (r)  Optional IPv4 or IPv6 address with an optional port, may be NULL
+ * @param inport     (r)  pointer to port string
+ *
+ * Creates listening AFP/DSI socket. If the parameter inaddress is NULL, then we listen
+ * on the wildcard address, ie on all interfaces. That should mean listening on the IPv6
+ * address "::" on IPv4/IPv6 dual stack kernels, accepting both v4 and v6 requests.
+ *
+ * If the parameter inaddress is not NULL, then we only listen on the given address.
+ * The parameter may contain a port number using the URL format for address and port:
+ *
+ *   IPv4, IPv4:port, IPv6, [IPv6], [IPv6]:port
+ *
+ * Parameter inport must be a valid pointer to a port string and is used if the inaddress
+ * parameter doesn't contain a port.
+ *
+ * @returns 0 on success, -1 on failure
+ */
 int dsi_tcp_init(DSI *dsi, const char *hostname, const char *inaddress, const char *inport)
 {
     EC_INIT;
     int                flag, err;
-    char               *a = NULL, *b;
-    const char         *address;
-    const char         *port;
+    char              *address = NULL, *port = NULL;
     struct addrinfo    hints, *servinfo, *p;
 
-    /* Check whether address is of the from IP:PORT and split */
-    address = inaddress;
-    port = inport;
-    if (address && strchr(address, ':')) {
-        EC_NULL_LOG( address = a = strdup(address) );
-        b = strchr(a, ':');
-        *b = 0;
-        port = b + 1;
-    }
+    /* inaddress may be NULL */
+    AFP_ASSERT(dsi && hostname && inport);
+
+    if (inaddress)
+        /* Check whether address is of the from IP:PORT and split */
+        EC_ZERO( tokenize_ip_port(inaddress, &address, &port) );
+
+    if (port == NULL)
+        /* inport is supposed to always contain a valid port string */
+        EC_NULL( port = strdup(inport) );
 
     /* Prepare hint for getaddrinfo */
     memset(&hints, 0, sizeof hints);
@@ -343,8 +363,8 @@ int dsi_tcp_init(DSI *dsi, const char *hostname, const char *inaddress, const ch
         hints.ai_family = AF_UNSPEC;
 #endif
     }
-    if ((ret = getaddrinfo(address ? address : NULL, port, &hints, &servinfo)) != 0) {
-        LOG(log_error, logtype_dsi, "dsi_tcp_init: getaddrinfo: %s\n", gai_strerror(ret));
+    if ((ret = getaddrinfo(address, port, &hints, &servinfo)) != 0) {
+        LOG(log_error, logtype_dsi, "dsi_tcp_init(%s): getaddrinfo: %s\n", address ? address : "*", gai_strerror(ret));
         EC_FAIL;
     }
 
@@ -447,8 +467,10 @@ interfaces:
     guess_interface(dsi, hostname, port ? port : "548");
 
 EC_CLEANUP:
-    if (a)
-        free(a);
+    if (address)
+        free(address);
+    if (port)
+        free(port);
     EC_EXIT;
 }
 
index acea8ad39218dfa03fa6ec828abf4030c25433db..c7ef534a7e556567bda75e22eb3048a4616816ae 100644 (file)
@@ -39,6 +39,7 @@
 
 #include <atalk/logger.h>
 #include <atalk/util.h>
+#include <atalk/errchk.h>
 
 static char ipv4mapprefix[] = {0,0,0,0,0,0,0,0,0,0,0xff,0xff};
 
@@ -417,6 +418,91 @@ int compare_ip(const struct sockaddr *sa1, const struct sockaddr *sa2)
     return ret;
 }
 
+/*!
+ * Tokenize IP(4/6) addresses with an optional port into address and port
+ *
+ * @param ipurl    (r) IP URL string
+ * @param address  (w) IP address
+ * @param port     (w) IP port
+ *
+ * @returns 0 on success, -1 on failure
+ *
+ * Tokenize IPv4, IPv4:port, IPv6, [IPv6] or [IPv6:port] URL into address and
+ * port and return two allocated strings with the address and the port.
+ *
+ * If the function returns 0, then address point to a newly allocated
+ * valid address string, port may either be NULL or point to a newly
+ * allocated port number.
+ *
+ * If the function returns -1, then the contents of address and port are
+ * undefined.
+ */
+int tokenize_ip_port(const char *ipurl, char **address, char **port)
+{
+    EC_INIT;
+    char *p = NULL;
+    char *s;
+
+    AFP_ASSERT(ipurl && address && port);
+    EC_NULL( p = strdup(ipurl));
+
+    /* Either ipv4, ipv4:port, ipv6, [ipv6] or [ipv6]:port */
+
+    if (!strchr(p, ':')) {
+        /* IPv4 address without port */
+        *address = p;
+        p = NULL;  /* prevent free() */
+        *port = NULL;
+        EC_EXIT_STATUS(0);
+    }
+
+    /* Either ipv4:port, ipv6, [ipv6] or [ipv6]:port */
+
+    if (strchr(p, '.')) {
+        /* ipv4:port */
+        *address = p;
+        p = strchr(p, ':');
+        *p = '\0';
+        EC_NULL( *port = strdup(p + 1));
+        p = NULL; /* prevent free() */
+        EC_EXIT_STATUS(0);
+    }
+
+    /* Either ipv6, [ipv6] or [ipv6]:port */
+
+    if (p[0] != '[') {
+        /* ipv6 */
+        *address = p;
+        p = NULL;  /* prevent free() */
+        *port = NULL;
+        EC_EXIT_STATUS(0);
+    }
+
+    /* [ipv6] or [ipv6]:port */
+
+    EC_NULL( *address = strdup(p + 1) );
+
+    if ((s = strchr(*address, ']')) == NULL) {
+        LOG(log_error, logtype_dsi, "tokenize_ip_port: malformed ipv6 address %s\n", ipurl);
+        EC_FAIL;
+    }
+    *s = '\0';
+    /* address now points to the ipv6 address without [] */
+
+    if (s[1] == ':') {
+        /* [ipv6]:port */
+        EC_NULL( *port = strdup(s + 2) );
+    } else {
+        /* [ipv6] */
+        *port = NULL;
+    }
+
+EC_CLEANUP:
+    if (p)
+        free(p);
+    EC_EXIT;
+}
+
 /*!
  * Add a fd to a dynamic pollfd array that is allocated and grown as needed
  *
index d9a2304cdc378fa4daa8c6338c794279b7293786..017a6fa4939abbd8800718bac051d97bfcf9d89a 100644 (file)
@@ -424,6 +424,8 @@ afp listen = \fIip address[:port] [ip address[:port] \&.\&.\&.]\fR \fB(G)\fR
 Specifies the IP address that the server should advertise
 \fBand\fR
 listens to\&. The default is advertise the first IP address of the system, but to listen for any incoming request\&. The network address may be specified either in dotted\-decimal format for IPv4 or in hexadecimal format for IPv6\&.
+.sp
+IPv6 address + port combination must use URL the format using square brackets [IPv6]:port
 .RE
 .PP
 afp port = \fIport number\fR \fB(G)\fR