1 /* $OpenBSD: listen.c,v 1.15 2024/12/20 07:35:56 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/stat.h> 20 #include <sys/un.h> 21 22 #include <netinet/in.h> 23 #include <netinet/tcp.h> 24 #include <netdb.h> 25 26 #include <errno.h> 27 #include <fcntl.h> 28 #include <poll.h> 29 #include <stdio.h> 30 #include <string.h> 31 #include <unistd.h> 32 33 #include "listen.h" 34 #include "file.h" 35 #include "sock.h" 36 #include "utils.h" 37 38 int listen_pollfd(void *, struct pollfd *); 39 int listen_revents(void *, struct pollfd *); 40 void listen_in(void *); 41 void listen_out(void *); 42 void listen_hup(void *); 43 44 struct fileops listen_fileops = { 45 "listen", 46 listen_pollfd, 47 listen_revents, 48 listen_in, 49 listen_out, 50 listen_hup 51 }; 52 53 struct listen *listen_list = NULL; 54 55 void 56 listen_close(struct listen *f) 57 { 58 struct listen **pf; 59 60 for (pf = &listen_list; *pf != f; pf = &(*pf)->next) { 61 #ifdef DEBUG 62 if (*pf == NULL) { 63 logx(0, "%s: not on list", __func__); 64 panic(); 65 } 66 #endif 67 } 68 *pf = f->next; 69 70 if (f->path != NULL) { 71 xfree(f->path); 72 } 73 file_del(f->file); 74 close(f->fd); 75 xfree(f); 76 } 77 78 int 79 listen_new_un(char *path) 80 { 81 int sock, oldumask; 82 struct sockaddr_un sockname; 83 struct listen *f; 84 85 sock = socket(AF_UNIX, SOCK_STREAM, 0); 86 if (sock == -1) { 87 logx(0, "%s: failed to create socket", path); 88 return 0; 89 } 90 if (unlink(path) == -1 && errno != ENOENT) { 91 logx(0, "%s: failed to unlink socket", path); 92 goto bad_close; 93 } 94 sockname.sun_family = AF_UNIX; 95 strlcpy(sockname.sun_path, path, sizeof(sockname.sun_path)); 96 oldumask = umask(0111); 97 if (bind(sock, (struct sockaddr *)&sockname, 98 sizeof(struct sockaddr_un)) == -1) { 99 logx(0, "%s: failed to bind socket", path); 100 goto bad_close; 101 } 102 if (listen(sock, 1) == -1) { 103 logx(0, "%s: failed to listen", path); 104 goto bad_close; 105 } 106 umask(oldumask); 107 f = xmalloc(sizeof(struct listen)); 108 f->file = file_new(&listen_fileops, f, "unix", 1); 109 if (f->file == NULL) 110 goto bad_close; 111 f->path = xstrdup(path); 112 f->fd = sock; 113 f->next = listen_list; 114 listen_list = f; 115 return 1; 116 bad_close: 117 close(sock); 118 return 0; 119 } 120 121 int 122 listen_new_tcp(char *addr, unsigned int port) 123 { 124 char *host, serv[sizeof(unsigned int) * 3 + 1]; 125 struct addrinfo *ailist, *ai, aihints; 126 struct listen *f; 127 int s, error, opt = 1, n = 0; 128 129 /* 130 * obtain a list of possible addresses for the host/port 131 */ 132 memset(&aihints, 0, sizeof(struct addrinfo)); 133 snprintf(serv, sizeof(serv), "%u", port); 134 host = strcmp(addr, "-") == 0 ? NULL : addr; 135 aihints.ai_flags |= AI_PASSIVE; 136 aihints.ai_socktype = SOCK_STREAM; 137 aihints.ai_protocol = IPPROTO_TCP; 138 error = getaddrinfo(host, serv, &aihints, &ailist); 139 if (error) { 140 logx(0, "%s: failed to resolve address", addr); 141 return 0; 142 } 143 144 /* 145 * for each address, try create a listening socket bound on 146 * that address 147 */ 148 for (ai = ailist; ai != NULL; ai = ai->ai_next) { 149 s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 150 if (s == -1) { 151 logx(0, "%s: failed to create socket", addr); 152 continue; 153 } 154 opt = 1; 155 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, 156 &opt, sizeof(int)) == -1) { 157 logx(0, "%s: failed to set SO_REUSEADDR", addr); 158 goto bad_close; 159 } 160 if (bind(s, ai->ai_addr, ai->ai_addrlen) == -1) { 161 logx(0, "%s: failed to bind socket", addr); 162 goto bad_close; 163 } 164 if (listen(s, 1) == -1) { 165 logx(0, "%s: failed to listen", addr); 166 goto bad_close; 167 } 168 f = xmalloc(sizeof(struct listen)); 169 f->file = file_new(&listen_fileops, f, "tcp", 1); 170 if (f == NULL) { 171 bad_close: 172 close(s); 173 continue; 174 } 175 f->path = NULL; 176 f->fd = s; 177 f->next = listen_list; 178 listen_list = f; 179 n++; 180 } 181 freeaddrinfo(ailist); 182 return n; 183 } 184 185 int 186 listen_init(struct listen *f) 187 { 188 return 1; 189 } 190 191 int 192 listen_pollfd(void *arg, struct pollfd *pfd) 193 { 194 struct listen *f = arg; 195 196 f->slowaccept = file_slowaccept; 197 if (f->slowaccept) 198 return 0; 199 pfd->fd = f->fd; 200 pfd->events = POLLIN; 201 return 1; 202 } 203 204 int 205 listen_revents(void *arg, struct pollfd *pfd) 206 { 207 struct listen *f = arg; 208 209 if (f->slowaccept) 210 return 0; 211 return pfd->revents; 212 } 213 214 void 215 listen_in(void *arg) 216 { 217 struct listen *f = arg; 218 struct sockaddr caddr; 219 socklen_t caddrlen; 220 int sock, opt; 221 222 caddrlen = sizeof(caddrlen); 223 while ((sock = accept(f->fd, &caddr, &caddrlen)) == -1) { 224 if (errno == EINTR) 225 continue; 226 if (errno == ENFILE || errno == EMFILE) 227 file_slowaccept = 1; 228 return; 229 } 230 if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) { 231 logx(0, "%s: failed to set non-blocking mode", f->file->name); 232 goto bad_close; 233 } 234 if (f->path == NULL) { 235 opt = 1; 236 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, 237 &opt, sizeof(int)) == -1) { 238 logx(0, "%s: failed to set TCP_NODELAY flag", f->file->name); 239 goto bad_close; 240 } 241 } 242 if (sock_new(sock) == NULL) 243 goto bad_close; 244 return; 245 bad_close: 246 close(sock); 247 } 248 249 void 250 listen_out(void *arg) 251 { 252 } 253 254 void 255 listen_hup(void *arg) 256 { 257 struct listen *f = arg; 258 259 listen_close(f); 260 } 261