xref: /openbsd/usr.bin/sndiod/listen.c (revision 7b639200)
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
listen_close(struct listen * f)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
listen_new_un(char * path)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
listen_new_tcp(char * addr,unsigned int port)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
listen_init(struct listen * f)186 listen_init(struct listen *f)
187 {
188 	return 1;
189 }
190 
191 int
listen_pollfd(void * arg,struct pollfd * pfd)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
listen_revents(void * arg,struct pollfd * pfd)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
listen_in(void * arg)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
listen_out(void * arg)250 listen_out(void *arg)
251 {
252 }
253 
254 void
listen_hup(void * arg)255 listen_hup(void *arg)
256 {
257 	struct listen *f = arg;
258 
259 	listen_close(f);
260 }
261