xref: /openbsd-src/usr.bin/sndiod/listen.c (revision 7b6392009e6e5a7f8e494c162a4d259ea5e13a62)
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