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
This commit is contained in:
David du Colombier 2012-06-02 21:50:59 +02:00
parent 5d03af6557
commit 3409bc9ae1
6 changed files with 107 additions and 119 deletions

View file

@ -542,7 +542,7 @@ extern void privfree(void**);
extern int p9accept(int, char*); extern int p9accept(int, char*);
extern int p9announce(char*, char*); extern int p9announce(char*, char*);
extern int p9dial(char*, char*, char*, int*); 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 void p9setnetmtpt(char*, int, char*);
extern int p9listen(char*, char*); extern int p9listen(char*, char*);
extern char* p9netmkaddr(char*, char*, char*); extern char* p9netmkaddr(char*, char*, char*);

View file

@ -1,6 +1,7 @@
#include <u.h> #include <u.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <arpa/inet.h>
#include <libc.h> #include <libc.h>
#include "mountnfs.h" #include "mountnfs.h"
@ -20,7 +21,7 @@ void
main(int argc, char **argv) main(int argc, char **argv)
{ {
char *p, *net, *unx; char *p, *net, *unx;
u32int host; char host[INET_ADDRSTRLEN];
int n, port, proto, verbose; int n, port, proto, verbose;
struct sockaddr_in sa; struct sockaddr_in sa;
@ -50,12 +51,17 @@ main(int argc, char **argv)
usage(); usage();
p = p9netmkaddr(argv[0], "udp", "nfs"); 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); 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) if(verbose)
print("nfs server is net=%s addr=%d.%d.%d.%d port=%d\n", print("nfs server is net=%s addr=%s port=%d\n",
net, host&0xFF, (host>>8)&0xFF, (host>>16)&0xFF, host>>24, port); net, host, port);
proto = 0; proto = 0;
if(strcmp(net, "tcp") == 0) if(strcmp(net, "tcp") == 0)
@ -65,11 +71,6 @@ main(int argc, char **argv)
else else
sysfatal("bad proto %s: can only handle tcp and udp", net); 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]); mountnfs(proto, &sa, handle, handlelen, argv[1]);
exits(0); exits(0);
} }

View file

@ -1,6 +1,7 @@
#include <u.h> #include <u.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <arpa/inet.h>
#include <libc.h> #include <libc.h>
#include "mountnfs.h" #include "mountnfs.h"
@ -22,7 +23,7 @@ void
main(int argc, char **argv) main(int argc, char **argv)
{ {
char *p, *net, *unx; char *p, *net, *unx;
u32int host; char host[INET_ADDRSTRLEN];
int n, port, proto, verbose; int n, port, proto, verbose;
struct sockaddr_in sa; struct sockaddr_in sa;
@ -52,12 +53,17 @@ main(int argc, char **argv)
usage(); usage();
p = p9netmkaddr(argv[0], "udp", "nfs"); 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); 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) if(verbose)
print("nfs server is net=%s addr=%d.%d.%d.%d port=%d\n", print("nfs server is net=%s addr=%s port=%d\n",
net, host&0xFF, (host>>8)&0xFF, (host>>16)&0xFF, host>>24, port); net, host, port);
proto = 0; proto = 0;
if(strcmp(net, "tcp") == 0) if(strcmp(net, "tcp") == 0)
@ -67,11 +73,6 @@ main(int argc, char **argv)
else else
sysfatal("bad proto %s: can only handle tcp and udp", net); 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]); mountnfs(proto, &sa, handle, handlelen, argv[1]);
exits(0); exits(0);
} }

View file

