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:
parent
5d03af6557
commit
3409bc9ae1
6 changed files with 107 additions and 119 deletions
|
|
@ -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 *dummy3)
|
|||
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 *dummy3)
|
|||
}
|
||||
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 *dummy3)
|
|||
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 *dummy3)
|
|||
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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue