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