1 /* $OpenBSD: listen.c,v 1.2 2013/03/13 08:28:33 ratchov Exp $ */ 2 /* 3 * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 #include <sys/types.h> 18 #include <sys/socket.h> 19 #include <sys/signal.h> 20 #include <sys/stat.h> 21 #include <sys/un.h> 22 23 #include <netinet/in.h> 24 #include <netinet/tcp.h> 25 #include <netdb.h> 26 27 #include <err.h> 28 #include <errno.h> 29 #include <fcntl.h> 30 #include <poll.h> 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <string.h> 34 #include <unistd.h> 35 36 #include "listen.h" 37 #include "file.h" 38 #include "sock.h" 39 #include "utils.h" 40 41 int listen_pollfd(void *, struct pollfd *); 42 int listen_revents(void *, struct pollfd *); 43 void listen_in(void *); 44 void listen_out(void *); 45 void listen_hup(void *); 46 47 struct fileops listen_fileops = { 48 "listen", 49 listen_pollfd, 50 listen_revents, 51 listen_in, 52 listen_out, 53 listen_hup 54 }; 55 56 struct listen *listen_list = NULL; 57 58 void 59 listen_close(struct listen *f) 60 { 61 struct listen **pf; 62 63 for (pf = &listen_list; *pf != f; pf = &(*pf)->next) { 64 #ifdef DEBUG 65 if (*pf == NULL) { 66 log_puts("listen_close: not on list\n"); 67 panic(); 68 } 69 #endif 70 } 71 *pf = f->next; 72 73 if (f->path != NULL) { 74 unlink(f->path); 75 xfree(f->path); 76 } 77 file_del(f->file); 78 close(f->fd); 79 xfree(f); 80 } 81 82 void 83 listen_new_un(char *path) 84 { 85 int sock, oldumask; 86 struct sockaddr_un sockname; 87 struct listen *f; 88 89 sock = socket(AF_UNIX, SOCK_STREAM, 0); 90 if (sock < 0) { 91 perror("socket"); 92 exit(1); 93 } 94 if (unlink(path) < 0 && errno != ENOENT) { 95 perror("unlink"); 96 goto bad_close; 97 } 98 sockname.sun_family = AF_UNIX; 99 strlcpy(sockname.sun_path, path, sizeof(sockname.sun_path)); 100 oldumask = umask(0111); 101 if (bind(sock, (struct sockaddr *)&sockname, 102 sizeof(struct sockaddr_un)) < 0) { 103 perror("bind"); 104 goto bad_close; 105 } 106 if (listen(sock, 1) < 0) { 107 perror("listen"); 108 goto bad_close; 109 } 110 umask(oldumask); 111 f = xmalloc(sizeof(struct listen)); 112 f->file = file_new(&listen_fileops, f, path, 1); 113 if (f->file == NULL) 114 goto bad_close; 115 f->path = xstrdup(path); 116 if (f->path == NULL) { 117 perror("strdup"); 118 exit(1); 119 } 120 f->fd = sock; 121 f->next = listen_list; 122 listen_list = f; 123 return; 124 bad_close: 125 close(sock); 126 exit(1); 127 } 128 129 void 130 listen_new_tcp(char *addr, unsigned int port) 131 { 132 char *host, serv[sizeof(unsigned int) * 3 + 1]; 133 struct addrinfo *ailist, *ai, aihints; 134 struct listen *f; 135 int s, error, opt = 1, n = 0; 136 137 /* 138 * obtain a list of possible addresses for the host/port 139 */ 140 memset(&aihints, 0, sizeof(struct addrinfo)); 141 snprintf(serv, sizeof(serv), "%u", port); 142 host = strcmp(addr, "-") == 0 ? NULL : addr; 143 aihints.ai_flags |= AI_PASSIVE; 144 aihints.ai_socktype = SOCK_STREAM; 145 aihints.ai_protocol = IPPROTO_TCP; 146 error = getaddrinfo(host, serv, &aihints, &ailist); 147 if (error) { 148 fprintf(stderr, "%s: %s\n", addr, gai_strerror(error)); 149 exit(1); 150 } 151 152 /* 153 * for each address, try create a listening socket bound on 154 * that address 155 */ 156 for (ai = ailist; ai != NULL; ai = ai->ai_next) { 157 s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 158 if (s < 0) { 159 perror("socket"); 160 continue; 161 } 162 opt = 1; 163 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, 164 &opt, sizeof(int)) < 0) { 165 perror("setsockopt"); 166 goto bad_close; 167 } 168 if (ai->ai_family == AF_INET6) { 169 /* 170 * make sure IPv6 sockets are restricted to IPv6 171 * addresses because we already use a IP socket 172 * for IP addresses 173 */ 174 opt = 1; 175 if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, 176 &opt, sizeof(int)) < 0) { 177 perror("setsockopt"); 178 goto bad_close; 179 } 180 } 181 182 if (bind(s, ai->ai_addr, ai->ai_addrlen) < 0) { 183 perror("bind"); 184 goto bad_close; 185 } 186 if (listen(s, 1) < 0) { 187 perror("listen"); 188 goto bad_close; 189 } 190 f = xmalloc(sizeof(struct listen)); 191 f->file = file_new(&listen_fileops, f, addr, 1); 192 if (f == NULL) { 193 bad_close: 194 close(s); 195 continue; 196 } 197 f->path = NULL; 198 f->fd = s; 199 f->next = listen_list; 200 listen_list = f; 201 n++; 202 } 203 freeaddrinfo(ailist); 204 if (n == 0) 205 exit(1); 206 } 207 208 int 209 listen_init(struct listen *f) 210 { 211 return 1; 212 } 213 214 int 215 listen_pollfd(void *arg, struct pollfd *pfd) 216 { 217 struct listen *f = arg; 218 219 if (file_slowaccept) 220 return 0; 221 pfd->fd = f->fd; 222 pfd->events = POLLIN; 223 return 1; 224 } 225 226 int 227 listen_revents(void *arg, struct pollfd *pfd) 228 { 229 return pfd->revents; 230 } 231 232 void 233 listen_in(void *arg) 234 { 235 struct listen *f = arg; 236 struct sockaddr caddr; 237 socklen_t caddrlen; 238 int sock, opt; 239 240 caddrlen = sizeof(caddrlen); 241 while ((sock = accept(f->fd, &caddr, &caddrlen)) < 0) { 242 if (errno == EINTR) 243 continue; 244 if (errno == ENFILE || errno == EMFILE) 245 file_slowaccept = 1; 246 else if (errno != ECONNABORTED && errno != EWOULDBLOCK) 247 perror("accept"); 248 return; 249 } 250 if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) { 251 perror("fcntl(sock, O_NONBLOCK)"); 252 close(sock); 253 return; 254 } 255 if (f->path == NULL) { 256 opt = 1; 257 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, 258 &opt, sizeof(int)) < 0) { 259 perror("setsockopt"); 260 close(sock); 261 return; 262 } 263 } 264 if (sock_new(sock) == NULL) { 265 close(sock); 266 return; 267 } 268 } 269 270 void 271 listen_out(void *arg) 272 { 273 } 274 275 void 276 listen_hup(void *arg) 277 { 278 struct listen *f = arg; 279 280 listen_close(f); 281 } 282