aboutsummaryrefslogtreecommitdiff
path: root/src/lib9/_p9dialparse.c
diff options
context:
space:
mode:
authorDavid du Colombier <0intro@gmail.com>2012-06-02 21:50:59 +0200
committerDavid du Colombier <0intro@gmail.com>2012-06-02 21:50:59 +0200
commit3409bc9ae1614ba47430a3f593fdbe5fc177c878 (patch)
treefbc0743e04e514a8fd5f46f532b4e0b54153e711 /src/lib9/_p9dialparse.c
parent5d03af6557cc215cabc0f83a274d9add703f526e (diff)
downloadplan9port-3409bc9ae1614ba47430a3f593fdbe5fc177c878.tar.gz
plan9port-3409bc9ae1614ba47430a3f593fdbe5fc177c878.tar.bz2
plan9port-3409bc9ae1614ba47430a3f593fdbe5fc177c878.zip
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
Diffstat (limited to 'src/lib9/_p9dialparse.c')
-rw-r--r--src/lib9/_p9dialparse.c105
1 files changed, 53 insertions, 52 deletions
diff --git a/src/lib9/_p9dialparse.c b/src/lib9/_p9dialparse.c
index 0c84ecdb..d8e91003 100644
--- a/src/lib9/_p9dialparse.c
+++ b/src/lib9/_p9dialparse.c
@@ -31,58 +31,35 @@ 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;
@@ -94,13 +71,14 @@ p9dialparse(char *addr, char **pnet, char **punix, u32int *phost, int *pport)
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, u32int *phost, int *pport)
}
/* 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, u32int *phost, int *pport)
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, u32int *phost, int *pport)
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, u32int *phost, int *pport)
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;