announce.c (2724B)
1 #include <u.h> 2 #define NOPLAN9DEFINES 3 #include <libc.h> 4 5 #include <sys/socket.h> 6 #include <netinet/in.h> 7 #include <netinet/tcp.h> 8 #include <sys/un.h> 9 #include <errno.h> 10 11 #undef sun 12 #define sun sockun 13 14 int 15 _p9netfd(char *dir) 16 { 17 int fd; 18 19 if(strncmp(dir, "/dev/fd/", 8) != 0) 20 return -1; 21 fd = strtol(dir+8, &dir, 0); 22 if(*dir != 0) 23 return -1; 24 return fd; 25 } 26 27 static void 28 putfd(char *dir, int fd) 29 { 30 snprint(dir, NETPATHLEN, "/dev/fd/%d", fd); 31 } 32 33 #undef unix 34 #define unix sockunix 35 36 int 37 p9announce(char *addr, char *dir) 38 { 39 int proto; 40 char *buf, *unix; 41 char *net; 42 u32int host; 43 int port, s; 44 int n; 45 socklen_t sn; 46 struct sockaddr_in sa; 47 struct sockaddr_un sun; 48 49 buf = strdup(addr); 50 if(buf == nil) 51 return -1; 52 53 if(p9dialparse(buf, &net, &unix, &host, &port) < 0){ 54 free(buf); 55 return -1; 56 } 57 if(strcmp(net, "tcp") == 0) 58 proto = SOCK_STREAM; 59 else if(strcmp(net, "udp") == 0) 60 proto = SOCK_DGRAM; 61 else if(strcmp(net, "unix") == 0) 62 goto Unix; 63 else{ 64 werrstr("can only handle tcp, udp, and unix: not %s", net); 65 free(buf); 66 return -1; 67 } 68 free(buf); 69 70 memset(&sa, 0, sizeof sa); 71 memmove(&sa.sin_addr, &host, 4); 72 sa.sin_family = AF_INET; 73 sa.sin_port = htons(port); 74 if((s = socket(AF_INET, proto, 0)) < 0) 75 return -1; 76 sn = sizeof n; 77 if(port && getsockopt(s, SOL_SOCKET, SO_TYPE, (void*)&n, &sn) >= 0 78 && n == SOCK_STREAM){ 79 n = 1; 80 setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&n, sizeof n); 81 } 82 if(bind(s, (struct sockaddr*)&sa, sizeof sa) < 0){ 83 close(s); 84 return -1; 85 } 86 if(proto == SOCK_STREAM){ 87 listen(s, 8); 88 putfd(dir, s); 89 } 90 return s; 91 92 Unix: 93 memset(&sun, 0, sizeof sun); 94 sun.sun_family = AF_UNIX; 95 strcpy(sun.sun_path, unix); 96 if((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) 97 return -1; 98 sn = sizeof sun; 99 if(bind(s, (struct sockaddr*)&sun, sizeof sun) < 0){ 100 if(errno == EADDRINUSE 101 && connect(s, (struct sockaddr*)&sun, sizeof sun) < 0 102 && errno == ECONNREFUSED){ 103 /* dead socket, so remove it */ 104 remove(unix); 105 close(s); 106 if((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) 107 return -1; 108 if(bind(s, (struct sockaddr*)&sun, sizeof sun) >= 0) 109 goto Success; 110 } 111 close(s); 112 return -1; 113 } 114 Success: 115 listen(s, 8); 116 putfd(dir, s); 117 return s; 118 } 119 120 int 121 p9listen(char *dir, char *newdir) 122 { 123 int fd, one; 124 125 if((fd = _p9netfd(dir)) < 0){ 126 werrstr("bad 'directory' in listen: %s", dir); 127 return -1; 128 } 129 130 if((fd = accept(fd, nil, nil)) < 0) 131 return -1; 132 133 one = 1; 134 setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof one); 135 136 putfd(newdir, fd); 137 return fd; 138 } 139 140 int 141 p9accept(int cfd, char *dir) 142 { 143 int fd; 144 145 if((fd = _p9netfd(dir)) < 0){ 146 werrstr("bad 'directory' in accept"); 147 return -1; 148 } 149 /* need to dup because the listen fd will be closed */ 150 return dup(fd); 151 } 152