LCOV - code coverage report
Current view: top level - socket-proxy - socket-proxyd.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 30 360 8.3 %
Date: 2019-08-23 13:36:53 Functions: 5 18 27.8 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 10 333 3.0 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: LGPL-2.1+ */
       2                 :            : 
       3                 :            : #include <errno.h>
       4                 :            : #include <fcntl.h>
       5                 :            : #include <getopt.h>
       6                 :            : #include <netdb.h>
       7                 :            : #include <stdio.h>
       8                 :            : #include <stdlib.h>
       9                 :            : #include <string.h>
      10                 :            : #include <sys/socket.h>
      11                 :            : #include <sys/un.h>
      12                 :            : #include <unistd.h>
      13                 :            : 
      14                 :            : #include "sd-daemon.h"
      15                 :            : #include "sd-event.h"
      16                 :            : #include "sd-resolve.h"
      17                 :            : 
      18                 :            : #include "alloc-util.h"
      19                 :            : #include "errno-util.h"
      20                 :            : #include "fd-util.h"
      21                 :            : #include "log.h"
      22                 :            : #include "main-func.h"
      23                 :            : #include "parse-util.h"
      24                 :            : #include "path-util.h"
      25                 :            : #include "pretty-print.h"
      26                 :            : #include "resolve-private.h"
      27                 :            : #include "set.h"
      28                 :            : #include "socket-util.h"
      29                 :            : #include "string-util.h"
      30                 :            : #include "util.h"
      31                 :            : 
      32                 :            : #define BUFFER_SIZE (256 * 1024)
      33                 :            : 
      34                 :            : static unsigned arg_connections_max = 256;
      35                 :            : static const char *arg_remote_host = NULL;
      36                 :            : 
      37                 :            : typedef struct Context {
      38                 :            :         sd_event *event;
      39                 :            :         sd_resolve *resolve;
      40                 :            : 
      41                 :            :         Set *listen;
      42                 :            :         Set *connections;
      43                 :            : } Context;
      44                 :            : 
      45                 :            : typedef struct Connection {
      46                 :            :         Context *context;
      47                 :            : 
      48                 :            :         int server_fd, client_fd;
      49                 :            :         int server_to_client_buffer[2]; /* a pipe */
      50                 :            :         int client_to_server_buffer[2]; /* a pipe */
      51                 :            : 
      52                 :            :         size_t server_to_client_buffer_full, client_to_server_buffer_full;
      53                 :            :         size_t server_to_client_buffer_size, client_to_server_buffer_size;
      54                 :            : 
      55                 :            :         sd_event_source *server_event_source, *client_event_source;
      56                 :            : 
      57                 :            :         sd_resolve_query *resolve_query;
      58                 :            : } Connection;
      59                 :            : 
      60                 :          0 : static void connection_free(Connection *c) {
      61         [ #  # ]:          0 :         assert(c);
      62                 :            : 
      63         [ #  # ]:          0 :         if (c->context)
      64                 :          0 :                 set_remove(c->context->connections, c);
      65                 :            : 
      66                 :          0 :         sd_event_source_unref(c->server_event_source);
      67                 :          0 :         sd_event_source_unref(c->client_event_source);
      68                 :            : 
      69                 :          0 :         safe_close(c->server_fd);
      70                 :          0 :         safe_close(c->client_fd);
      71                 :            : 
      72                 :          0 :         safe_close_pair(c->server_to_client_buffer);
      73                 :          0 :         safe_close_pair(c->client_to_server_buffer);
      74                 :            : 
      75                 :          0 :         sd_resolve_query_unref(c->resolve_query);
      76                 :            : 
      77                 :          0 :         free(c);
      78                 :          0 : }
      79                 :            : 
      80                 :         16 : static void context_clear(Context *context) {
      81         [ -  + ]:         16 :         assert(context);
      82                 :            : 
      83         [ -  + ]:         16 :         set_free_with_destructor(context->listen, sd_event_source_unref);
      84         [ -  + ]:         16 :         set_free_with_destructor(context->connections, connection_free);
      85                 :            : 
      86                 :         16 :         sd_event_unref(context->event);
      87                 :         16 :         sd_resolve_unref(context->resolve);
      88                 :         16 : }
      89                 :            : 
      90                 :          0 : static int connection_create_pipes(Connection *c, int buffer[static 2], size_t *sz) {
      91                 :            :         int r;
      92                 :            : 
      93         [ #  # ]:          0 :         assert(c);
      94         [ #  # ]:          0 :         assert(buffer);
      95         [ #  # ]:          0 :         assert(sz);
      96                 :            : 
      97         [ #  # ]:          0 :         if (buffer[0] >= 0)
      98                 :          0 :                 return 0;
      99                 :            : 
     100                 :          0 :         r = pipe2(buffer, O_CLOEXEC|O_NONBLOCK);
     101         [ #  # ]:          0 :         if (r < 0)
     102         [ #  # ]:          0 :                 return log_error_errno(errno, "Failed to allocate pipe buffer: %m");
     103                 :            : 
     104                 :          0 :         (void) fcntl(buffer[0], F_SETPIPE_SZ, BUFFER_SIZE);
     105                 :            : 
     106                 :          0 :         r = fcntl(buffer[0], F_GETPIPE_SZ);
     107         [ #  # ]:          0 :         if (r < 0)
     108         [ #  # ]:          0 :                 return log_error_errno(errno, "Failed to get pipe buffer size: %m");
     109                 :            : 
     110         [ #  # ]:          0 :         assert(r > 0);
     111                 :          0 :         *sz = r;
     112                 :            : 
     113                 :          0 :         return 0;
     114                 :            : }
     115                 :            : 
     116                 :          0 : static int connection_shovel(
     117                 :            :                 Connection *c,
     118                 :            :                 int *from, int buffer[2], int *to,
     119                 :            :                 size_t *full, size_t *sz,
     120                 :            :                 sd_event_source **from_source, sd_event_source **to_source) {
     121                 :            : 
     122                 :            :         bool shoveled;
     123                 :            : 
     124         [ #  # ]:          0 :         assert(c);
     125         [ #  # ]:          0 :         assert(from);
     126         [ #  # ]:          0 :         assert(buffer);
     127         [ #  # ]:          0 :         assert(buffer[0] >= 0);
     128         [ #  # ]:          0 :         assert(buffer[1] >= 0);
     129         [ #  # ]:          0 :         assert(to);
     130         [ #  # ]:          0 :         assert(full);
     131         [ #  # ]:          0 :         assert(sz);
     132         [ #  # ]:          0 :         assert(from_source);
     133         [ #  # ]:          0 :         assert(to_source);
     134                 :            : 
     135                 :            :         do {
     136                 :            :                 ssize_t z;
     137                 :            : 
     138                 :          0 :                 shoveled = false;
     139                 :            : 
     140   [ #  #  #  #  :          0 :                 if (*full < *sz && *from >= 0 && *to >= 0) {
                   #  # ]
     141                 :          0 :                         z = splice(*from, NULL, buffer[1], NULL, *sz - *full, SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
     142         [ #  # ]:          0 :                         if (z > 0) {
     143                 :          0 :                                 *full += z;
     144                 :          0 :                                 shoveled = true;
     145   [ #  #  #  # ]:          0 :                         } else if (z == 0 || ERRNO_IS_DISCONNECT(errno)) {
     146                 :          0 :                                 *from_source = sd_event_source_unref(*from_source);
     147                 :          0 :                                 *from = safe_close(*from);
     148   [ #  #  #  # ]:          0 :                         } else if (!IN_SET(errno, EAGAIN, EINTR))
     149         [ #  # ]:          0 :                                 return log_error_errno(errno, "Failed to splice: %m");
     150                 :            :                 }
     151                 :            : 
     152   [ #  #  #  # ]:          0 :                 if (*full > 0 && *to >= 0) {
     153                 :          0 :                         z = splice(buffer[0], NULL, *to, NULL, *full, SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
     154         [ #  # ]:          0 :                         if (z > 0) {
     155                 :          0 :                                 *full -= z;
     156                 :          0 :                                 shoveled = true;
     157   [ #  #  #  # ]:          0 :                         } else if (z == 0 || ERRNO_IS_DISCONNECT(errno)) {
     158                 :          0 :                                 *to_source = sd_event_source_unref(*to_source);
     159                 :          0 :                                 *to = safe_close(*to);
     160   [ #  #  #  # ]:          0 :                         } else if (!IN_SET(errno, EAGAIN, EINTR))
     161         [ #  # ]:          0 :                                 return log_error_errno(errno, "Failed to splice: %m");
     162                 :            :                 }
     163         [ #  # ]:          0 :         } while (shoveled);
     164                 :            : 
     165                 :          0 :         return 0;
     166                 :            : }
     167                 :            : 
     168                 :            : static int connection_enable_event_sources(Connection *c);
     169                 :            : 
     170                 :          0 : static int traffic_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
     171                 :          0 :         Connection *c = userdata;
     172                 :            :         int r;
     173                 :            : 
     174         [ #  # ]:          0 :         assert(s);
     175         [ #  # ]:          0 :         assert(fd >= 0);
     176         [ #  # ]:          0 :         assert(c);
     177                 :            : 
     178                 :          0 :         r = connection_shovel(c,
     179                 :          0 :                               &c->server_fd, c->server_to_client_buffer, &c->client_fd,
     180                 :            :                               &c->server_to_client_buffer_full, &c->server_to_client_buffer_size,
     181                 :            :                               &c->server_event_source, &c->client_event_source);
     182         [ #  # ]:          0 :         if (r < 0)
     183                 :          0 :                 goto quit;
     184                 :            : 
     185                 :          0 :         r = connection_shovel(c,
     186                 :          0 :                               &c->client_fd, c->client_to_server_buffer, &c->server_fd,
     187                 :            :                               &c->client_to_server_buffer_full, &c->client_to_server_buffer_size,
     188                 :            :                               &c->client_event_source, &c->server_event_source);
     189         [ #  # ]:          0 :         if (r < 0)
     190                 :          0 :                 goto quit;
     191                 :            : 
     192                 :            :         /* EOF on both sides? */
     193   [ #  #  #  # ]:          0 :         if (c->server_fd == -1 && c->client_fd == -1)
     194                 :          0 :                 goto quit;
     195                 :            : 
     196                 :            :         /* Server closed, and all data written to client? */
     197   [ #  #  #  # ]:          0 :         if (c->server_fd == -1 && c->server_to_client_buffer_full <= 0)
     198                 :          0 :                 goto quit;
     199                 :            : 
     200                 :            :         /* Client closed, and all data written to server? */
     201   [ #  #  #  # ]:          0 :         if (c->client_fd == -1 && c->client_to_server_buffer_full <= 0)
     202                 :          0 :                 goto quit;
     203                 :            : 
     204                 :          0 :         r = connection_enable_event_sources(c);
     205         [ #  # ]:          0 :         if (r < 0)
     206                 :          0 :                 goto quit;
     207                 :            : 
     208                 :          0 :         return 1;
     209                 :            : 
     210                 :          0 : quit:
     211                 :          0 :         connection_free(c);
     212                 :          0 :         return 0; /* ignore errors, continue serving */
     213                 :            : }
     214                 :            : 
     215                 :          0 : static int connection_enable_event_sources(Connection *c) {
     216                 :          0 :         uint32_t a = 0, b = 0;
     217                 :            :         int r;
     218                 :            : 
     219         [ #  # ]:          0 :         assert(c);
     220                 :            : 
     221         [ #  # ]:          0 :         if (c->server_to_client_buffer_full > 0)
     222                 :          0 :                 b |= EPOLLOUT;
     223         [ #  # ]:          0 :         if (c->server_to_client_buffer_full < c->server_to_client_buffer_size)
     224                 :          0 :                 a |= EPOLLIN;
     225                 :            : 
     226         [ #  # ]:          0 :         if (c->client_to_server_buffer_full > 0)
     227                 :          0 :                 a |= EPOLLOUT;
     228         [ #  # ]:          0 :         if (c->client_to_server_buffer_full < c->client_to_server_buffer_size)
     229                 :          0 :                 b |= EPOLLIN;
     230                 :            : 
     231         [ #  # ]:          0 :         if (c->server_event_source)
     232                 :          0 :                 r = sd_event_source_set_io_events(c->server_event_source, a);
     233         [ #  # ]:          0 :         else if (c->server_fd >= 0)
     234                 :          0 :                 r = sd_event_add_io(c->context->event, &c->server_event_source, c->server_fd, a, traffic_cb, c);
     235                 :            :         else
     236                 :          0 :                 r = 0;
     237                 :            : 
     238         [ #  # ]:          0 :         if (r < 0)
     239         [ #  # ]:          0 :                 return log_error_errno(r, "Failed to set up server event source: %m");
     240                 :            : 
     241         [ #  # ]:          0 :         if (c->client_event_source)
     242                 :          0 :                 r = sd_event_source_set_io_events(c->client_event_source, b);
     243         [ #  # ]:          0 :         else if (c->client_fd >= 0)
     244                 :          0 :                 r = sd_event_add_io(c->context->event, &c->client_event_source, c->client_fd, b, traffic_cb, c);
     245                 :            :         else
     246                 :          0 :                 r = 0;
     247                 :            : 
     248         [ #  # ]:          0 :         if (r < 0)
     249         [ #  # ]:          0 :                 return log_error_errno(r, "Failed to set up client event source: %m");
     250                 :            : 
     251                 :          0 :         return 0;
     252                 :            : }
     253                 :            : 
     254                 :          0 : static int connection_complete(Connection *c) {
     255                 :            :         int r;
     256                 :            : 
     257         [ #  # ]:          0 :         assert(c);
     258                 :            : 
     259                 :          0 :         r = connection_create_pipes(c, c->server_to_client_buffer, &c->server_to_client_buffer_size);
     260         [ #  # ]:          0 :         if (r < 0)
     261                 :          0 :                 goto fail;
     262                 :            : 
     263                 :          0 :         r = connection_create_pipes(c, c->client_to_server_buffer, &c->client_to_server_buffer_size);
     264         [ #  # ]:          0 :         if (r < 0)
     265                 :          0 :                 goto fail;
     266                 :            : 
     267                 :          0 :         r = connection_enable_event_sources(c);
     268         [ #  # ]:          0 :         if (r < 0)
     269                 :          0 :                 goto fail;
     270                 :            : 
     271                 :          0 :         return 0;
     272                 :            : 
     273                 :          0 : fail:
     274                 :          0 :         connection_free(c);
     275                 :          0 :         return 0; /* ignore errors, continue serving */
     276                 :            : }
     277                 :            : 
     278                 :          0 : static int connect_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
     279                 :          0 :         Connection *c = userdata;
     280                 :            :         socklen_t solen;
     281                 :            :         int error, r;
     282                 :            : 
     283         [ #  # ]:          0 :         assert(s);
     284         [ #  # ]:          0 :         assert(fd >= 0);
     285         [ #  # ]:          0 :         assert(c);
     286                 :            : 
     287                 :          0 :         solen = sizeof(error);
     288                 :          0 :         r = getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &solen);
     289         [ #  # ]:          0 :         if (r < 0) {
     290         [ #  # ]:          0 :                 log_error_errno(errno, "Failed to issue SO_ERROR: %m");
     291                 :          0 :                 goto fail;
     292                 :            :         }
     293                 :            : 
     294         [ #  # ]:          0 :         if (error != 0) {
     295         [ #  # ]:          0 :                 log_error_errno(error, "Failed to connect to remote host: %m");
     296                 :          0 :                 goto fail;
     297                 :            :         }
     298                 :            : 
     299                 :          0 :         c->client_event_source = sd_event_source_unref(c->client_event_source);
     300                 :            : 
     301                 :          0 :         return connection_complete(c);
     302                 :            : 
     303                 :          0 : fail:
     304                 :          0 :         connection_free(c);
     305                 :          0 :         return 0; /* ignore errors, continue serving */
     306                 :            : }
     307                 :            : 
     308                 :          0 : static int connection_start(Connection *c, struct sockaddr *sa, socklen_t salen) {
     309                 :            :         int r;
     310                 :            : 
     311         [ #  # ]:          0 :         assert(c);
     312         [ #  # ]:          0 :         assert(sa);
     313         [ #  # ]:          0 :         assert(salen);
     314                 :            : 
     315                 :          0 :         c->client_fd = socket(sa->sa_family, SOCK_STREAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0);
     316         [ #  # ]:          0 :         if (c->client_fd < 0) {
     317         [ #  # ]:          0 :                 log_error_errno(errno, "Failed to get remote socket: %m");
     318                 :          0 :                 goto fail;
     319                 :            :         }
     320                 :            : 
     321                 :          0 :         r = connect(c->client_fd, sa, salen);
     322         [ #  # ]:          0 :         if (r < 0) {
     323         [ #  # ]:          0 :                 if (errno == EINPROGRESS) {
     324                 :          0 :                         r = sd_event_add_io(c->context->event, &c->client_event_source, c->client_fd, EPOLLOUT, connect_cb, c);
     325         [ #  # ]:          0 :                         if (r < 0) {
     326         [ #  # ]:          0 :                                 log_error_errno(r, "Failed to add connection socket: %m");
     327                 :          0 :                                 goto fail;
     328                 :            :                         }
     329                 :            : 
     330                 :          0 :                         r = sd_event_source_set_enabled(c->client_event_source, SD_EVENT_ONESHOT);
     331         [ #  # ]:          0 :                         if (r < 0) {
     332         [ #  # ]:          0 :                                 log_error_errno(r, "Failed to enable oneshot event source: %m");
     333                 :          0 :                                 goto fail;
     334                 :            :                         }
     335                 :            :                 } else {
     336         [ #  # ]:          0 :                         log_error_errno(errno, "Failed to connect to remote host: %m");
     337                 :          0 :                         goto fail;
     338                 :            :                 }
     339                 :            :         } else {
     340                 :          0 :                 r = connection_complete(c);
     341         [ #  # ]:          0 :                 if (r < 0)
     342                 :          0 :                         goto fail;
     343                 :            :         }
     344                 :            : 
     345                 :          0 :         return 0;
     346                 :            : 
     347                 :          0 : fail:
     348                 :          0 :         connection_free(c);
     349                 :          0 :         return 0; /* ignore errors, continue serving */
     350                 :            : }
     351                 :            : 
     352                 :          0 : static int resolve_handler(sd_resolve_query *q, int ret, const struct addrinfo *ai, Connection *c) {
     353         [ #  # ]:          0 :         assert(q);
     354         [ #  # ]:          0 :         assert(c);
     355                 :            : 
     356         [ #  # ]:          0 :         if (ret != 0) {
     357         [ #  # ]:          0 :                 log_error("Failed to resolve host: %s", gai_strerror(ret));
     358                 :          0 :                 goto fail;
     359                 :            :         }
     360                 :            : 
     361                 :          0 :         c->resolve_query = sd_resolve_query_unref(c->resolve_query);
     362                 :            : 
     363                 :          0 :         return connection_start(c, ai->ai_addr, ai->ai_addrlen);
     364                 :            : 
     365                 :          0 : fail:
     366                 :          0 :         connection_free(c);
     367                 :          0 :         return 0; /* ignore errors, continue serving */
     368                 :            : }
     369                 :            : 
     370                 :          0 : static int resolve_remote(Connection *c) {
     371                 :            : 
     372                 :            :         static const struct addrinfo hints = {
     373                 :            :                 .ai_family = AF_UNSPEC,
     374                 :            :                 .ai_socktype = SOCK_STREAM,
     375                 :            :                 .ai_flags = AI_ADDRCONFIG
     376                 :            :         };
     377                 :            : 
     378                 :          0 :         union sockaddr_union sa = {};
     379                 :            :         const char *node, *service;
     380                 :            :         int r;
     381                 :            : 
     382   [ #  #  #  # ]:          0 :         if (IN_SET(arg_remote_host[0], '/', '@')) {
     383                 :            :                 int salen;
     384                 :            : 
     385                 :          0 :                 salen = sockaddr_un_set_path(&sa.un, arg_remote_host);
     386         [ #  # ]:          0 :                 if (salen < 0) {
     387         [ #  # ]:          0 :                         log_error_errno(salen, "Specified address doesn't fit in an AF_UNIX address, refusing: %m");
     388                 :          0 :                         goto fail;
     389                 :            :                 }
     390                 :            : 
     391                 :          0 :                 return connection_start(c, &sa.sa, salen);
     392                 :            :         }
     393                 :            : 
     394                 :          0 :         service = strrchr(arg_remote_host, ':');
     395         [ #  # ]:          0 :         if (service) {
     396                 :          0 :                 node = strndupa(arg_remote_host, service - arg_remote_host);
     397                 :          0 :                 service++;
     398                 :            :         } else {
     399                 :          0 :                 node = arg_remote_host;
     400                 :          0 :                 service = "80";
     401                 :            :         }
     402                 :            : 
     403         [ #  # ]:          0 :         log_debug("Looking up address info for %s:%s", node, service);
     404                 :          0 :         r = resolve_getaddrinfo(c->context->resolve, &c->resolve_query, node, service, &hints, resolve_handler, NULL, c);
     405         [ #  # ]:          0 :         if (r < 0) {
     406         [ #  # ]:          0 :                 log_error_errno(r, "Failed to resolve remote host: %m");
     407                 :          0 :                 goto fail;
     408                 :            :         }
     409                 :            : 
     410                 :          0 :         return 0;
     411                 :            : 
     412                 :          0 : fail:
     413                 :          0 :         connection_free(c);
     414                 :          0 :         return 0; /* ignore errors, continue serving */
     415                 :            : }
     416                 :            : 
     417                 :          0 : static int add_connection_socket(Context *context, int fd) {
     418                 :            :         Connection *c;
     419                 :            :         int r;
     420                 :            : 
     421         [ #  # ]:          0 :         assert(context);
     422         [ #  # ]:          0 :         assert(fd >= 0);
     423                 :            : 
     424         [ #  # ]:          0 :         if (set_size(context->connections) > arg_connections_max) {
     425         [ #  # ]:          0 :                 log_warning("Hit connection limit, refusing connection.");
     426                 :          0 :                 safe_close(fd);
     427                 :          0 :                 return 0;
     428                 :            :         }
     429                 :            : 
     430                 :          0 :         r = set_ensure_allocated(&context->connections, NULL);
     431         [ #  # ]:          0 :         if (r < 0) {
     432                 :          0 :                 log_oom();
     433                 :          0 :                 return 0;
     434                 :            :         }
     435                 :            : 
     436                 :          0 :         c = new0(Connection, 1);
     437         [ #  # ]:          0 :         if (!c) {
     438                 :          0 :                 log_oom();
     439                 :          0 :                 return 0;
     440                 :            :         }
     441                 :            : 
     442                 :          0 :         c->context = context;
     443                 :          0 :         c->server_fd = fd;
     444                 :          0 :         c->client_fd = -1;
     445                 :          0 :         c->server_to_client_buffer[0] = c->server_to_client_buffer[1] = -1;
     446                 :          0 :         c->client_to_server_buffer[0] = c->client_to_server_buffer[1] = -1;
     447                 :            : 
     448                 :          0 :         r = set_put(context->connections, c);
     449         [ #  # ]:          0 :         if (r < 0) {
     450                 :          0 :                 free(c);
     451                 :          0 :                 log_oom();
     452                 :          0 :                 return 0;
     453                 :            :         }
     454                 :            : 
     455                 :          0 :         return resolve_remote(c);
     456                 :            : }
     457                 :            : 
     458                 :          0 : static int accept_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
     459                 :          0 :         _cleanup_free_ char *peer = NULL;
     460                 :          0 :         Context *context = userdata;
     461                 :          0 :         int nfd = -1, r;
     462                 :            : 
     463         [ #  # ]:          0 :         assert(s);
     464         [ #  # ]:          0 :         assert(fd >= 0);
     465         [ #  # ]:          0 :         assert(revents & EPOLLIN);
     466         [ #  # ]:          0 :         assert(context);
     467                 :            : 
     468                 :          0 :         nfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
     469         [ #  # ]:          0 :         if (nfd < 0) {
     470         [ #  # ]:          0 :                 if (!ERRNO_IS_ACCEPT_AGAIN(errno))
     471         [ #  # ]:          0 :                         log_warning_errno(errno, "Failed to accept() socket: %m");
     472                 :            :         } else {
     473                 :          0 :                 (void) getpeername_pretty(nfd, true, &peer);
     474         [ #  # ]:          0 :                 log_debug("New connection from %s", strna(peer));
     475                 :            : 
     476                 :          0 :                 r = add_connection_socket(context, nfd);
     477         [ #  # ]:          0 :                 if (r < 0) {
     478         [ #  # ]:          0 :                         log_error_errno(r, "Failed to accept connection, ignoring: %m");
     479                 :          0 :                         safe_close(fd);
     480                 :            :                 }
     481                 :            :         }
     482                 :            : 
     483                 :          0 :         r = sd_event_source_set_enabled(s, SD_EVENT_ONESHOT);
     484         [ #  # ]:          0 :         if (r < 0) {
     485         [ #  # ]:          0 :                 log_error_errno(r, "Error while re-enabling listener with ONESHOT: %m");
     486                 :          0 :                 sd_event_exit(context->event, r);
     487                 :          0 :                 return r;
     488                 :            :         }
     489                 :            : 
     490                 :          0 :         return 1;
     491                 :            : }
     492                 :            : 
     493                 :          0 : static int add_listen_socket(Context *context, int fd) {
     494                 :            :         sd_event_source *source;
     495                 :            :         int r;
     496                 :            : 
     497         [ #  # ]:          0 :         assert(context);
     498         [ #  # ]:          0 :         assert(fd >= 0);
     499                 :            : 
     500                 :          0 :         r = set_ensure_allocated(&context->listen, NULL);
     501         [ #  # ]:          0 :         if (r < 0) {
     502                 :          0 :                 log_oom();
     503                 :          0 :                 return r;
     504                 :            :         }
     505                 :            : 
     506                 :          0 :         r = sd_is_socket(fd, 0, SOCK_STREAM, 1);
     507         [ #  # ]:          0 :         if (r < 0)
     508         [ #  # ]:          0 :                 return log_error_errno(r, "Failed to determine socket type: %m");
     509         [ #  # ]:          0 :         if (r == 0)
     510         [ #  # ]:          0 :                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
     511                 :            :                                        "Passed in socket is not a stream socket.");
     512                 :            : 
     513                 :          0 :         r = fd_nonblock(fd, true);
     514         [ #  # ]:          0 :         if (r < 0)
     515         [ #  # ]:          0 :                 return log_error_errno(r, "Failed to mark file descriptor non-blocking: %m");
     516                 :            : 
     517                 :          0 :         r = sd_event_add_io(context->event, &source, fd, EPOLLIN, accept_cb, context);
     518         [ #  # ]:          0 :         if (r < 0)
     519         [ #  # ]:          0 :                 return log_error_errno(r, "Failed to add event source: %m");
     520                 :            : 
     521                 :          0 :         r = set_put(context->listen, source);
     522         [ #  # ]:          0 :         if (r < 0) {
     523         [ #  # ]:          0 :                 log_error_errno(r, "Failed to add source to set: %m");
     524                 :          0 :                 sd_event_source_unref(source);
     525                 :          0 :                 return r;
     526                 :            :         }
     527                 :            : 
     528                 :            :         /* Set the watcher to oneshot in case other processes are also
     529                 :            :          * watching to accept(). */
     530                 :          0 :         r = sd_event_source_set_enabled(source, SD_EVENT_ONESHOT);
     531         [ #  # ]:          0 :         if (r < 0)
     532         [ #  # ]:          0 :                 return log_error_errno(r, "Failed to enable oneshot mode: %m");
     533                 :            : 
     534                 :          0 :         return 0;
     535                 :            : }
     536                 :            : 
     537                 :         12 : static int help(void) {
     538                 :         12 :         _cleanup_free_ char *link = NULL;
     539                 :            :         int r;
     540                 :            : 
     541                 :         12 :         r = terminal_urlify_man("systemd-socket-proxyd", "8", &link);
     542         [ -  + ]:         12 :         if (r < 0)
     543                 :          0 :                 return log_oom();
     544                 :            : 
     545                 :         12 :         printf("%1$s [HOST:PORT]\n"
     546                 :            :                "%1$s [SOCKET]\n\n"
     547                 :            :                "Bidirectionally proxy local sockets to another (possibly remote) socket.\n\n"
     548                 :            :                "  -c --connections-max=  Set the maximum number of connections to be accepted\n"
     549                 :            :                "  -h --help              Show this help\n"
     550                 :            :                "     --version           Show package version\n"
     551                 :            :                "\nSee the %2$s for details.\n"
     552                 :            :                , program_invocation_short_name
     553                 :            :                , link
     554                 :            :         );
     555                 :            : 
     556                 :         12 :         return 0;
     557                 :            : }
     558                 :            : 
     559                 :         16 : static int parse_argv(int argc, char *argv[]) {
     560                 :            : 
     561                 :            :         enum {
     562                 :            :                 ARG_VERSION = 0x100,
     563                 :            :                 ARG_IGNORE_ENV
     564                 :            :         };
     565                 :            : 
     566                 :            :         static const struct option options[] = {
     567                 :            :                 { "connections-max", required_argument, NULL, 'c'           },
     568                 :            :                 { "help",            no_argument,       NULL, 'h'           },
     569                 :            :                 { "version",         no_argument,       NULL, ARG_VERSION   },
     570                 :            :                 {}
     571                 :            :         };
     572                 :            : 
     573                 :            :         int c, r;
     574                 :            : 
     575         [ -  + ]:         16 :         assert(argc >= 0);
     576         [ -  + ]:         16 :         assert(argv);
     577                 :            : 
     578         [ +  - ]:         16 :         while ((c = getopt_long(argc, argv, "c:h", options, NULL)) >= 0)
     579                 :            : 
     580   [ +  -  -  +  :         16 :                 switch (c) {
                      - ]
     581                 :            : 
     582                 :         12 :                 case 'h':
     583                 :         12 :                         return help();
     584                 :            : 
     585                 :          0 :                 case ARG_VERSION:
     586                 :          0 :                         return version();
     587                 :            : 
     588                 :          0 :                 case 'c':
     589                 :          0 :                         r = safe_atou(optarg, &arg_connections_max);
     590         [ #  # ]:          0 :                         if (r < 0) {
     591         [ #  # ]:          0 :                                 log_error("Failed to parse --connections-max= argument: %s", optarg);
     592                 :          0 :                                 return r;
     593                 :            :                         }
     594                 :            : 
     595         [ #  # ]:          0 :                         if (arg_connections_max < 1)
     596         [ #  # ]:          0 :                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
     597                 :            :                                                        "Connection limit is too low.");
     598                 :            : 
     599                 :          0 :                         break;
     600                 :            : 
     601                 :          4 :                 case '?':
     602                 :          4 :                         return -EINVAL;
     603                 :            : 
     604                 :          0 :                 default:
     605                 :          0 :                         assert_not_reached("Unhandled option");
     606                 :            :                 }
     607                 :            : 
     608         [ #  # ]:          0 :         if (optind >= argc)
     609         [ #  # ]:          0 :                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
     610                 :            :                                        "Not enough parameters.");
     611                 :            : 
     612         [ #  # ]:          0 :         if (argc != optind+1)
     613         [ #  # ]:          0 :                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
     614                 :            :                                        "Too many parameters.");
     615                 :            : 
     616                 :          0 :         arg_remote_host = argv[optind];
     617                 :          0 :         return 1;
     618                 :            : }
     619                 :            : 
     620                 :         16 : static int run(int argc, char *argv[]) {
     621                 :         16 :         _cleanup_(context_clear) Context context = {};
     622                 :            :         int r, n, fd;
     623                 :            : 
     624                 :         16 :         log_parse_environment();
     625                 :         16 :         log_open();
     626                 :            : 
     627                 :         16 :         r = parse_argv(argc, argv);
     628         [ +  - ]:         16 :         if (r <= 0)
     629                 :         16 :                 return r;
     630                 :            : 
     631                 :          0 :         r = sd_event_default(&context.event);
     632         [ #  # ]:          0 :         if (r < 0)
     633         [ #  # ]:          0 :                 return log_error_errno(r, "Failed to allocate event loop: %m");
     634                 :            : 
     635                 :          0 :         r = sd_resolve_default(&context.resolve);
     636         [ #  # ]:          0 :         if (r < 0)
     637         [ #  # ]:          0 :                 return log_error_errno(r, "Failed to allocate resolver: %m");
     638                 :            : 
     639                 :          0 :         r = sd_resolve_attach_event(context.resolve, context.event, 0);
     640         [ #  # ]:          0 :         if (r < 0)
     641         [ #  # ]:          0 :                 return log_error_errno(r, "Failed to attach resolver: %m");
     642                 :            : 
     643                 :          0 :         sd_event_set_watchdog(context.event, true);
     644                 :            : 
     645                 :          0 :         r = sd_listen_fds(1);
     646         [ #  # ]:          0 :         if (r < 0)
     647         [ #  # ]:          0 :                 return log_error_errno(r, "Failed to receive sockets from parent.");
     648         [ #  # ]:          0 :         if (r == 0)
     649         [ #  # ]:          0 :                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Didn't get any sockets passed in.");
     650                 :            : 
     651                 :          0 :         n = r;
     652                 :            : 
     653         [ #  # ]:          0 :         for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
     654                 :          0 :                 r = add_listen_socket(&context, fd);
     655         [ #  # ]:          0 :                 if (r < 0)
     656                 :          0 :                         return r;
     657                 :            :         }
     658                 :            : 
     659                 :          0 :         r = sd_event_loop(context.event);
     660         [ #  # ]:          0 :         if (r < 0)
     661         [ #  # ]:          0 :                 return log_error_errno(r, "Failed to run event loop: %m");
     662                 :            : 
     663                 :          0 :         return 0;
     664                 :            : }
     665                 :            : 
     666                 :         16 : DEFINE_MAIN_FUNCTION(run);

Generated by: LCOV version 1.14