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
142 lines
2.5 KiB
C
142 lines
2.5 KiB
C
#include <u.h>
|
|
#define NOPLAN9DEFINES
|
|
#include <libc.h>
|
|
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <netinet/tcp.h>
|
|
#include <sys/un.h>
|
|
#include <errno.h>
|
|
|
|
#undef sun
|
|
#define sun sockun
|
|
|
|
int
|
|
_p9netfd(char *dir)
|
|
{
|
|
int fd;
|
|
|
|
if(strncmp(dir, "/dev/fd/", 8) != 0)
|
|
return -1;
|
|
fd = strtol(dir+8, &dir, 0);
|
|
if(*dir != 0)
|
|
return -1;
|
|
return fd;
|
|
}
|
|
|
|
static void
|
|
putfd(char *dir, int fd)
|
|
{
|
|
snprint(dir, NETPATHLEN, "/dev/fd/%d", fd);
|
|
}
|
|
|
|
#undef unix
|
|
#define unix sockunix
|
|
|
|
int
|
|
p9announce(char *addr, char *dir)
|
|
{
|
|
int proto;
|
|
char *buf, *unix;
|
|
char *net;
|
|
int port, s;
|
|
int n;
|
|
socklen_t sn;
|
|
struct sockaddr_storage ss;
|
|
|
|
buf = strdup(addr);
|
|
if(buf == nil)
|
|
return -1;
|
|
|
|
if(p9dialparse(buf, &net, &unix, &ss, &port) < 0){
|
|
free(buf);
|
|
return -1;
|
|
}
|
|
if(strcmp(net, "tcp") == 0)
|
|
proto = SOCK_STREAM;
|
|
else if(strcmp(net, "udp") == 0)
|
|
proto = SOCK_DGRAM;
|
|
else if(strcmp(net, "unix") == 0)
|
|
goto Unix;
|
|
else{
|
|
werrstr("can only handle tcp, udp, and unix: not %s", net);
|
|
free(buf);
|
|
return -1;
|
|
}
|
|
free(buf);
|
|
|
|
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
|
|
&& n == SOCK_STREAM){
|
|
n = 1;
|
|
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&n, sizeof n);
|
|
}
|
|
if(bind(s, (struct sockaddr*)&ss, sizeof ss) < 0){
|
|
close(s);
|
|
return -1;
|
|
}
|
|
if(proto == SOCK_STREAM){
|
|
listen(s, 8);
|
|
putfd(dir, s);
|
|
}
|
|
return s;
|
|
|
|
Unix:
|
|
if((s = socket(ss.ss_family, SOCK_STREAM, 0)) < 0)
|
|
return -1;
|
|
if(bind(s, (struct sockaddr*)&ss, sizeof (struct sockaddr_un)) < 0){
|
|
if(errno == EADDRINUSE
|
|
&& connect(s, (struct sockaddr*)&ss, sizeof (struct sockaddr_un)) < 0
|
|
&& errno == ECONNREFUSED){
|
|
/* dead socket, so remove it */
|
|
remove(unix);
|
|
close(s);
|
|
if((s = socket(ss.ss_family, SOCK_STREAM, 0)) < 0)
|
|
return -1;
|
|
if(bind(s, (struct sockaddr*)&ss, sizeof (struct sockaddr_un)) >= 0)
|
|
goto Success;
|
|
}
|
|
close(s);
|
|
return -1;
|
|
}
|
|
Success:
|
|
listen(s, 8);
|
|
putfd(dir, s);
|
|
return s;
|
|
}
|
|
|
|
int
|
|
p9listen(char *dir, char *newdir)
|
|
{
|
|
int fd, one;
|
|
|
|
if((fd = _p9netfd(dir)) < 0){
|
|
werrstr("bad 'directory' in listen: %s", dir);
|
|
return -1;
|
|
}
|
|
|
|
if((fd = accept(fd, nil, nil)) < 0)
|
|
return -1;
|
|
|
|
one = 1;
|
|
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof one);
|
|
|
|
putfd(newdir, fd);
|
|
return fd;
|
|
}
|
|
|
|
int
|
|
p9accept(int cfd, char *dir)
|
|
{
|
|
int fd;
|
|
|
|
if((fd = _p9netfd(dir)) < 0){
|
|
werrstr("bad 'directory' in accept");
|
|
return -1;
|
|
}
|
|
/* need to dup because the listen fd will be closed */
|
|
return dup(fd);
|
|
}
|
|
|