1 /*	$OpenBSD$	*/
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 #include "bsd-compat.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
listen_close(struct listen * f)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 		unlink(f->path);
73 		xfree(f->path);
74 	}
75 	file_del(f->file);
76 	close(f->fd);
77 	xfree(f);
78 }
79 
80 int
listen_new_un(char * path)81 listen_new_un(char *path)
82 {
83 	int sock, oldumask;
84 	struct sockaddr_un sockname;
85 	struct listen *f;
86 
87 	sock = socket(AF_UNIX, SOCK_STREAM, 0);
88 	if (sock == -1) {
89 		log_puts(path);
90 		log_puts(": failed to create socket\n");
91 		return 0;
92 	}
93 	if (unlink(path) == -1 && errno != ENOENT) {
94 		log_puts(path);
95 		log_puts(": failed to unlink socket\n");
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)) == -1) {
103 		log_puts(path);
104 		log_puts(": failed to bind socket\n");
105 		goto bad_close;
106 	}
107 	if (listen(sock, 1) == -1) {
108 		log_puts(path);
109 		log_puts(": failed to listen\n");
110 		goto bad_close;
111 	}
112 	umask(oldumask);
113 	f = xmalloc(sizeof(struct listen));
114 	f->file = file_new(&listen_fileops, f, path, 1);
115 	if (f->file == NULL)
116 		goto bad_close;
117 	f->path = xstrdup(path);
118 	f->fd = sock;
119 	f->next = listen_list;
120 	listen_list = f;
121 	return 1;
122  bad_close:
123 	close(sock);
124 	return 0;
125 }
126 
127 int
listen_new_tcp(char * addr,unsigned int port)128 listen_new_tcp(char *addr, unsigned int port)
129 {
130 	char *host, serv[sizeof(unsigned int) * 3 + 1];
131 	struct addrinfo *ailist, *ai, aihints;
132 	struct listen *f;
133 	int s, error, opt = 1, n = 0;
134 
135 	/*
136 	 * obtain a list of possible addresses for the host/port
137 	 */
138 	memset(&aihints, 0, sizeof(struct addrinfo));
139 	snprintf(serv, sizeof(serv), "%u", port);
140 	host = strcmp(addr, "-") == 0 ? NULL : addr;
141 	aihints.ai_flags |= AI_PASSIVE;
142 	aihints.ai_socktype = SOCK_STREAM;
143 	aihints.ai_protocol = IPPROTO_TCP;
144 	error = getaddrinfo(host, serv, &aihints, &ailist);
145 	if (error) {
146 		log_puts(addr);
147 		log_puts(": failed to resolve address\n");
148 		return 0;
149 	}
150 
151 	/*
152 	 * for each address, try create a listening socket bound on
153 	 * that address
154 	 */
155 	for (ai = ailist; ai != NULL; ai = ai->ai_next) {
156 		s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
157 		if (s == -1) {
158 			log_puts(addr);
159 			log_puts(": failed to create socket\n");
160 			continue;
161 		}
162 		opt = 1;
163 		if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
164 		    &opt, sizeof(int)) == -1) {
165 			log_puts(addr);
166 			log_puts(": failed to set SO_REUSEADDR\n");
167 			goto bad_close;
168 		}
169 		if (ai->ai_family == AF_INET6) {
170 			/*
171 			 * make sure IPv6 sockets are restricted to IPv6
172 			 * addresses because we already use a IP socket
173 			 * for IP addresses
174 			 */
175 			opt = 1;
176 			if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
177 				&opt, sizeof(int)) == -1) {
178 				log_puts(addr);
179 				log_puts(": failed to set IPV6_V6ONLY\n");
180 				goto bad_close;
181 			}
182 		}
183 
184 		if (bind(s, ai->ai_addr, ai->ai_addrlen) == -1) {
185 			log_puts(addr);
186 			log_puts(": failed to bind socket\n");
187 			goto bad_close;
188 		}
189 		if (listen(s, 1) == -1) {
190 			log_puts(addr);
191 			log_puts(": failed to listen\n");
192 			goto bad_close;
193 		}
194 		f = xmalloc(sizeof(struct listen));
195 		f->file = file_new(&listen_fileops, f, addr, 1);
196 		if (f == NULL) {
197 		bad_close:
198 			close(s);
199 			continue;
200 		}
201 		f->path = NULL;
202 		f->fd = s;
203 		f->next = listen_list;
204 		listen_list = f;
205 		n++;
206 	}
207 	freeaddrinfo(ailist);
208 	return n;
209 }
210 
211 int
listen_init(struct listen * f)212 listen_init(struct listen *f)
213 {
214 	return 1;
215 }
216 
217 int
listen_pollfd(void * arg,struct pollfd * pfd)218 listen_pollfd(void *arg, struct pollfd *pfd)
219 {
220 	struct listen *f = arg;
221 
222 	f->slowaccept = file_slowaccept;
223 	if (f->slowaccept)
224 		return 0;
225 	pfd->fd = f->fd;
226 	pfd->events = POLLIN;
227 	return 1;
228 }
229 
230 int
listen_revents(void * arg,struct pollfd * pfd)231 listen_revents(void *arg, struct pollfd *pfd)
232 {
233 	struct listen *f = arg;
234 
235 	if (f->slowaccept)
236 		return 0;
237 	return pfd->revents;
238 }
239 
240 void
listen_in(void * arg)241 listen_in(void *arg)
242 {
243 	struct listen *f = arg;
244 	struct sockaddr caddr;
245 	socklen_t caddrlen;
246 	int sock, opt;
247 
248 	caddrlen = sizeof(caddrlen);
249 	while ((sock = accept(f->fd, &caddr, &caddrlen)) == -1) {
250 		if (errno == EINTR)
251 			continue;
252 		if (errno == ENFILE || errno == EMFILE)
253 			file_slowaccept = 1;
254 		return;
255 	}
256 	if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) {
257 		file_log(f->file);
258 		log_puts(": failed to set non-blocking mode\n");
259 		goto bad_close;
260 	}
261 	if (f->path == NULL) {
262 		opt = 1;
263 		if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
264 		    &opt, sizeof(int)) == -1) {
265 			file_log(f->file);
266 			log_puts(": failed to set TCP_NODELAY flag\n");
267 			goto bad_close;
268 		}
269 	}
270 	if (sock_new(sock) == NULL)
271 		goto bad_close;
272 	return;
273 bad_close:
274 	close(sock);
275 }
276 
277 void
listen_out(void * arg)278 listen_out(void *arg)
279 {
280 }
281 
282 void
listen_hup(void * arg)283 listen_hup(void *arg)
284 {
285 	struct listen *f = arg;
286 
287 	listen_close(f);
288 }
289