commit 3409bc9ae1614ba47430a3f593fdbe5fc177c878 from: David du Colombier <0intro@gmail.com> date: Sat Jun 02 19:50:59 2012 UTC lib9/dial: add support for IPv6 The function p9dialparse() returns the host as a sockaddr_storage structure instead of a u32int, to be able to handle both IPv4 and IPv6 addresses. Because the sockaddr_storage structure also handle port numbers and Unix path names, there is no longer need to set them in the calling functions. However, these values are still returned for convenience. The sockaddr_in and sockaddr_un structures have been replaced by sockaddr_storage to handle Unix, IPv4 and IPv6 sockets. Names and addresses are resolved using either gethostbyname() or getaddrinfo() functions. The getaddrinfo() function is documented in RFC2553 and standardized since POSIX.1-2001. It supports both IPv4 and IPv6 addresses. The gethostbyname() function is deprecated since POSIX.1-2008. However, some libc implementations don't handle getaddrinfo() properly, thus we preferred to try gethostbyname() first. I've tried to preserve most of the old code logic to prevent from surprising or unwanted behavior. R=rsc http://codereview.appspot.com/6255068 commit - 5d03af6557cc215cabc0f83a274d9add703f526e commit + 3409bc9ae1614ba47430a3f593fdbe5fc177c878 blob - d16b40436389d9e7533621976ebd95265af1aedb blob + 45b22b691cded6bfcfdf2e11fd3883bd049d5c3b --- include/libc.h +++ include/libc.h @@ -542,7 +542,7 @@ extern void privfree(void**); extern int p9accept(int, char*); extern int p9announce(char*, char*); extern int p9dial(char*, char*, char*, int*); -extern int p9dialparse(char *ds, char **net, char **unixa, u32int *ip, int *port); +extern int p9dialparse(char *ds, char **net, char **unixa, void *ip, int *port); extern void p9setnetmtpt(char*, int, char*); extern int p9listen(char*, char*); extern char* p9netmkaddr(char*, char*, char*); blob - bdb283fb1cad7c87c89fdc6b9479cea98d953ddc blob + dda0949f41a8449a16b480a85b77c82124e30e69 --- src/cmd/vbackup/vmount.c +++ src/cmd/vbackup/vmount.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include "mountnfs.h" @@ -20,7 +21,7 @@ void main(int argc, char **argv) { char *p, *net, *unx; - u32int host; + char host[INET_ADDRSTRLEN]; int n, port, proto, verbose; struct sockaddr_in sa; @@ -50,12 +51,17 @@ main(int argc, char **argv) usage(); p = p9netmkaddr(argv[0], "udp", "nfs"); - if(p9dialparse(strdup(p), &net, &unx, &host, &port) < 0) + if(p9dialparse(strdup(p), &net, &unx, &sa, &port) < 0) sysfatal("bad address '%s'", p); + if(sa.sin_family != AF_INET) + sysfatal("only IPv4 is supported"); + + inet_ntop(AF_INET, &(sa.sin_addr), host, INET_ADDRSTRLEN); + if(verbose) - print("nfs server is net=%s addr=%d.%d.%d.%d port=%d\n", - net, host&0xFF, (host>>8)&0xFF, (host>>16)&0xFF, host>>24, port); + print("nfs server is net=%s addr=%s port=%d\n", + net, host, port); proto = 0; if(strcmp(net, "tcp") == 0) @@ -65,11 +71,6 @@ main(int argc, char **argv) else sysfatal("bad proto %s: can only handle tcp and udp", net); - memset(&sa, 0, sizeof sa); - memmove(&sa.sin_addr, &host, 4); - sa.sin_family = AF_INET; - sa.sin_port = htons(port); - mountnfs(proto, &sa, handle, handlelen, argv[1]); exits(0); } blob - 93340c116d348a38d893249221526c9888289ed9 blob + 064e057b60784c94472ad22e5f60a3e3556bcdfd --- src/cmd/vbackup/vmount0.c +++ src/cmd/vbackup/vmount0.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include "mountnfs.h" @@ -22,7 +23,7 @@ void main(int argc, char **argv) { char *p, *net, *unx; - u32int host; + char host[INET_ADDRSTRLEN]; int n, port, proto, verbose; struct sockaddr_in sa; @@ -52,12 +53,17 @@ main(int argc, char **argv) usage(); p = p9netmkaddr(argv[0], "udp", "nfs"); - if(p9dialparse(strdup(p), &net, &unx, &host, &port) < 0) + if(p9dialparse(strdup(p), &net, &unx, &sa, &port) < 0) sysfatal("bad address '%s'", p); + if(sa.sin_family != AF_INET) + sysfatal("only IPv4 is supported"); + + inet_ntop(AF_INET, &(sa.sin_addr), host, INET_ADDRSTRLEN); + if(verbose) - print("nfs server is net=%s addr=%d.%d.%d.%d port=%d\n", - net, host&0xFF, (host>>8)&0xFF, (host>>16)&0xFF, host>>24, port); + print("nfs server is net=%s addr=%s port=%d\n", + net, host, port); proto = 0; if(strcmp(net, "tcp") == 0) @@ -67,11 +73,6 @@ main(int argc, char **argv) else sysfatal("bad proto %s: can only handle tcp and udp", net); - memset(&sa, 0, sizeof sa); - memmove(&sa.sin_addr, &host, 4); - sa.sin_family = AF_INET; - sa.sin_port = htons(port); - mountnfs(proto, &sa, handle, handlelen, argv[1]); exits(0); } blob - 0c84ecdb287689aeebbbda85581cb7547b721b7c blob + d8e910030bbc1330a5a35cfe9fcc38de6446ba45 --- src/lib9/_p9dialparse.c +++ src/lib9/_p9dialparse.c @@ -31,59 +31,36 @@ static struct { }; static int -parseip(char *host, u32int *pip) +setport(struct sockaddr_storage *ss, int port) { - uchar addr[4]; - int x, i; - char *p; - - p = host; - for(i=0; i<4 && *p; i++){ - x = strtoul(p, &p, 0); - if(x < 0 || x >= 256) - return -1; - if(*p != '.' && *p != 0) - return -1; - if(*p == '.') - p++; - addr[i] = x; - } - - switch(CLASS(addr)){ - case 0: - case 1: - if(i == 3){ - addr[3] = addr[2]; - addr[2] = addr[1]; - addr[1] = 0; - }else if(i == 2){ - addr[3] = addr[1]; - addr[2] = 0; - addr[1] = 0; - }else if(i != 4) - return -1; + switch(ss->ss_family){ + case AF_INET: + ((struct sockaddr_in*)ss)->sin_port = htons(port); break; - case 2: - if(i == 3){ - addr[3] = addr[2]; - addr[2] = 0; - }else if(i != 4) - return -1; + case AF_INET6: + ((struct sockaddr_in6*)ss)->sin6_port = htons(port); break; + default: + errstr("unknown protocol family %d", ss->ss_family); + return -1; } - memmove(pip, addr, 4); return 0; } int -p9dialparse(char *addr, char **pnet, char **punix, u32int *phost, int *pport) +p9dialparse(char *addr, char **pnet, char **punix, void *phost, int *pport) { char *net, *host, *port, *e; int i; struct servent *se; struct hostent *he; - struct sockaddr_un *sockun; + struct sockaddr_storage *ss; + struct addrinfo *result; + ss = phost; + + memset(ss, 0, sizeof(ss)); + *punix = nil; net = addr; if((host = strchr(net, '!')) == nil){ @@ -94,13 +71,14 @@ p9dialparse(char *addr, char **pnet, char **punix, u32 if((port = strchr(host, '!')) == nil){ if(strcmp(net, "unix")==0 || strcmp(net, "net")==0){ Unix: - if(strlen(host)+1 > sizeof sockun->sun_path){ + if(strlen(host)+1 > sizeof ((struct sockaddr_un*)&ss)->sun_path){ werrstr("unix socket name too long"); return -1; } *punix = host; *pnet = "unix"; - *phost = 0; + ss->ss_family = AF_UNIX; + strcpy(((struct sockaddr_un*)ss)->sun_path, host); *pport = 0; return 0; } @@ -127,13 +105,36 @@ p9dialparse(char *addr, char **pnet, char **punix, u32 } /* translate host */ - if(strcmp(host, "*") == 0) - *phost = 0; - else if(parseip(host, phost) == 0) - {} - else if((he = gethostbyname(host)) != nil) - *phost = *(u32int*)(he->h_addr); - else{ + if(strcmp(host, "*") == 0){ + ss->ss_family = AF_INET6; + ((struct sockaddr_in6*)ss)->sin6_addr = in6addr_any; + }else if((he = gethostbyname(host)) != nil){ + ss->ss_family = he->h_addrtype; + switch(ss->ss_family){ + case AF_INET: + ((struct sockaddr_in*)ss)->sin_addr = *(struct in_addr*) *(he->h_addr_list); + break; + case AF_INET6: + ((struct sockaddr_in6*)ss)->sin6_addr = *(struct in6_addr*) *(he->h_addr_list); + break; + default: + errstr("unknown protocol family %d", ss->ss_family); + return -1; + } + }else if(getaddrinfo(host, NULL, NULL, &result) == 0) { + ss->ss_family = result->ai_family; + switch (ss->ss_family) { + case AF_INET: + memcpy((struct sockaddr_in*)ss, result->ai_addr, result->ai_addrlen); + break; + case AF_INET6: + memcpy((struct sockaddr_in6*)ss, result->ai_addr, result->ai_addrlen); + break; + default: + errstr("unknown protocol family %d", ss->ss_family); + return -1; + } + }else{ werrstr("unknown host %s", host); return -1; } @@ -144,7 +145,7 @@ p9dialparse(char *addr, char **pnet, char **punix, u32 if((se = getservbyname(port, nets[i])) != nil){ *pnet = nets[i]; *pport = ntohs(se->s_port); - return 0; + return setport(ss, *pport); } } } @@ -154,7 +155,7 @@ p9dialparse(char *addr, char **pnet, char **punix, u32 if(strcmp(porttbl[i].service, port) == 0){ *pnet = porttbl[i].net; *pport = porttbl[i].port; - return 0; + return setport(ss, *pport); } } @@ -172,12 +173,12 @@ p9dialparse(char *addr, char **pnet, char **punix, u32 i = strtol(port, &e, 0); if(*e == 0){ *pport = i; - return 0; + return setport(ss, *pport); } if((se = getservbyname(port, net)) != nil){ *pport = ntohs(se->s_port); - return 0; + return setport(ss, *pport); } werrstr("unknown service %s!*!%s", net, port); return -1; blob - ecdd897a4a951c62d9e17f976590c5fd7e2557c0 blob + 6e5357c5dbea5ed36e413d360320f6e07fd5d1e2 --- src/lib9/announce.c +++ src/lib9/announce.c @@ -39,18 +39,16 @@ p9announce(char *addr, char *dir) int proto; char *buf, *unix; char *net; - u32int host; int port, s; int n; socklen_t sn; - struct sockaddr_in sa; - struct sockaddr_un sun; + struct sockaddr_storage ss; buf = strdup(addr); if(buf == nil) return -1; - if(p9dialparse(buf, &net, &unix, &host, &port) < 0){ + if(p9dialparse(buf, &net, &unix, &ss, &port) < 0){ free(buf); return -1; } @@ -67,11 +65,7 @@ p9announce(char *addr, char *dir) } free(buf); - memset(&sa, 0, sizeof sa); - memmove(&sa.sin_addr, &host, 4); - sa.sin_family = AF_INET; - sa.sin_port = htons(port); - if((s = socket(AF_INET, proto, 0)) < 0) + if((s = socket(ss.ss_family, proto, 0)) < 0) return -1; sn = sizeof n; if(port && getsockopt(s, SOL_SOCKET, SO_TYPE, (void*)&n, &sn) >= 0 @@ -79,7 +73,7 @@ p9announce(char *addr, char *dir) n = 1; setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&n, sizeof n); } - if(bind(s, (struct sockaddr*)&sa, sizeof sa) < 0){ + if(bind(s, (struct sockaddr*)&ss, sizeof ss) < 0){ close(s); return -1; } @@ -90,22 +84,18 @@ p9announce(char *addr, char *dir) return s; Unix: - memset(&sun, 0, sizeof sun); - sun.sun_family = AF_UNIX; - strcpy(sun.sun_path, unix); - if((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) + if((s = socket(ss.ss_family, SOCK_STREAM, 0)) < 0) return -1; - sn = sizeof sun; - if(bind(s, (struct sockaddr*)&sun, sizeof sun) < 0){ + if(bind(s, (struct sockaddr*)&ss, sizeof (struct sockaddr_un)) < 0){ if(errno == EADDRINUSE - && connect(s, (struct sockaddr*)&sun, sizeof sun) < 0 + && connect(s, (struct sockaddr*)&ss, sizeof (struct sockaddr_un)) < 0 && errno == ECONNREFUSED){ /* dead socket, so remove it */ remove(unix); close(s); - if((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) + if((s = socket(ss.ss_family, SOCK_STREAM, 0)) < 0) return -1; - if(bind(s, (struct sockaddr*)&sun, sizeof sun) >= 0) + if(bind(s, (struct sockaddr*)&ss, sizeof (struct sockaddr_un)) >= 0) goto Success; } close(s); blob - 7c7f74808cf137e6dd69f564fcd1572ab1f98aa0 blob + 730b60e3500e2ef3bab341adeacb444126974304 --- src/lib9/dial.c +++ src/lib9/dial.c @@ -19,18 +19,29 @@ #undef unix #define unix xunix +static int +isany(struct sockaddr_storage *ss) +{ + switch(ss->ss_family){ + case AF_INET: + return (((struct sockaddr_in*)ss)->sin_addr.s_addr == INADDR_ANY); + case AF_INET6: + return (memcmp(((struct sockaddr_in6*)ss)->sin6_addr.s6_addr, + in6addr_any.s6_addr, sizeof (struct in6_addr)) == 0); + } + return 0; +} + int p9dial(char *addr, char *local, char *dummy2, int *dummy3) { char *buf; char *net, *unix; - u32int host; int port; int proto; socklen_t sn; int n; - struct sockaddr_in sa, sal; - struct sockaddr_un su; + struct sockaddr_storage ss, ssl; int s; if(dummy2 || dummy3){ @@ -42,11 +53,11 @@ p9dial(char *addr, char *local, char *dummy2, int *dum if(buf == nil) return -1; - if(p9dialparse(buf, &net, &unix, &host, &port) < 0){ + if(p9dialparse(buf, &net, &unix, &ss, &port) < 0){ free(buf); return -1; } - if(strcmp(net, "unix") != 0 && host == 0){ + if(strcmp(net, "unix") != 0 && isany(&ss)){ werrstr("invalid dial address 0.0.0.0 (aka *)"); free(buf); return -1; @@ -65,7 +76,7 @@ p9dial(char *addr, char *local, char *dummy2, int *dum } free(buf); - if((s = socket(AF_INET, proto, 0)) < 0) + if((s = socket(ss.ss_family, proto, 0)) < 0) return -1; if(local){ @@ -74,7 +85,7 @@ p9dial(char *addr, char *local, char *dummy2, int *dum close(s); return -1; } - if(p9dialparse(buf, &net, &unix, &host, &port) < 0){ + if(p9dialparse(buf, &net, &unix, &ss, &port) < 0){ badlocal: free(buf); close(s); @@ -84,29 +95,21 @@ p9dial(char *addr, char *local, char *dummy2, int *dum werrstr("bad local address %s for dial %s", local, addr); goto badlocal; } - memset(&sal, 0, sizeof sal); - memmove(&sal.sin_addr, &host, 4); - sal.sin_family = AF_INET; - sal.sin_port = htons(port); sn = sizeof n; if(port && getsockopt(s, SOL_SOCKET, SO_TYPE, (void*)&n, &sn) >= 0 && n == SOCK_STREAM){ n = 1; setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&n, sizeof n); } - if(bind(s, (struct sockaddr*)&sal, sizeof sal) < 0) + if(bind(s, (struct sockaddr*)&ssl, sizeof ssl) < 0) goto badlocal; free(buf); } n = 1; setsockopt(s, SOL_SOCKET, SO_BROADCAST, &n, sizeof n); - if(host != 0){ - memset(&sa, 0, sizeof sa); - memmove(&sa.sin_addr, &host, 4); - sa.sin_family = AF_INET; - sa.sin_port = htons(port); - if(connect(s, (struct sockaddr*)&sa, sizeof sa) < 0){ + if(!isany(&ss)){ + if(connect(s, (struct sockaddr*)&ss, sizeof ss) < 0){ close(s); return -1; } @@ -126,21 +129,13 @@ Unix: /* Allow regular files in addition to Unix sockets. */ if((s = open(unix, ORDWR)) >= 0) return s; - memset(&su, 0, sizeof su); - su.sun_family = AF_UNIX; - if(strlen(unix)+1 > sizeof su.sun_path){ - werrstr("unix socket name too long"); - free(buf); - return -1; - } - strcpy(su.sun_path, unix); free(buf); - if((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0){ + if((s = socket(ss.ss_family, SOCK_STREAM, 0)) < 0){ werrstr("socket: %r"); return -1; } - if(connect(s, (struct sockaddr*)&su, sizeof su) < 0){ - werrstr("connect %s: %r", su.sun_path); + if(connect(s, (struct sockaddr*)&ss, sizeof (struct sockaddr_un)) < 0){ + werrstr("connect %s: %r", ((struct sockaddr_un*)&ss)->sun_path); close(s); return -1; }