1*7b639200Sratchov /* $OpenBSD: listen.c,v 1.15 2024/12/20 07:35:56 ratchov Exp $ */ 287bc9f6aSratchov /* 387bc9f6aSratchov * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> 487bc9f6aSratchov * 587bc9f6aSratchov * Permission to use, copy, modify, and distribute this software for any 687bc9f6aSratchov * purpose with or without fee is hereby granted, provided that the above 787bc9f6aSratchov * copyright notice and this permission notice appear in all copies. 887bc9f6aSratchov * 987bc9f6aSratchov * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1087bc9f6aSratchov * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1187bc9f6aSratchov * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1287bc9f6aSratchov * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1387bc9f6aSratchov * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1487bc9f6aSratchov * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1587bc9f6aSratchov * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1687bc9f6aSratchov */ 1787bc9f6aSratchov #include <sys/types.h> 1887bc9f6aSratchov #include <sys/socket.h> 1987bc9f6aSratchov #include <sys/stat.h> 2087bc9f6aSratchov #include <sys/un.h> 2187bc9f6aSratchov 2287bc9f6aSratchov #include <netinet/in.h> 2387bc9f6aSratchov #include <netinet/tcp.h> 2487bc9f6aSratchov #include <netdb.h> 2587bc9f6aSratchov 2687bc9f6aSratchov #include <errno.h> 2787bc9f6aSratchov #include <fcntl.h> 2887bc9f6aSratchov #include <poll.h> 2987bc9f6aSratchov #include <stdio.h> 3087bc9f6aSratchov #include <string.h> 3187bc9f6aSratchov #include <unistd.h> 3287bc9f6aSratchov 3387bc9f6aSratchov #include "listen.h" 3487bc9f6aSratchov #include "file.h" 3587bc9f6aSratchov #include "sock.h" 3687bc9f6aSratchov #include "utils.h" 3787bc9f6aSratchov 3887bc9f6aSratchov int listen_pollfd(void *, struct pollfd *); 3987bc9f6aSratchov int listen_revents(void *, struct pollfd *); 4087bc9f6aSratchov void listen_in(void *); 4187bc9f6aSratchov void listen_out(void *); 4287bc9f6aSratchov void listen_hup(void *); 4387bc9f6aSratchov 4487bc9f6aSratchov struct fileops listen_fileops = { 4587bc9f6aSratchov "listen", 4687bc9f6aSratchov listen_pollfd, 4787bc9f6aSratchov listen_revents, 4887bc9f6aSratchov listen_in, 4987bc9f6aSratchov listen_out, 5087bc9f6aSratchov listen_hup 5187bc9f6aSratchov }; 5287bc9f6aSratchov 5387bc9f6aSratchov struct listen *listen_list = NULL; 5487bc9f6aSratchov 5587bc9f6aSratchov void 5687bc9f6aSratchov listen_close(struct listen *f) 5787bc9f6aSratchov { 5887bc9f6aSratchov struct listen **pf; 5987bc9f6aSratchov 6087bc9f6aSratchov for (pf = &listen_list; *pf != f; pf = &(*pf)->next) { 6187bc9f6aSratchov #ifdef DEBUG 6287bc9f6aSratchov if (*pf == NULL) { 63*7b639200Sratchov logx(0, "%s: not on list", __func__); 6487bc9f6aSratchov panic(); 6587bc9f6aSratchov } 6687bc9f6aSratchov #endif 6787bc9f6aSratchov } 6887bc9f6aSratchov *pf = f->next; 6987bc9f6aSratchov 70a447b73fSratchov if (f->path != NULL) { 7187bc9f6aSratchov xfree(f->path); 72a447b73fSratchov } 7387bc9f6aSratchov file_del(f->file); 7487bc9f6aSratchov close(f->fd); 7587bc9f6aSratchov xfree(f); 7687bc9f6aSratchov } 7787bc9f6aSratchov 78a447b73fSratchov int 7987bc9f6aSratchov listen_new_un(char *path) 8087bc9f6aSratchov { 8187bc9f6aSratchov int sock, oldumask; 8287bc9f6aSratchov struct sockaddr_un sockname; 8387bc9f6aSratchov struct listen *f; 8487bc9f6aSratchov 8587bc9f6aSratchov sock = socket(AF_UNIX, SOCK_STREAM, 0); 863aaa63ebSderaadt if (sock == -1) { 87*7b639200Sratchov logx(0, "%s: failed to create socket", path); 88a447b73fSratchov return 0; 8987bc9f6aSratchov } 903aaa63ebSderaadt if (unlink(path) == -1 && errno != ENOENT) { 91*7b639200Sratchov logx(0, "%s: failed to unlink socket", path); 9287bc9f6aSratchov goto bad_close; 9387bc9f6aSratchov } 9487bc9f6aSratchov sockname.sun_family = AF_UNIX; 9587bc9f6aSratchov strlcpy(sockname.sun_path, path, sizeof(sockname.sun_path)); 9687bc9f6aSratchov oldumask = umask(0111); 9787bc9f6aSratchov if (bind(sock, (struct sockaddr *)&sockname, 983aaa63ebSderaadt sizeof(struct sockaddr_un)) == -1) { 99*7b639200Sratchov logx(0, "%s: failed to bind socket", path); 10087bc9f6aSratchov goto bad_close; 10187bc9f6aSratchov } 1023aaa63ebSderaadt if (listen(sock, 1) == -1) { 103*7b639200Sratchov logx(0, "%s: failed to listen", path); 10487bc9f6aSratchov goto bad_close; 10587bc9f6aSratchov } 10687bc9f6aSratchov umask(oldumask); 10787bc9f6aSratchov f = xmalloc(sizeof(struct listen)); 108*7b639200Sratchov f->file = file_new(&listen_fileops, f, "unix", 1); 10987bc9f6aSratchov if (f->file == NULL) 11087bc9f6aSratchov goto bad_close; 11187bc9f6aSratchov f->path = xstrdup(path); 11287bc9f6aSratchov f->fd = sock; 11387bc9f6aSratchov f->next = listen_list; 11487bc9f6aSratchov listen_list = f; 115a447b73fSratchov return 1; 11687bc9f6aSratchov bad_close: 11787bc9f6aSratchov close(sock); 118a447b73fSratchov return 0; 11987bc9f6aSratchov } 12087bc9f6aSratchov 121a447b73fSratchov int 12287bc9f6aSratchov listen_new_tcp(char *addr, unsigned int port) 12387bc9f6aSratchov { 12487bc9f6aSratchov char *host, serv[sizeof(unsigned int) * 3 + 1]; 12587bc9f6aSratchov struct addrinfo *ailist, *ai, aihints; 12687bc9f6aSratchov struct listen *f; 12787bc9f6aSratchov int s, error, opt = 1, n = 0; 12887bc9f6aSratchov 12987bc9f6aSratchov /* 13087bc9f6aSratchov * obtain a list of possible addresses for the host/port 13187bc9f6aSratchov */ 13287bc9f6aSratchov memset(&aihints, 0, sizeof(struct addrinfo)); 13387bc9f6aSratchov snprintf(serv, sizeof(serv), "%u", port); 13487bc9f6aSratchov host = strcmp(addr, "-") == 0 ? NULL : addr; 13587bc9f6aSratchov aihints.ai_flags |= AI_PASSIVE; 13687bc9f6aSratchov aihints.ai_socktype = SOCK_STREAM; 13787bc9f6aSratchov aihints.ai_protocol = IPPROTO_TCP; 13887bc9f6aSratchov error = getaddrinfo(host, serv, &aihints, &ailist); 13987bc9f6aSratchov if (error) { 140*7b639200Sratchov logx(0, "%s: failed to resolve address", addr); 141a447b73fSratchov return 0; 14287bc9f6aSratchov } 14387bc9f6aSratchov 14487bc9f6aSratchov /* 14587bc9f6aSratchov * for each address, try create a listening socket bound on 14687bc9f6aSratchov * that address 14787bc9f6aSratchov */ 14887bc9f6aSratchov for (ai = ailist; ai != NULL; ai = ai->ai_next) { 14987bc9f6aSratchov s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 1503aaa63ebSderaadt if (s == -1) { 151*7b639200Sratchov logx(0, "%s: failed to create socket", addr); 15287bc9f6aSratchov continue; 15387bc9f6aSratchov } 15487bc9f6aSratchov opt = 1; 15587bc9f6aSratchov if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, 1563aaa63ebSderaadt &opt, sizeof(int)) == -1) { 157*7b639200Sratchov logx(0, "%s: failed to set SO_REUSEADDR", addr); 15887bc9f6aSratchov goto bad_close; 15987bc9f6aSratchov } 1603aaa63ebSderaadt if (bind(s, ai->ai_addr, ai->ai_addrlen) == -1) { 161*7b639200Sratchov logx(0, "%s: failed to bind socket", addr); 16287bc9f6aSratchov goto bad_close; 16387bc9f6aSratchov } 1643aaa63ebSderaadt if (listen(s, 1) == -1) { 165*7b639200Sratchov logx(0, "%s: failed to listen", addr); 16687bc9f6aSratchov goto bad_close; 16787bc9f6aSratchov } 16887bc9f6aSratchov f = xmalloc(sizeof(struct listen)); 169*7b639200Sratchov f->file = file_new(&listen_fileops, f, "tcp", 1); 17087bc9f6aSratchov if (f == NULL) { 17187bc9f6aSratchov bad_close: 17287bc9f6aSratchov close(s); 17387bc9f6aSratchov continue; 17487bc9f6aSratchov } 17587bc9f6aSratchov f->path = NULL; 17687bc9f6aSratchov f->fd = s; 17787bc9f6aSratchov f->next = listen_list; 17887bc9f6aSratchov listen_list = f; 17987bc9f6aSratchov n++; 18087bc9f6aSratchov } 18187bc9f6aSratchov freeaddrinfo(ailist); 182a447b73fSratchov return n; 18387bc9f6aSratchov } 18487bc9f6aSratchov 18587bc9f6aSratchov int 18687bc9f6aSratchov listen_init(struct listen *f) 18787bc9f6aSratchov { 18887bc9f6aSratchov return 1; 18987bc9f6aSratchov } 19087bc9f6aSratchov 19187bc9f6aSratchov int 19287bc9f6aSratchov listen_pollfd(void *arg, struct pollfd *pfd) 19387bc9f6aSratchov { 19487bc9f6aSratchov struct listen *f = arg; 19587bc9f6aSratchov 196f728557cSratchov f->slowaccept = file_slowaccept; 197f728557cSratchov if (f->slowaccept) 19887bc9f6aSratchov return 0; 19987bc9f6aSratchov pfd->fd = f->fd; 20087bc9f6aSratchov pfd->events = POLLIN; 20187bc9f6aSratchov return 1; 20287bc9f6aSratchov } 20387bc9f6aSratchov 20487bc9f6aSratchov int 20587bc9f6aSratchov listen_revents(void *arg, struct pollfd *pfd) 20687bc9f6aSratchov { 207f728557cSratchov struct listen *f = arg; 208f728557cSratchov 209f728557cSratchov if (f->slowaccept) 210f728557cSratchov return 0; 21187bc9f6aSratchov return pfd->revents; 21287bc9f6aSratchov } 21387bc9f6aSratchov 21487bc9f6aSratchov void 21587bc9f6aSratchov listen_in(void *arg) 21687bc9f6aSratchov { 21787bc9f6aSratchov struct listen *f = arg; 21887bc9f6aSratchov struct sockaddr caddr; 21987bc9f6aSratchov socklen_t caddrlen; 22087bc9f6aSratchov int sock, opt; 22187bc9f6aSratchov 22287bc9f6aSratchov caddrlen = sizeof(caddrlen); 2233aaa63ebSderaadt while ((sock = accept(f->fd, &caddr, &caddrlen)) == -1) { 22487bc9f6aSratchov if (errno == EINTR) 22587bc9f6aSratchov continue; 22687bc9f6aSratchov if (errno == ENFILE || errno == EMFILE) 22787bc9f6aSratchov file_slowaccept = 1; 22887bc9f6aSratchov return; 22987bc9f6aSratchov } 2303aaa63ebSderaadt if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) { 231*7b639200Sratchov logx(0, "%s: failed to set non-blocking mode", f->file->name); 23261bfbbe3Sratchov goto bad_close; 23387bc9f6aSratchov } 23487bc9f6aSratchov if (f->path == NULL) { 23587bc9f6aSratchov opt = 1; 23687bc9f6aSratchov if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, 2373aaa63ebSderaadt &opt, sizeof(int)) == -1) { 238*7b639200Sratchov logx(0, "%s: failed to set TCP_NODELAY flag", f->file->name); 23961bfbbe3Sratchov goto bad_close; 24061bfbbe3Sratchov } 24161bfbbe3Sratchov } 24261bfbbe3Sratchov if (sock_new(sock) == NULL) 24361bfbbe3Sratchov goto bad_close; 24487bc9f6aSratchov return; 24561bfbbe3Sratchov bad_close: 24687bc9f6aSratchov close(sock); 24787bc9f6aSratchov } 24887bc9f6aSratchov 24987bc9f6aSratchov void 25087bc9f6aSratchov listen_out(void *arg) 25187bc9f6aSratchov { 25287bc9f6aSratchov } 25387bc9f6aSratchov 25487bc9f6aSratchov void 25587bc9f6aSratchov listen_hup(void *arg) 25687bc9f6aSratchov { 25787bc9f6aSratchov struct listen *f = arg; 25887bc9f6aSratchov 25987bc9f6aSratchov listen_close(f); 26087bc9f6aSratchov } 261