announce.c (2737B)
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 static int 37 addrlen(struct sockaddr_storage *ss) 38 { 39 switch(ss->ss_family){ 40 case AF_INET: 41 return sizeof(struct sockaddr_in); 42 case AF_INET6: 43 return sizeof(struct sockaddr_in6); 44 case AF_UNIX: 45 return sizeof(struct sockaddr_un); 46 } 47 return 0; 48 } 49 50 int 51 p9announce(char *addr, char *dir) 52 { 53 int proto; 54 char *buf, *unix; 55 char *net; 56 int port, s; 57 int n; 58 socklen_t sn; 59 struct sockaddr_storage ss; 60 61 buf = strdup(addr); 62 if(buf == nil) 63 return -1; 64 65 if(p9dialparse(buf, &net, &unix, &ss, &port) < 0){ 66 free(buf); 67 return -1; 68 } 69 if(strcmp(net, "tcp") == 0) 70 proto = SOCK_STREAM; 71 else if(strcmp(net, "udp") == 0) 72 proto = SOCK_DGRAM; 73 else if(strcmp(net, "unix") == 0) 74 goto Unix; 75 else{ 76 werrstr("can only handle tcp, udp, and unix: not %s", net); 77 free(buf); 78 return -1; 79 } 80 free(buf); 81 82 if((s = socket(ss.ss_family, proto, 0)) < 0) 83 return -1; 84 sn = sizeof n; 85 if(port && getsockopt(s, SOL_SOCKET, SO_TYPE, (void*)&n, &sn) >= 0 86 && n == SOCK_STREAM){ 87 n = 1; 88 setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&n, sizeof n); 89 } 90 if(bind(s, (struct sockaddr*)&ss, addrlen(&ss)) < 0){ 91 close(s); 92 return -1; 93 } 94 if(proto == SOCK_STREAM){ 95 listen(s, 8); 96 putfd(dir, s); 97 } 98 return s; 99 100 Unix: 101 if((s = socket(ss.ss_family, SOCK_STREAM, 0)) < 0) 102 return -1; 103 if(bind(s, (struct sockaddr*)&ss, addrlen(&ss)) < 0){ 104 if(errno == EADDRINUSE 105 && connect(s, (struct sockaddr*)&ss, addrlen(&ss)) < 0 106 && errno == ECONNREFUSED){ 107 /* dead socket, so remove it */ 108 remove(unix); 109 close(s); 110 if((s = socket(ss.ss_family, SOCK_STREAM, 0)) < 0) 111 return -1; 112 if(bind(s, (struct sockaddr*)&ss, addrlen(&ss)) >= 0) 113 goto Success; 114 } 115 close(s); 116 return -1; 117 } 118 Success: 119 listen(s, 8); 120 putfd(dir, s); 121 return s; 122 } 123 124 int 125 p9listen(char *dir, char *newdir) 126 { 127 int fd, one; 128 129 if((fd = _p9netfd(dir)) < 0){ 130 werrstr("bad 'directory' in listen: %s", dir); 131 return -1; 132 } 133 134 if((fd = accept(fd, nil, nil)) < 0) 135 return -1; 136 137 one = 1; 138 setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof one); 139 140 putfd(newdir, fd); 141 return fd; 142 } 143 144 int 145 p9accept(int cfd, char *dir) 146 { 147 int fd; 148 149 if((fd = _p9netfd(dir)) < 0){ 150 werrstr("bad 'directory' in accept"); 151 return -1; 152 } 153 /* need to dup because the listen fd will be closed */ 154 return dup(fd); 155 }