socket.c (4977B)
1 /* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail> 2 * Copyright ©2004-2006 Anselm R. Garbe <garbeam at gmail dot com> 3 * See LICENSE file for license details. 4 */ 5 #include <errno.h> 6 #include <netdb.h> 7 #include <netinet/in.h> 8 #include <signal.h> 9 #include <string.h> 10 #include <stdlib.h> 11 #include <stdio.h> 12 #include <sys/types.h> 13 #include <sys/stat.h> 14 #include <sys/socket.h> 15 #include <sys/un.h> 16 #include <unistd.h> 17 #include "ixp_local.h" 18 19 /* Note: These functions modify the strings that they are passed. 20 * The lookup function duplicates the original string, so it is 21 * not modified. 22 */ 23 24 /* From FreeBSD's sys/su.h */ 25 #define SUN_LEN(su) \ 26 (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path)) 27 28 typedef struct addrinfo addrinfo; 29 typedef struct sockaddr sockaddr; 30 typedef struct sockaddr_un sockaddr_un; 31 typedef struct sockaddr_in sockaddr_in; 32 33 static char* 34 get_port(char *addr) { 35 char *s; 36 37 s = strchr(addr, '!'); 38 if(s == nil) { 39 werrstr("no port provided"); 40 return nil; 41 } 42 43 *s++ = '\0'; 44 if(*s == '\0') { 45 werrstr("invalid port number"); 46 return nil; 47 } 48 return s; 49 } 50 51 static int 52 sock_unix(char *address, sockaddr_un *sa, socklen_t *salen) { 53 int fd; 54 55 memset(sa, 0, sizeof *sa); 56 57 sa->sun_family = AF_UNIX; 58 strncpy(sa->sun_path, address, sizeof sa->sun_path); 59 *salen = SUN_LEN(sa); 60 61 fd = socket(AF_UNIX, SOCK_STREAM, 0); 62 if(fd < 0) 63 return -1; 64 return fd; 65 } 66 67 static int 68 dial_unix(char *address) { 69 sockaddr_un sa; 70 socklen_t salen; 71 int fd; 72 73 fd = sock_unix(address, &sa, &salen); 74 if(fd == -1) 75 return fd; 76 77 if(connect(fd, (sockaddr*) &sa, salen)) { 78 close(fd); 79 return -1; 80 } 81 return fd; 82 } 83 84 static int 85 announce_unix(char *file) { 86 const int yes = 1; 87 sockaddr_un sa; 88 socklen_t salen; 89 int fd; 90 91 signal(SIGPIPE, SIG_IGN); 92 93 fd = sock_unix(file, &sa, &salen); 94 if(fd == -1) 95 return fd; 96 97 if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void*)&yes, sizeof yes) < 0) 98 goto fail; 99 100 unlink(file); 101 if(bind(fd, (sockaddr*)&sa, salen) < 0) 102 goto fail; 103 104 chmod(file, S_IRWXU); 105 if(listen(fd, IXP_MAX_CACHE) < 0) 106 goto fail; 107 108 return fd; 109 110 fail: 111 close(fd); 112 return -1; 113 } 114 115 static addrinfo* 116 alookup(char *host, int announce) { 117 addrinfo hints, *ret; 118 char *port; 119 int err; 120 121 /* Truncates host at '!' */ 122 port = get_port(host); 123 if(port == nil) 124 return nil; 125 126 memset(&hints, 0, sizeof hints); 127 hints.ai_family = AF_INET; 128 hints.ai_socktype = SOCK_STREAM; 129 130 if(announce) { 131 hints.ai_flags = AI_PASSIVE; 132 if(!strcmp(host, "*")) 133 host = nil; 134 } 135 136 err = getaddrinfo(host, port, &hints, &ret); 137 if(err) { 138 werrstr("getaddrinfo: %s", gai_strerror(err)); 139 return nil; 140 } 141 return ret; 142 } 143 144 static int 145 ai_socket(addrinfo *ai) { 146 return socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 147 } 148 149 static int 150 dial_tcp(char *host) { 151 addrinfo *ai, *aip; 152 int fd; 153 154 aip = alookup(host, 0); 155 if(aip == nil) 156 return -1; 157 158 SET(fd); 159 for(ai = aip; ai; ai = ai->ai_next) { 160 fd = ai_socket(ai); 161 if(fd == -1) { 162 werrstr("socket: %s", strerror(errno)); 163 continue; 164 } 165 166 if(connect(fd, ai->ai_addr, ai->ai_addrlen) == 0) 167 break; 168 169 werrstr("connect: %s", strerror(errno)); 170 close(fd); 171 fd = -1; 172 } 173 174 freeaddrinfo(aip); 175 return fd; 176 } 177 178 static int 179 announce_tcp(char *host) { 180 addrinfo *ai, *aip; 181 int fd; 182 183 aip = alookup(host, 1); 184 if(aip == nil) 185 return -1; 186 187 /* Probably don't need to loop */ 188 SET(fd); 189 for(ai = aip; ai; ai = ai->ai_next) { 190 fd = ai_socket(ai); 191 if(fd == -1) 192 continue; 193 194 if(bind(fd, ai->ai_addr, ai->ai_addrlen) < 0) 195 goto fail; 196 197 if(listen(fd, IXP_MAX_CACHE) < 0) 198 goto fail; 199 break; 200 fail: 201 close(fd); 202 fd = -1; 203 } 204 205 freeaddrinfo(aip); 206 return fd; 207 } 208 209 typedef struct addrtab addrtab; 210 static 211 struct addrtab { 212 char *type; 213 int (*fn)(char*); 214 } dtab[] = { 215 {"tcp", dial_tcp}, 216 {"unix", dial_unix}, 217 {0, 0} 218 }, atab[] = { 219 {"tcp", announce_tcp}, 220 {"unix", announce_unix}, 221 {0, 0} 222 }; 223 224 static int 225 lookup(const char *address, addrtab *tab) { 226 char *addr, *type; 227 int ret; 228 229 ret = -1; 230 type = estrdup(address); 231 232 addr = strchr(type, '!'); 233 if(addr == nil) 234 werrstr("no address type defined"); 235 else { 236 *addr++ = '\0'; 237 for(; tab->type; tab++) 238 if(strcmp(tab->type, type) == 0) break; 239 if(tab->type == nil) 240 werrstr("unsupported address type"); 241 else 242 ret = tab->fn(addr); 243 } 244 245 free(type); 246 return ret; 247 } 248 249 /** 250 * Function: ixp_dial 251 * Function: ixp_announce 252 * 253 * Params: 254 * address: An address on which to connect or listen, 255 * specified in the Plan 9 resources 256 * specification format 257 * (<protocol>!address[!<port>]) 258 * 259 * These functions hide some of the ugliness of Berkely 260 * Sockets. ixp_dial connects to the resource at P<address>, 261 * while ixp_announce begins listening on P<address>. 262 * 263 * Returns: 264 * These functions return file descriptors on success, and -1 265 * on failure. ixp_errbuf(3) may be inspected on failure. 266 * See also: 267 * socket(2) 268 */ 269 270 int 271 ixp_dial(const char *address) { 272 return lookup(address, dtab); 273 } 274 275 int 276 ixp_announce(const char *address) { 277 return lookup(address, atab); 278 } 279