1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
12 #include "sd-daemon.h"
14 #include "sd-resolve.h"
16 #include "alloc-util.h"
18 #include "daemon-util.h"
19 #include "errno-util.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"
28 #include "socket-util.h"
29 #include "string-util.h"
31 #define BUFFER_SIZE (256 * 1024)
33 static unsigned arg_connections_max
= 256;
34 static const char *arg_remote_host
= NULL
;
35 static usec_t arg_exit_idle_time
= USEC_INFINITY
;
37 typedef struct Context
{
40 sd_event_source
*idle_time
;
46 typedef struct Connection
{
49 int server_fd
, client_fd
;
50 int server_to_client_buffer
[2]; /* a pipe */
51 int client_to_server_buffer
[2]; /* a pipe */
53 size_t server_to_client_buffer_full
, client_to_server_buffer_full
;
54 size_t server_to_client_buffer_size
, client_to_server_buffer_size
;
56 sd_event_source
*server_event_source
, *client_event_source
;
58 sd_resolve_query
*resolve_query
;
61 static void connection_free(Connection
*c
) {
65 set_remove(c
->context
->connections
, c
);
67 sd_event_source_unref(c
->server_event_source
);
68 sd_event_source_unref(c
->client_event_source
);
70 safe_close(c
->server_fd
);
71 safe_close(c
->client_fd
);
73 safe_close_pair(c
->server_to_client_buffer
);
74 safe_close_pair(c
->client_to_server_buffer
);
76 sd_resolve_query_unref(c
->resolve_query
);
81 static int idle_time_cb(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
82 Context
*c
= userdata
;
85 if (!set_isempty(c
->connections
)) {
86 log_warning("Idle timer fired even though there are connections, ignoring");
90 r
= sd_event_exit(c
->event
, 0);
92 log_warning_errno(r
, "Error while stopping event loop, ignoring: %m");
98 static int connection_release(Connection
*c
) {
99 Context
*context
= ASSERT_PTR(ASSERT_PTR(c
)->context
);
104 if (arg_exit_idle_time
< USEC_INFINITY
&& set_isempty(context
->connections
)) {
105 if (context
->idle_time
) {
106 r
= sd_event_source_set_time_relative(context
->idle_time
, arg_exit_idle_time
);
108 return log_error_errno(r
, "Error while setting idle time: %m");
110 r
= sd_event_source_set_enabled(context
->idle_time
, SD_EVENT_ONESHOT
);
112 return log_error_errno(r
, "Error while enabling idle time: %m");
114 r
= sd_event_add_time_relative(
115 context
->event
, &context
->idle_time
, CLOCK_MONOTONIC
,
116 arg_exit_idle_time
, 0, idle_time_cb
, context
);
118 return log_error_errno(r
, "Failed to create idle timer: %m");
125 static void context_clear(Context
*context
) {
128 set_free_with_destructor(context
->listen
, sd_event_source_unref
);
129 set_free_with_destructor(context
->connections
, connection_free
);
131 sd_event_unref(context
->event
);
132 sd_resolve_unref(context
->resolve
);
133 sd_event_source_unref(context
->idle_time
);
136 static int connection_create_pipes(Connection
*c
, int buffer
[static 2], size_t *sz
) {
146 r
= pipe2(buffer
, O_CLOEXEC
|O_NONBLOCK
);
148 return log_error_errno(errno
, "Failed to allocate pipe buffer: %m");
150 (void) fcntl(buffer
[0], F_SETPIPE_SZ
, BUFFER_SIZE
);
152 r
= fcntl(buffer
[0], F_GETPIPE_SZ
);
154 return log_error_errno(errno
, "Failed to get pipe buffer size: %m");
162 static int connection_shovel(
164 int *from
, int buffer
[2], int *to
,
165 size_t *full
, size_t *sz
,
166 sd_event_source
**from_source
, sd_event_source
**to_source
) {
173 assert(buffer
[0] >= 0);
174 assert(buffer
[1] >= 0);
186 if (*full
< *sz
&& *from
>= 0 && *to
>= 0) {
187 z
= splice(*from
, NULL
, buffer
[1], NULL
, *sz
- *full
, SPLICE_F_MOVE
|SPLICE_F_NONBLOCK
);
191 } else if (z
== 0 || ERRNO_IS_DISCONNECT(errno
)) {
192 *from_source
= sd_event_source_unref(*from_source
);
193 *from
= safe_close(*from
);
194 } else if (!ERRNO_IS_TRANSIENT(errno
))
195 return log_error_errno(errno
, "Failed to splice: %m");
198 if (*full
> 0 && *to
>= 0) {
199 z
= splice(buffer
[0], NULL
, *to
, NULL
, *full
, SPLICE_F_MOVE
|SPLICE_F_NONBLOCK
);
203 } else if (z
== 0 || ERRNO_IS_DISCONNECT(errno
)) {
204 *to_source
= sd_event_source_unref(*to_source
);
205 *to
= safe_close(*to
);
206 } else if (!ERRNO_IS_TRANSIENT(errno
))
207 return log_error_errno(errno
, "Failed to splice: %m");
214 static int connection_enable_event_sources(Connection
*c
);
216 static int traffic_cb(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
217 Connection
*c
= ASSERT_PTR(userdata
);
223 r
= connection_shovel(c
,
224 &c
->server_fd
, c
->server_to_client_buffer
, &c
->client_fd
,
225 &c
->server_to_client_buffer_full
, &c
->server_to_client_buffer_size
,
226 &c
->server_event_source
, &c
->client_event_source
);
230 r
= connection_shovel(c
,
231 &c
->client_fd
, c
->client_to_server_buffer
, &c
->server_fd
,
232 &c
->client_to_server_buffer_full
, &c
->client_to_server_buffer_size
,
233 &c
->client_event_source
, &c
->server_event_source
);
237 /* EOF on both sides? */
238 if (c
->server_fd
< 0 && c
->client_fd
< 0)
241 /* Server closed, and all data written to client? */
242 if (c
->server_fd
< 0 && c
->server_to_client_buffer_full
<= 0)
245 /* Client closed, and all data written to server? */
246 if (c
->client_fd
< 0 && c
->client_to_server_buffer_full
<= 0)
249 r
= connection_enable_event_sources(c
);
256 connection_release(c
);
257 return 0; /* ignore errors, continue serving */
260 static int connection_enable_event_sources(Connection
*c
) {
261 uint32_t a
= 0, b
= 0;
266 if (c
->server_to_client_buffer_full
> 0)
268 if (c
->server_to_client_buffer_full
< c
->server_to_client_buffer_size
)
271 if (c
->client_to_server_buffer_full
> 0)
273 if (c
->client_to_server_buffer_full
< c
->client_to_server_buffer_size
)
276 if (c
->server_event_source
)
277 r
= sd_event_source_set_io_events(c
->server_event_source
, a
);
278 else if (c
->server_fd
>= 0)
279 r
= sd_event_add_io(c
->context
->event
, &c
->server_event_source
, c
->server_fd
, a
, traffic_cb
, c
);
284 return log_error_errno(r
, "Failed to set up server event source: %m");
286 if (c
->client_event_source
)
287 r
= sd_event_source_set_io_events(c
->client_event_source
, b
);
288 else if (c
->client_fd
>= 0)
289 r
= sd_event_add_io(c
->context
->event
, &c
->client_event_source
, c
->client_fd
, b
, traffic_cb
, c
);
294 return log_error_errno(r
, "Failed to set up client event source: %m");
299 static int connection_complete(Connection
*c
) {
304 r
= connection_create_pipes(c
, c
->server_to_client_buffer
, &c
->server_to_client_buffer_size
);
308 r
= connection_create_pipes(c
, c
->client_to_server_buffer
, &c
->client_to_server_buffer_size
);
312 r
= connection_enable_event_sources(c
);
319 connection_release(c
);
320 return 0; /* ignore errors, continue serving */
323 static int connect_cb(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
324 Connection
*c
= ASSERT_PTR(userdata
);
331 solen
= sizeof(error
);
332 r
= getsockopt(fd
, SOL_SOCKET
, SO_ERROR
, &error
, &solen
);
334 log_error_errno(errno
, "Failed to issue SO_ERROR: %m");
339 log_error_errno(error
, "Failed to connect to remote host: %m");
343 c
->client_event_source
= sd_event_source_unref(c
->client_event_source
);
345 return connection_complete(c
);
348 connection_release(c
);
349 return 0; /* ignore errors, continue serving */
352 static int connection_start(Connection
*c
, struct sockaddr
*sa
, socklen_t salen
) {
359 c
->client_fd
= socket(sa
->sa_family
, SOCK_STREAM
|SOCK_NONBLOCK
|SOCK_CLOEXEC
, 0);
360 if (c
->client_fd
< 0) {
361 log_error_errno(errno
, "Failed to get remote socket: %m");
365 r
= connect(c
->client_fd
, sa
, salen
);
367 if (errno
== EINPROGRESS
) {
368 r
= sd_event_add_io(c
->context
->event
, &c
->client_event_source
, c
->client_fd
, EPOLLOUT
, connect_cb
, c
);
370 log_error_errno(r
, "Failed to add connection socket: %m");
374 r
= sd_event_source_set_enabled(c
->client_event_source
, SD_EVENT_ONESHOT
);
376 log_error_errno(r
, "Failed to enable oneshot event source: %m");
380 log_error_errno(errno
, "Failed to connect to remote host: %m");
384 r
= connection_complete(c
);
392 connection_release(c
);
393 return 0; /* ignore errors, continue serving */
396 static int resolve_handler(sd_resolve_query
*q
, int ret
, const struct addrinfo
*ai
, Connection
*c
) {
401 log_error("Failed to resolve host: %s", gai_strerror(ret
));
405 c
->resolve_query
= sd_resolve_query_unref(c
->resolve_query
);
407 return connection_start(c
, ai
->ai_addr
, ai
->ai_addrlen
);
410 connection_release(c
);
411 return 0; /* ignore errors, continue serving */
414 static int resolve_remote(Connection
*c
) {
416 static const struct addrinfo hints
= {
417 .ai_family
= AF_UNSPEC
,
418 .ai_socktype
= SOCK_STREAM
,
421 const char *node
, *service
;
424 if (IN_SET(arg_remote_host
[0], '/', '@')) {
425 union sockaddr_union sa
;
428 r
= sockaddr_un_set_path(&sa
.un
, arg_remote_host
);
430 log_error_errno(r
, "Specified address doesn't fit in an AF_UNIX address, refusing: %m");
435 return connection_start(c
, &sa
.sa
, sa_len
);
438 service
= strrchr(arg_remote_host
, ':');
440 node
= strndupa_safe(arg_remote_host
,
441 service
- arg_remote_host
);
444 node
= arg_remote_host
;
448 log_debug("Looking up address info for %s:%s", node
, service
);
449 r
= resolve_getaddrinfo(c
->context
->resolve
, &c
->resolve_query
, node
, service
, &hints
, resolve_handler
, NULL
, c
);
451 log_error_errno(r
, "Failed to resolve remote host: %m");
458 connection_release(c
);
459 return 0; /* ignore errors, continue serving */
462 static int add_connection_socket(Context
*context
, int fd
) {
469 if (set_size(context
->connections
) > arg_connections_max
) {
470 log_warning("Hit connection limit, refusing connection.");
475 if (context
->idle_time
) {
476 r
= sd_event_source_set_enabled(context
->idle_time
, SD_EVENT_OFF
);
478 log_warning_errno(r
, "Unable to disable idle timer, continuing: %m");
481 c
= new(Connection
, 1);
491 .server_to_client_buffer
= EBADF_PAIR
,
492 .client_to_server_buffer
= EBADF_PAIR
,
495 r
= set_ensure_put(&context
->connections
, NULL
, c
);
502 return resolve_remote(c
);
505 static int accept_cb(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
506 _cleanup_free_
char *peer
= NULL
;
507 Context
*context
= ASSERT_PTR(userdata
);
512 assert(revents
& EPOLLIN
);
514 nfd
= accept4(fd
, NULL
, NULL
, SOCK_NONBLOCK
|SOCK_CLOEXEC
);
516 if (!ERRNO_IS_ACCEPT_AGAIN(errno
))
517 log_warning_errno(errno
, "Failed to accept() socket: %m");
519 (void) getpeername_pretty(nfd
, true, &peer
);
520 log_debug("New connection from %s", strna(peer
));
522 r
= add_connection_socket(context
, nfd
);
524 log_warning_errno(r
, "Failed to accept connection, ignoring: %m");
529 r
= sd_event_source_set_enabled(s
, SD_EVENT_ONESHOT
);
531 return log_error_errno(r
, "Error while re-enabling listener with ONESHOT: %m");
536 static int add_listen_socket(Context
*context
, int fd
) {
537 sd_event_source
*source
;
543 r
= sd_is_socket(fd
, 0, SOCK_STREAM
, 1);
545 return log_error_errno(r
, "Failed to determine socket type: %m");
547 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
548 "Passed in socket is not a stream socket.");
550 r
= fd_nonblock(fd
, true);
552 return log_error_errno(r
, "Failed to mark file descriptor non-blocking: %m");
554 r
= sd_event_add_io(context
->event
, &source
, fd
, EPOLLIN
, accept_cb
, context
);
556 return log_error_errno(r
, "Failed to add event source: %m");
558 r
= set_ensure_put(&context
->listen
, NULL
, source
);
560 sd_event_source_unref(source
);
561 return log_error_errno(r
, "Failed to add source to set: %m");
564 r
= sd_event_source_set_exit_on_failure(source
, true);
566 return log_error_errno(r
, "Failed to enable exit-on-failure logic: %m");
568 /* Set the watcher to oneshot in case other processes are also
569 * watching to accept(). */
570 r
= sd_event_source_set_enabled(source
, SD_EVENT_ONESHOT
);
572 return log_error_errno(r
, "Failed to enable oneshot mode: %m");
577 static int help(void) {
578 _cleanup_free_
char *link
= NULL
;
579 _cleanup_free_
char *time_link
= NULL
;
582 r
= terminal_urlify_man("systemd-socket-proxyd", "8", &link
);
585 r
= terminal_urlify_man("systemd.time", "7", &time_link
);
589 printf("%1$s [HOST:PORT]\n"
591 "Bidirectionally proxy local sockets to another (possibly remote) socket.\n\n"
592 " -c --connections-max= Set the maximum number of connections to be accepted\n"
593 " --exit-idle-time= Exit when without a connection for this duration. See\n"
594 " the %3$s for time span format\n"
595 " -h --help Show this help\n"
596 " --version Show package version\n"
597 "\nSee the %2$s for details.\n",
598 program_invocation_short_name
,
605 static int parse_argv(int argc
, char *argv
[]) {
613 static const struct option options
[] = {
614 { "connections-max", required_argument
, NULL
, 'c' },
615 { "exit-idle-time", required_argument
, NULL
, ARG_EXIT_IDLE
},
616 { "help", no_argument
, NULL
, 'h' },
617 { "version", no_argument
, NULL
, ARG_VERSION
},
626 while ((c
= getopt_long(argc
, argv
, "c:h", options
, NULL
)) >= 0)
637 r
= safe_atou(optarg
, &arg_connections_max
);
639 log_error("Failed to parse --connections-max= argument: %s", optarg
);
643 if (arg_connections_max
< 1)
644 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
645 "Connection limit is too low.");
650 r
= parse_sec(optarg
, &arg_exit_idle_time
);
652 return log_error_errno(r
, "Failed to parse --exit-idle-time= argument: %s", optarg
);
659 assert_not_reached();
663 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
664 "Not enough parameters.");
666 if (argc
!= optind
+1)
667 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
668 "Too many parameters.");
670 arg_remote_host
= argv
[optind
];
674 static int run(int argc
, char *argv
[]) {
675 _cleanup_(context_clear
) Context context
= {};
676 _unused_
_cleanup_(notify_on_cleanup
) const char *notify_stop
= NULL
;
679 log_parse_environment();
682 r
= parse_argv(argc
, argv
);
686 r
= sd_event_default(&context
.event
);
688 return log_error_errno(r
, "Failed to allocate event loop: %m");
690 r
= sd_resolve_default(&context
.resolve
);
692 return log_error_errno(r
, "Failed to allocate resolver: %m");
694 r
= sd_resolve_attach_event(context
.resolve
, context
.event
, 0);
696 return log_error_errno(r
, "Failed to attach resolver: %m");
698 sd_event_set_watchdog(context
.event
, true);
700 r
= sd_listen_fds(1);
702 return log_error_errno(r
, "Failed to receive sockets from parent.");
704 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Didn't get any sockets passed in.");
708 for (fd
= SD_LISTEN_FDS_START
; fd
< SD_LISTEN_FDS_START
+ n
; fd
++) {
709 r
= add_listen_socket(&context
, fd
);
714 notify_stop
= notify_start(NOTIFY_READY
, NOTIFY_STOPPING
);
715 r
= sd_event_loop(context
.event
);
717 return log_error_errno(r
, "Failed to run event loop: %m");
722 DEFINE_MAIN_FUNCTION(run
);