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