@ -31,58 +31,35 @@ static struct {
}; };
static int static int
parseip(char *host, u32int *pip) setport(struct sockaddr_storage *ss, int port)
{ {
uchar addr[4]; switch(ss->ss_family){
int x, i; case AF_INET:
char *p; ((struct sockaddr_in*)ss)->sin_port = htons(port);
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;
break; break;
case 2: case AF_INET6:
if(i == 3){ ((struct sockaddr_in6*)ss)->sin6_port = htons(port);
addr[3] = addr[2];
addr[2] = 0;
}else if(i != 4)
return -1;
break; break;
default:
errstr("unknown protocol family %d", ss->ss_family);
return -1;
} }
memmove(pip, addr, 4);
return 0; return 0;
} }
int 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; char *net, *host, *port, *e;
int i; int i;
struct servent *se; struct servent *se;
struct hostent *he; struct hostent *he;
struct sockaddr_un *sockun; struct sockaddr_storage *ss;
struct addrinfo *result;
ss = phost;
memset(ss, 0, sizeof(ss));
*punix = nil; *punix = nil;
net = addr; net = addr;
@ -94,13 +71,14 @@ p9dialparse(char *addr, char **pnet, char **punix, u32int *phost, int *pport)
if((port = strchr(host, '!')) == nil){ if((port = strchr(host, '!')) == nil){
if(strcmp(net, "unix")==0 || strcmp(net, "net")==0){ if(strcmp(net, "unix")==0 || strcmp(net, "net")==0){
Unix: 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"); werrstr("unix socket name too long");
return -1; return -1;
} }
*punix = host; *punix = host;
*pnet = "unix"; *pnet = "unix";
*phost = 0; ss->ss_family = AF_UNIX;
strcpy(((struct sockaddr_un*)ss)->sun_path, host);
*pport = 0; *pport = 0;
return 0; return 0;
} }
@ -127,13 +105,36 @@ p9dialparse(char *addr, char **pnet, char **punix, u32int *phost, int *pport)
} }
/* translate host */ /* translate host */
if(strcmp(host, "*") == 0) if(strcmp(host, "*") == 0){
*phost = 0; ss->ss_family = AF_INET6;
else if(parseip(host, phost) == 0) ((struct sockaddr_in6*)ss)->sin6_addr = in6addr_any;
{} }else if((he = gethostbyname(host)) != nil){
else if((he = gethostbyname(host)) != nil) ss->ss_family = he->h_addrtype;
*phost = *(u32int*)(he->h_addr); switch(ss->ss_family){
else{ 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); werrstr("unknown host %s", host);
return -1; return -1;
} }
@ -144,7 +145,7 @@ p9dialparse(char *addr, char **pnet, char **punix, u32int *phost, int *pport)
if((se = getservbyname(port, nets[i])) != nil){ if((se = getservbyname(port, nets[i])) != nil){
*pnet = nets[i]; *pnet = nets[i];
*pport = ntohs(se->s_port); *pport = ntohs(se->s_port);
return 0; return setport(ss, *pport);
} }
} }
} }
@ -154,7 +155,7 @@ p9dialparse(char *addr, char **pnet, char **punix, u32int *phost, int *pport)
if(strcmp(porttbl[i].service, port) == 0){ if(strcmp(porttbl[i].service, port) == 0){
*pnet = porttbl[i].net; *pnet = porttbl[i].net;
*pport = porttbl[i].port; *pport = porttbl[i].port;
return 0; return setport(ss, *pport);
} }
} }
@ -172,12 +173,12 @@ p9dialparse(char *addr, char **pnet, char **punix, u32int *phost, int *pport)
i = strtol(port, &e, 0); i = strtol(port, &e, 0);
if(*e == 0){ if(*e == 0){
*pport = i; *pport = i;
return 0; return setport(ss, *pport);
} }
if((se = getservbyname(port, net)) != nil){ if((se = getservbyname(port, net)) != nil){
*pport = ntohs(se->s_port); *pport = ntohs(se->s_port);
return 0; return setport(ss, *pport);
} }
werrstr("unknown service %s!*!%s", net, port); werrstr("unknown service %s!*!%s", net, port);
return -1; return -1;

View file

