|  | #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 | 
|  |  | 
|  | static int | 
|  | addrlen(struct sockaddr_storage *ss) | 
|  | { | 
|  | switch(ss->ss_family){ | 
|  | case AF_INET: | 
|  | return sizeof(struct sockaddr_in); | 
|  | case AF_INET6: | 
|  | return sizeof(struct sockaddr_in6); | 
|  | case AF_UNIX: | 
|  | return sizeof(struct sockaddr_un); | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | 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, addrlen(&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, addrlen(&ss)) < 0){ | 
|  | if(errno == EADDRINUSE | 
|  | && connect(s, (struct sockaddr*)&ss, addrlen(&ss)) < 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, addrlen(&ss)) >= 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); | 
|  | } | 
|  |  |