xref: /openbsd/usr.bin/sndiod/listen.c (revision 43f8c1bd)
1 /*	$OpenBSD: listen.c,v 1.14 2020/01/23 20:55:01 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 			log_puts("listen_close: not on list\n");
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 		log_puts(path);
88 		log_puts(": failed to create socket\n");
89 		return 0;
90 	}
91 	if (unlink(path) == -1 && errno != ENOENT) {
92 		log_puts(path);
93 		log_puts(": failed to unlink socket\n");
94 		goto bad_close;
95 	}
96 	sockname.sun_family = AF_UNIX;
97 	strlcpy(sockname.sun_path, path, sizeof(sockname.sun_path));
98 	oldumask = umask(0111);
99 	if (bind(sock, (struct sockaddr *)&sockname,
100 		sizeof(struct sockaddr_un)) == -1) {
101 		log_puts(path);
102 		log_puts(": failed to bind socket\n");
103 		goto bad_close;
104 	}
105 	if (listen(sock, 1) == -1) {
106 		log_puts(path);
107 		log_puts(": failed to listen\n");
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 	f->fd = sock;
117 	f->next = listen_list;
118 	listen_list = f;
119 	return 1;
120  bad_close:
121 	close(sock);
122 	return 0;
123 }
124 
125 int
listen_new_tcp(char * addr,unsigned int port)126 listen_new_tcp(char *addr, unsigned int port)
127 {
128 	char *host, serv[sizeof(unsigned int) * 3 + 1];
129 	struct addrinfo *ailist, *ai, aihints;
130 	struct listen *f;
131 	int s, error, opt = 1, n = 0;
132 
133 	/*
134 	 * obtain a list of possible addresses for the host/port
135 	 */
136 	memset(&aihints, 0, sizeof(struct addrinfo));
137 	snprintf(serv, sizeof(serv), "%u", port);
138 	host = strcmp(addr, "-") == 0 ? NULL : addr;
139 	aihints.ai_flags |= AI_PASSIVE;
140 	aihints.ai_socktype = SOCK_STREAM;
141 	aihints.ai_protocol = IPPROTO_TCP;
142 	error = getaddrinfo(host, serv, &aihints, &ailist);
143 	if (error) {
144 		log_puts(addr);
145 		log_puts(": failed to resolve address\n");
146 		return 0;
147 	}
148 
149 	/*
150 	 * for each address, try create a listening socket bound on
151 	 * that address
152 	 */
153 	for (ai = ailist; ai != NULL; ai = ai->ai_next) {
154 		s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
155 		if (s == -1) {
156 			log_puts(addr);
157 			log_puts(": failed to create socket\n");
158 			continue;
159 		}
160 		opt = 1;
161 		if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
162 		    &opt, sizeof(int)) == -1) {
163 			log_puts(addr);
164 			log_puts(": failed to set SO_REUSEADDR\n");
165 			goto bad_close;
166 		}
167 		if (bind(s, ai->ai_addr, ai->ai_addrlen) == -1) {
168 			log_puts(addr);
169 			log_puts(": failed to bind socket\n");
170 			goto bad_close;
171 		}
172 		if (listen(s, 1) == -1) {
173 			log_puts(addr);
174 			log_puts(": failed to listen\n");
175 			goto bad_close;
176 		}
177 		f = xmalloc(sizeof(struct listen));
178 		f->file = file_new(&listen_fileops, f, addr, 1);
179 		if (f == NULL) {
180 		bad_close:
181 			close(s);
182 			continue;
183 		}
184 		f->path = NULL;
185 		f->fd = s;
186 		f->next = listen_list;
187 		listen_list = f;
188 		n++;
189 	}
190 	freeaddrinfo(ailist);
191 	return n;
192 }
193 
194 int
listen_init(struct listen * f)195 listen_init(struct listen *f)
196 {
197 	return 1;
198 }
199 
200 int
listen_pollfd(void * arg,struct pollfd * pfd)201 listen_pollfd(void *arg, struct pollfd *pfd)
202 {
203 	struct listen *f = arg;
204 
205 	f->slowaccept = file_slowaccept;
206 	if (f->slowaccept)
207 		return 0;
208 	pfd->fd = f->fd;
209 	pfd->events = POLLIN;
210 	return 1;
211 }
212 
213 int
listen_revents(void * arg,struct pollfd * pfd)214 listen_revents(void *arg, struct pollfd *pfd)
215 {
216 	struct listen *f = arg;
217 
218 	if (f->slowaccept)
219 		return 0;
220 	return pfd->revents;
221 }
222 
223 void
listen_in(void * arg)224 listen_in(void *arg)
225 {
226 	struct listen *f = arg;
227 	struct sockaddr caddr;
228 	socklen_t caddrlen;
229 	int sock, opt;
230 
231 	caddrlen = sizeof(caddrlen);
232 	while ((sock = accept(f->fd, &caddr, &caddrlen)) == -1) {
233 		if (errno == EINTR)
234 			continue;
235 		if (errno == ENFILE || errno == EMFILE)
236 			file_slowaccept = 1;
237 		return;
238 	}
239 	if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) {
240 		file_log(f->file);
241 		log_puts(": failed to set non-blocking mode\n");
242 		goto bad_close;
243 	}
244 	if (f->path == NULL) {
245 		opt = 1;
246 		if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
247 		    &opt, sizeof(int)) == -1) {
248 			file_log(f->file);
249 			log_puts(": failed to set TCP_NODELAY flag\n");
250 			goto bad_close;
251 		}
252 	}
253 	if (sock_new(sock) == NULL)
254 		goto bad_close;
255 	return;
256 bad_close:
257 	close(sock);
258 }
259 
260 void
listen_out(void * arg)261 listen_out(void *arg)
262 {
263 }
264 
265 void
listen_hup(void * arg)266 listen_hup(void *arg)
267 {
268 	struct listen *f = arg;
269 
270 	listen_close(f);
271 }
272