#include #define NOPLAN9DEFINES #include #include #include #include #include #include #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); }