#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 int p9announce(char *addr, char *dir) { int proto; char *buf, *unix; char *net; u32int host; int port, s; int n; socklen_t sn; struct sockaddr_in sa; struct sockaddr_un sun; buf = strdup(addr); if(buf == nil) return -1; if(p9dialparse(buf, &net, &unix, &host, &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); memset(&sa, 0, sizeof sa); memmove(&sa.sin_addr, &host, 4); sa.sin_family = AF_INET; sa.sin_port = htons(port); if((s = socket(AF_INET, 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*)&sa, sizeof sa) < 0){ close(s); return -1; } if(proto == SOCK_STREAM){ listen(s, 8); putfd(dir, s); } return s; Unix: memset(&sun, 0, sizeof sun); sun.sun_family = AF_UNIX; strcpy(sun.sun_path, unix); if((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) return -1; sn = sizeof sun; if(bind(s, (struct sockaddr*)&sun, sizeof sun) < 0){ if(errno == EADDRINUSE && connect(s, (struct sockaddr*)&sun, sizeof sun) < 0 && errno == ECONNREFUSED){ /* dead socket, so remove it */ remove(unix); close(s); if((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) return -1; if(bind(s, (struct sockaddr*)&sun, sizeof sun) >= 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); }