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