LCOV - code coverage report
Current view: top level - lib - lwan-socket.c (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 89 150 59.3 %
Date: 2023-04-18 16:19:03 Functions: 13 15 86.7 %

          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

Generated by: LCOV version 1.15-2-gb9d6727