@ -39,18 +39,16 @@ p9announce(char *addr, char *dir)
int proto; int proto;
char *buf, *unix; char *buf, *unix;
char *net; char *net;
u32int host;
int port, s; int port, s;
int n; int n;
socklen_t sn; socklen_t sn;
struct sockaddr_in sa; struct sockaddr_storage ss;
struct sockaddr_un sun;
buf = strdup(addr); buf = strdup(addr);
if(buf == nil) if(buf == nil)
return -1; return -1;
if(p9dialparse(buf, &net, &unix, &host, &port) < 0){ if(p9dialparse(buf, &net, &unix, &ss, &port) < 0){
free(buf); free(buf);
return -1; return -1;
} }
@ -67,11 +65,7 @@ p9announce(char *addr, char *dir)
} }
free(buf); free(buf);
memset(&sa, 0, sizeof sa); if((s = socket(ss.ss_family, proto, 0)) < 0)
memmove(&sa.sin_addr, &host, 4);
sa.sin_family = AF_INET;
sa.sin_port = htons(port);
if((s = socket(AF_INET, proto, 0)) < 0)
return -1; return -1;
sn = sizeof n; sn = sizeof n;
if(port && getsockopt(s, SOL_SOCKET, SO_TYPE, (void*)&n, &sn) >= 0 if(port && getsockopt(s, SOL_SOCKET, SO_TYPE, (void*)&n, &sn) >= 0
@ -79,7 +73,7 @@ p9announce(char *addr, char *dir)
n = 1; n = 1;
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&n, sizeof n); 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); close(s);
return -1; return -1;
} }
@ -90,22 +84,18 @@ p9announce(char *addr, char *dir)
return s; return s;
Unix: Unix:
memset(&sun, 0, sizeof sun); if((s = socket(ss.ss_family, SOCK_STREAM, 0)) < 0)
sun.sun_family = AF_UNIX;
strcpy(sun.sun_path, unix);
if((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
return -1; return -1;
sn = sizeof sun; if(bind(s, (struct sockaddr*)&ss, sizeof (struct sockaddr_un)) < 0){
if(bind(s, (struct sockaddr*)&sun, sizeof sun) < 0){
if(errno == EADDRINUSE if(errno == EADDRINUSE
&& connect(s, (struct sockaddr*)&sun, sizeof sun) < 0 && connect(s, (struct sockaddr*)&ss, sizeof (struct sockaddr_un)) < 0
&& errno == ECONNREFUSED){ && errno == ECONNREFUSED){
/* dead socket, so remove it */ /* dead socket, so remove it */
remove(unix); remove(unix);
close(s); close(s);
if((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) if((s = socket(ss.ss_family, SOCK_STREAM, 0)) < 0)
return -1; return -1;
if(bind(s, (struct sockaddr*)&sun, sizeof sun) >= 0) if(bind(s, (struct sockaddr*)&ss, sizeof (struct sockaddr_un)) >= 0)
goto Success; goto Success;
} }
close(s); close(s);

View file

@ -19,18 +19,29 @@
#undef unix #undef unix
#define unix xunix #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 int
p9dial(char *addr, char *local, char *dummy2, int *dummy3) p9dial(char *addr, char *local, char *dummy2, int *dummy3)
{ {
char *buf; char *buf;
char *net, *unix; char *net, *unix;
u32int host;
int port; int port;
int proto; int proto;
socklen_t sn; socklen_t sn;
int n; int n;
struct sockaddr_in sa, sal; struct sockaddr_storage ss, ssl;
struct sockaddr_un su;
int s; int s;
if(dummy2 || dummy3){ if(dummy2 || dummy3){
@ -42,11 +53,11 @@ p9dial(char *addr, char *local, char *dummy2, int *dummy3)
if(buf == nil) if(buf == nil)
return -1; return -1;
if(p9dialparse(buf, &net, &unix, &host, &port) < 0){ if(p9dialparse(buf, &net, &unix, &ss, &port) < 0){
free(buf); free(buf);
return -1; 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 *)"); werrstr("invalid dial address 0.0.0.0 (aka *)");
free(buf); free(buf);
return -1; return -1;
@ -65,7 +76,7 @@ p9dial(char *addr, char *local, char *dummy2, int *dummy3)
} }
free(buf); free(buf);
if((s = socket(AF_INET, proto, 0)) < 0) if((s = socket(ss.ss_family, proto, 0)) < 0)
return -1; return -1;
if(local){ if(local){
@ -74,7 +85,7 @@ p9dial(char *addr, char *local, char *dummy2, int *dummy3)
close(s); close(s);
return -1; return -1;
} }
if(p9dialparse(buf, &net, &unix, &host, &port) < 0){ if(p9dialparse(buf, &net, &unix, &ss, &port) < 0){
badlocal: badlocal:
free(buf); free(buf);
close(s); close(s);
@ -84,29 +95,21 @@ p9dial(char *addr, char *local, char *dummy2, int *dummy3)
werrstr("bad local address %s for dial %s", local, addr); werrstr("bad local address %s for dial %s", local, addr);
goto badlocal; 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; sn = sizeof n;
if(port && getsockopt(s, SOL_SOCKET, SO_TYPE, (void*)&n, &sn) >= 0 if(port && getsockopt(s, SOL_SOCKET, SO_TYPE, (void*)&n, &sn) >= 0
&& n == SOCK_STREAM){ && n == SOCK_STREAM){
n = 1; n = 1;
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&n, sizeof n); 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; goto badlocal;
free(buf); free(buf);
} }
n = 1; n = 1;
setsockopt(s, SOL_SOCKET, SO_BROADCAST, &n, sizeof n); setsockopt(s, SOL_SOCKET, SO_BROADCAST, &n, sizeof n);
if(host != 0){ if(!isany(&ss)){
memset(&sa, 0, sizeof sa); if(connect(s, (struct sockaddr*)&ss, sizeof ss) < 0){
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){
close(s); close(s);
return -1; return -1;
} }
@ -126,21 +129,13 @@ Unix:
/* Allow regular files in addition to Unix sockets. */ /* Allow regular files in addition to Unix sockets. */
if((s = open(unix, ORDWR)) >= 0) if((s = open(unix, ORDWR)) >= 0)
return s; 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); free(buf);
if((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0){ if((s = socket(ss.ss_family, SOCK_STREAM, 0)) < 0){
werrstr("socket: %r"); werrstr("socket: %r");
return -1; return -1;
} }
if(connect(s, (struct sockaddr*)&su, sizeof su) < 0){ if(connect(s, (struct sockaddr*)&ss, sizeof (struct sockaddr_un)) < 0){
werrstr("connect %s: %r", su.sun_path); werrstr("connect %s: %r", ((struct sockaddr_un*)&ss)->sun_path);
close(s); close(s);
return -1; return -1;
} }