Line data Source code
1 : /*
2 : * lwan - web server
3 : * Copyright (c) 2012, 2013 L. A. F. Pereira <l@tia.mat.br>
4 : *
5 : * This program is free software; you can redistribute it and/or
6 : * modify it under the terms of the GNU General Public License
7 : * as published by the Free Software Foundation; either version 2
8 : * of the License, or any later version.
9 : *
10 : * This program is distributed in the hope that it will be useful,
11 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 : * GNU General Public License for more details.
14 : *
15 : * You should have received a copy of the GNU General Public License
16 : * along with this program; if not, write to the Free Software
17 : * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
18 : * USA.
19 : */
20 :
21 : #define _GNU_SOURCE
22 : #include <arpa/inet.h>
23 : #include <errno.h>
24 : #include <fcntl.h>
25 : #include <netdb.h>
26 : #include <netinet/tcp.h>
27 : #include <pthread.h>
28 : #include <stdlib.h>
29 : #include <string.h>
30 : #include <sys/resource.h>
31 : #include <sys/socket.h>
32 : #include <sys/types.h>
33 : #include <unistd.h>
34 :
35 : #include "lwan-private.h"
36 :
37 : #include "int-to-str.h"
38 : #include "sd-daemon.h"
39 :
40 : #ifdef __linux__
41 :
42 : static bool reno_supported;
43 92 : static void init_reno_supported(void)
44 : {
45 : FILE *allowed;
46 :
47 92 : reno_supported = false;
48 :
49 92 : allowed = fopen("/proc/sys/net/ipv4/tcp_allowed_congestion_control", "re");
50 92 : if (!allowed)
51 0 : return;
52 :
53 : char line[4096];
54 92 : if (fgets(line, sizeof(line), allowed)) {
55 92 : if (strstr(line, "reno"))
56 92 : reno_supported = true;
57 : }
58 92 : fclose(allowed);
59 : }
60 :
61 184 : static bool is_reno_supported(void)
62 : {
63 : static pthread_once_t reno_supported_once = PTHREAD_ONCE_INIT;
64 184 : pthread_once(&reno_supported_once, init_reno_supported);
65 184 : return reno_supported;
66 : }
67 : #endif
68 :
69 : static int backlog_size;
70 92 : static void init_backlog_size(void)
71 : {
72 : #ifdef __linux__
73 : FILE *somaxconn;
74 :
75 92 : somaxconn = fopen("/proc/sys/net/core/somaxconn", "re");
76 92 : if (somaxconn) {
77 : int tmp;
78 92 : if (fscanf(somaxconn, "%d", &tmp) == 1)
79 92 : backlog_size = tmp;
80 92 : fclose(somaxconn);
81 : }
82 : #endif
83 :
84 92 : if (!backlog_size)
85 0 : backlog_size = SOMAXCONN;
86 92 : }
87 :
88 184 : static int get_backlog_size(void)
89 : {
90 : static pthread_once_t backlog_size_once = PTHREAD_ONCE_INIT;
91 184 : pthread_once(&backlog_size_once, init_backlog_size);
92 184 : return backlog_size;
93 : }
94 :
95 184 : static int set_socket_flags(int fd)
96 : {
97 184 : int flags = fcntl(fd, F_GETFD);
98 184 : if (flags < 0)
99 0 : lwan_status_critical_perror("Could not obtain socket flags");
100 184 : if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0)
101 0 : lwan_status_critical_perror("Could not set socket flags");
102 :
103 184 : flags = fcntl(fd, F_GETFL);
104 184 : if (flags < 0)
105 0 : lwan_status_critical_perror("Could not obtain socket flags");
106 184 : if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0)
107 0 : lwan_status_critical_perror("Could not set socket flags");
108 :
109 184 : return fd;
110 : }
111 :
112 184 : static sa_family_t parse_listener_ipv4(char *listener, char **node, char **port)
113 : {
114 184 : char *colon = strrchr(listener, ':');
115 184 : if (!colon) {
116 0 : *port = "8080";
117 0 : if (!strchr(listener, '.')) {
118 : /* 8080 */
119 0 : *node = "0.0.0.0";
120 : } else {
121 : /* 127.0.0.1 */
122 0 : *node = listener;
123 : }
124 : } else {
125 : /*
126 : * 127.0.0.1:8080
127 : * localhost:8080
128 : */
129 184 : *colon = '\0';
130 184 : *node = listener;
131 184 : *port = colon + 1;
132 :
133 184 : if (streq(*node, "*")) {
134 : /* *:8080 */
135 184 : *node = "0.0.0.0";
136 :
137 184 : return AF_UNSPEC; /* IPv4 or IPv6 */
138 : }
139 : }
140 :
141 0 : return AF_INET;
142 : }
143 :
144 0 : static sa_family_t parse_listener_ipv6(char *listener, char **node, char **port)
145 : {
146 0 : char *last_colon = strrchr(listener, ':');
147 0 : if (!last_colon)
148 0 : return AF_MAX;
149 :
150 0 : if (*(last_colon - 1) == ']') {
151 : /* [::]:8080 */
152 0 : *(last_colon - 1) = '\0';
153 0 : *node = listener + 1;
154 0 : *port = last_colon + 1;
155 : } else {
156 : /* [::1] */
157 0 : listener[strlen(listener) - 1] = '\0';
158 0 : *node = listener + 1;
159 0 : *port = "8080";
160 : }
161 :
162 0 : return AF_INET6;
163 : }
164 :
165 184 : sa_family_t lwan_socket_parse_address(char *listener, char **node, char **port)
166 : {
167 184 : if (*listener == '[')
168 0 : return parse_listener_ipv6(listener, node, port);
169 :
170 184 : return parse_listener_ipv4(listener, node, port);
171 : }
172 :
173 184 : static sa_family_t parse_listener(char *listener, char **node, char **port)
174 : {
175 184 : if (streq(listener, "systemd")) {
176 0 : lwan_status_critical(
177 : "Listener configured to use systemd socket activation, "
178 : "but started outside systemd.");
179 : return AF_MAX;
180 : }
181 :
182 184 : return lwan_socket_parse_address(listener, node, port);
183 : }
184 :
185 184 : static int listen_addrinfo(int fd,
186 : const struct addrinfo *addr,
187 : bool print_listening_msg,
188 : bool is_https)
189 : {
190 184 : if (listen(fd, get_backlog_size()) < 0)
191 0 : lwan_status_critical_perror("listen");
192 :
193 184 : if (print_listening_msg) {
194 92 : const char *s_if_https = is_https ? "s" : "";
195 : char host_buf[NI_MAXHOST], serv_buf[NI_MAXSERV];
196 92 : int ret = getnameinfo(addr->ai_addr, addr->ai_addrlen, host_buf,
197 : sizeof(host_buf), serv_buf, sizeof(serv_buf),
198 : NI_NUMERICHOST | NI_NUMERICSERV);
199 92 : if (ret)
200 0 : lwan_status_critical("getnameinfo: %s", gai_strerror(ret));
201 :
202 92 : if (addr->ai_family == AF_INET6)
203 0 : lwan_status_info("Listening on http%s://[%s]:%s", s_if_https,
204 : host_buf, serv_buf);
205 : else
206 92 : lwan_status_info("Listening on http%s://%s:%s", s_if_https,
207 : host_buf, serv_buf);
208 : }
209 :
210 184 : return set_socket_flags(fd);
211 : }
212 :
213 : #define SET_SOCKET_OPTION(_domain, _option, _param) \
214 : do { \
215 : const socklen_t _param_size_ = (socklen_t)sizeof(*(_param)); \
216 : if (setsockopt(fd, (_domain), (_option), (_param), _param_size_) < 0) \
217 : lwan_status_critical_perror("setsockopt"); \
218 : } while (0)
219 :
220 : #define SET_SOCKET_OPTION_MAY_FAIL(_domain, _option, _param) \
221 : do { \
222 : const socklen_t _param_size_ = (socklen_t)sizeof(*(_param)); \
223 : if (setsockopt(fd, (_domain), (_option), (_param), _param_size_) < 0) \
224 : lwan_status_perror("%s not supported by the kernel", #_option); \
225 : } while (0)
226 :
227 184 : static int bind_and_listen_addrinfos(const struct addrinfo *addrs,
228 : bool print_listening_msg,
229 : bool is_https)
230 : {
231 : const struct addrinfo *addr;
232 :
233 : /* Try each address until we bind one successfully. */
234 184 : for (addr = addrs; addr; addr = addr->ai_next) {
235 184 : int fd = socket(addr->ai_family,
236 184 : addr->ai_socktype,
237 184 : addr->ai_protocol);
238 184 : if (fd < 0)
239 0 : continue;
240 :
241 184 : SET_SOCKET_OPTION(SOL_SOCKET, SO_REUSEADDR, (int[]){1});
242 : #ifdef SO_REUSEPORT
243 184 : SET_SOCKET_OPTION(SOL_SOCKET, SO_REUSEPORT, (int[]){1});
244 : #else
245 : lwan_status_critical("SO_REUSEPORT not supported by the OS");
246 : #endif
247 :
248 184 : if (!bind(fd, addr->ai_addr, addr->ai_addrlen))
249 184 : return listen_addrinfo(fd, addr, print_listening_msg, is_https);
250 :
251 0 : close(fd);
252 : }
253 :
254 0 : lwan_status_critical("Could not bind socket");
255 : }
256 :
257 184 : static int setup_socket_normally(const struct lwan *l,
258 : bool print_listening_msg,
259 : bool is_https,
260 : const char *listener_from_config)
261 : {
262 : char *node, *port;
263 184 : char *listener = strdupa(listener_from_config);
264 184 : sa_family_t family = parse_listener(listener, &node, &port);
265 :
266 184 : if (family == AF_MAX) {
267 0 : lwan_status_critical("Could not parse listener: %s",
268 : l->config.listener);
269 : }
270 :
271 : struct addrinfo *addrs;
272 184 : struct addrinfo hints = {.ai_family = family,
273 : .ai_socktype = SOCK_STREAM,
274 : .ai_flags = AI_PASSIVE};
275 :
276 184 : int ret = getaddrinfo(node, port, &hints, &addrs);
277 184 : if (ret)
278 0 : lwan_status_critical("getaddrinfo: %s", gai_strerror(ret));
279 :
280 184 : int fd = bind_and_listen_addrinfos(addrs, print_listening_msg, is_https);
281 184 : freeaddrinfo(addrs);
282 184 : return fd;
283 : }
284 :
285 184 : static int set_socket_options(const struct lwan *l, int fd)
286 : {
287 184 : SET_SOCKET_OPTION(SOL_SOCKET, SO_LINGER,
288 : (&(struct linger){.l_onoff = 1, .l_linger = 1}));
289 :
290 : #ifdef __linux__
291 :
292 : #ifndef TCP_FASTOPEN
293 : #define TCP_FASTOPEN 23
294 : #endif
295 :
296 184 : SET_SOCKET_OPTION_MAY_FAIL(SOL_SOCKET, SO_REUSEADDR, (int[]){1});
297 184 : SET_SOCKET_OPTION_MAY_FAIL(SOL_TCP, TCP_FASTOPEN, (int[]){5});
298 184 : SET_SOCKET_OPTION_MAY_FAIL(SOL_TCP, TCP_QUICKACK, (int[]){0});
299 184 : SET_SOCKET_OPTION_MAY_FAIL(SOL_TCP, TCP_DEFER_ACCEPT,
300 : (int[]){(int)l->config.keep_alive_timeout});
301 :
302 184 : if (is_reno_supported())
303 184 : setsockopt(fd, IPPROTO_TCP, TCP_CONGESTION, "reno", 4);
304 : #endif
305 :
306 184 : return fd;
307 : }
308 :
309 0 : static int from_systemd_socket(const struct lwan *l, int fd)
310 : {
311 0 : if (!sd_is_socket_inet(fd, AF_UNSPEC, SOCK_STREAM, 1, 0)) {
312 0 : lwan_status_critical("Passed file descriptor is not a "
313 : "listening TCP socket");
314 : }
315 :
316 0 : return set_socket_options(l, set_socket_flags(fd));
317 : }
318 :
319 184 : int lwan_create_listen_socket(const struct lwan *l,
320 : bool print_listening_msg,
321 : bool is_https)
322 : {
323 184 : const char *listener = is_https ? l->config.tls_listener
324 : : l->config.listener;
325 :
326 184 : if (!strncmp(listener, "systemd:", sizeof("systemd:") - 1)) {
327 0 : char **names = NULL;
328 0 : int n = sd_listen_fds_with_names(false, &names);
329 0 : int fd = -1;
330 :
331 0 : if (n < 0) {
332 0 : errno = -n;
333 0 : lwan_status_perror(
334 : "Could not parse socket activation data from systemd");
335 0 : return n;
336 : }
337 :
338 0 : listener += sizeof("systemd:") - 1;
339 :
340 0 : for (int i = 0; i < n; i++) {
341 0 : if (!strcmp(names[i], listener)) {
342 0 : fd = SD_LISTEN_FDS_START + i;
343 0 : break;
344 : }
345 : }
346 :
347 0 : strv_free(names);
348 :
349 0 : if (fd < 0) {
350 0 : lwan_status_critical(
351 : "No socket named `%s' has been passed from systemd", listener);
352 : }
353 :
354 0 : return from_systemd_socket(l, fd);
355 : }
356 :
357 184 : if (streq(listener, "systemd")) {
358 0 : int n = sd_listen_fds(false);
359 :
360 0 : if (n < 0) {
361 0 : errno = -n;
362 0 : lwan_status_perror("Could not obtain sockets passed from systemd");
363 0 : return n;
364 : }
365 :
366 0 : if (n != 1) {
367 0 : lwan_status_critical(
368 : "%d listeners passed from systemd. Must specify listeners with "
369 : "systemd:listener-name syntax",
370 : n);
371 : }
372 :
373 0 : return from_systemd_socket(l, SD_LISTEN_FDS_START);
374 : }
375 :
376 184 : int fd = setup_socket_normally(l, print_listening_msg, is_https, listener);
377 184 : return set_socket_options(l, fd);
378 : }
379 :
380 : #undef SET_SOCKET_OPTION
381 : #undef SET_SOCKET_OPTION_MAY_FAIL
|