1 /* $OpenBSD: listen.c,v 1.15 2024/12/20 07:35:56 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 logx(0, "%s: not on list", __func__);
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 logx(0, "%s: failed to create socket", path);
88 return 0;
89 }
90 if (unlink(path) == -1 && errno != ENOENT) {
91 logx(0, "%s: failed to unlink socket", path);
92 goto bad_close;
93 }
94 sockname.sun_family = AF_UNIX;
95 strlcpy(sockname.sun_path, path, sizeof(sockname.sun_path));
96 oldumask = umask(0111);
97 if (bind(sock, (struct sockaddr *)&sockname,
98 sizeof(struct sockaddr_un)) == -1) {
99 logx(0, "%s: failed to bind socket", path);
100 goto bad_close;
101 }
102 if (listen(sock, 1) == -1) {
103 logx(0, "%s: failed to listen", path);
104 goto bad_close;
105 }
106 umask(oldumask);
107 f = xmalloc(sizeof(struct listen));
108 f->file = file_new(&listen_fileops, f, "unix", 1);
109 if (f->file == NULL)
110 goto bad_close;
111 f->path = xstrdup(path);
112 f->fd = sock;
113 f->next = listen_list;
114 listen_list = f;
115 return 1;
116 bad_close:
117 close(sock);
118 return 0;
119 }
120
121 int
listen_new_tcp(char * addr,unsigned int port)122 listen_new_tcp(char *addr, unsigned int port)
123 {
124 char *host, serv[sizeof(unsigned int) * 3 + 1];
125 struct addrinfo *ailist, *ai, aihints;
126 struct listen *f;
127 int s, error, opt = 1, n = 0;
128
129 /*
130 * obtain a list of possible addresses for the host/port
131 */
132 memset(&aihints, 0, sizeof(struct addrinfo));
133 snprintf(serv, sizeof(serv), "%u", port);
134 host = strcmp(addr, "-") == 0 ? NULL : addr;
135 aihints.ai_flags |= AI_PASSIVE;
136 aihints.ai_socktype = SOCK_STREAM;
137 aihints.ai_protocol = IPPROTO_TCP;
138 error = getaddrinfo(host, serv, &aihints, &ailist);
139 if (error) {
140 logx(0, "%s: failed to resolve address", addr);
141 return 0;
142 }
143
144 /*
145 * for each address, try create a listening socket bound on
146 * that address
147 */
148 for (ai = ailist; ai != NULL; ai = ai->ai_next) {
149 s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
150 if (s == -1) {
151 logx(0, "%s: failed to create socket", addr);
152 continue;
153 }
154 opt = 1;
155 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
156 &opt, sizeof(int)) == -1) {
157 logx(0, "%s: failed to set SO_REUSEADDR", addr);
158 goto bad_close;
159 }
160 if (bind(s, ai->ai_addr, ai->ai_addrlen) == -1) {
161 logx(0, "%s: failed to bind socket", addr);
162 goto bad_close;
163 }
164 if (listen(s, 1) == -1) {
165 logx(0, "%s: failed to listen", addr);
166 goto bad_close;
167 }
168 f = xmalloc(sizeof(struct listen));
169 f->file = file_new(&listen_fileops, f, "tcp", 1);
170 if (f == NULL) {
171 bad_close:
172 close(s);
173 continue;
174 }
175 f->path = NULL;
176 f->fd = s;
177 f->next = listen_list;
178 listen_list = f;
179 n++;
180 }
181 freeaddrinfo(ailist);
182 return n;
183 }
184
185 int
listen_init(struct listen * f)186 listen_init(struct listen *f)
187 {
188 return 1;
189 }
190
191 int
listen_pollfd(void * arg,struct pollfd * pfd)192 listen_pollfd(void *arg, struct pollfd *pfd)
193 {
194 struct listen *f = arg;
195
196 f->slowaccept = file_slowaccept;
197 if (f->slowaccept)
198 return 0;
199 pfd->fd = f->fd;
200 pfd->events = POLLIN;
201 return 1;
202 }
203
204 int
listen_revents(void * arg,struct pollfd * pfd)205 listen_revents(void *arg, struct pollfd *pfd)
206 {
207 struct listen *f = arg;
208
209 if (f->slowaccept)
210 return 0;
211 return pfd->revents;
212 }
213
214 void
listen_in(void * arg)215 listen_in(void *arg)
216 {
217 struct listen *f = arg;
218 struct sockaddr caddr;
219 socklen_t caddrlen;
220 int sock, opt;
221
222 caddrlen = sizeof(caddrlen);
223 while ((sock = accept(f->fd, &caddr, &caddrlen)) == -1) {
224 if (errno == EINTR)
225 continue;
226 if (errno == ENFILE || errno == EMFILE)
227 file_slowaccept = 1;
228 return;
229 }
230 if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) {
231 logx(0, "%s: failed to set non-blocking mode", f->file->name);
232 goto bad_close;
233 }
234 if (f->path == NULL) {
235 opt = 1;
236 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
237 &opt, sizeof(int)) == -1) {
238 logx(0, "%s: failed to set TCP_NODELAY flag", f->file->name);
239 goto bad_close;
240 }
241 }
242 if (sock_new(sock) == NULL)
243 goto bad_close;
244 return;
245 bad_close:
246 close(sock);
247 }
248
249 void
listen_out(void * arg)250 listen_out(void *arg)
251 {
252 }
253
254 void
listen_hup(void * arg)255 listen_hup(void *arg)
256 {
257 struct listen *f = arg;
258
259 listen_close(f);
260 }
261