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