1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2013 David Strauss
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
28 #include <sys/socket.h>
32 #include "sd-daemon.h"
34 #include "sd-resolve.h"
36 #include "alloc-util.h"
39 #include "path-util.h"
41 #include "socket-util.h"
42 #include "string-util.h"
43 #include "parse-util.h"
46 #define BUFFER_SIZE (256 * 1024)
47 static unsigned arg_connections_max
= 256;
49 static const char *arg_remote_host
= NULL
;
51 typedef struct Context
{
59 typedef struct Connection
{
62 int server_fd
, client_fd
;
63 int server_to_client_buffer
[2]; /* a pipe */
64 int client_to_server_buffer
[2]; /* a pipe */
66 size_t server_to_client_buffer_full
, client_to_server_buffer_full
;
67 size_t server_to_client_buffer_size
, client_to_server_buffer_size
;
69 sd_event_source
*server_event_source
, *client_event_source
;
71 sd_resolve_query
*resolve_query
;
74 static void connection_free(Connection
*c
) {
78 set_remove(c
->context
->connections
, c
);
80 sd_event_source_unref(c
->server_event_source
);
81 sd_event_source_unref(c
->client_event_source
);
83 safe_close(c
->server_fd
);
84 safe_close(c
->client_fd
);
86 safe_close_pair(c
->server_to_client_buffer
);
87 safe_close_pair(c
->client_to_server_buffer
);
89 sd_resolve_query_unref(c
->resolve_query
);
94 static void context_free(Context
*context
) {
97 set_free_with_destructor(context
->listen
, sd_event_source_unref
);
98 set_free_with_destructor(context
->connections
, connection_free
);
100 sd_event_unref(context
->event
);
101 sd_resolve_unref(context
->resolve
);
104 static int connection_create_pipes(Connection
*c
, int buffer
[2], size_t *sz
) {
114 r
= pipe2(buffer
, O_CLOEXEC
|O_NONBLOCK
);
116 return log_error_errno(errno
, "Failed to allocate pipe buffer: %m");
118 (void) fcntl(buffer
[0], F_SETPIPE_SZ
, BUFFER_SIZE
);
120 r
= fcntl(buffer
[0], F_GETPIPE_SZ
);
122 return log_error_errno(errno
, "Failed to get pipe buffer size: %m");
130 static int connection_shovel(
132 int *from
, int buffer
[2], int *to
,
133 size_t *full
, size_t *sz
,
134 sd_event_source
**from_source
, sd_event_source
**to_source
) {
141 assert(buffer
[0] >= 0);
142 assert(buffer
[1] >= 0);
154 if (*full
< *sz
&& *from
>= 0 && *to
>= 0) {
155 z
= splice(*from
, NULL
, buffer
[1], NULL
, *sz
- *full
, SPLICE_F_MOVE
|SPLICE_F_NONBLOCK
);
159 } else if (z
== 0 || IN_SET(errno
, EPIPE
, ECONNRESET
)) {
160 *from_source
= sd_event_source_unref(*from_source
);
161 *from
= safe_close(*from
);
162 } else if (!IN_SET(errno
, EAGAIN
, EINTR
))
163 return log_error_errno(errno
, "Failed to splice: %m");
166 if (*full
> 0 && *to
>= 0) {
167 z
= splice(buffer
[0], NULL
, *to
, NULL
, *full
, SPLICE_F_MOVE
|SPLICE_F_NONBLOCK
);
171 } else if (z
== 0 || IN_SET(errno
, EPIPE
, ECONNRESET
)) {
172 *to_source
= sd_event_source_unref(*to_source
);
173 *to
= safe_close(*to
);
174 } else if (!IN_SET(errno
, EAGAIN
, EINTR
))
175 return log_error_errno(errno
, "Failed to splice: %m");
182 static int connection_enable_event_sources(Connection
*c
);
184 static int traffic_cb(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
185 Connection
*c
= userdata
;
192 r
= connection_shovel(c
,
193 &c
->server_fd
, c
->server_to_client_buffer
, &c
->client_fd
,
194 &c
->server_to_client_buffer_full
, &c
->server_to_client_buffer_size
,
195 &c
->server_event_source
, &c
->client_event_source
);
199 r
= connection_shovel(c
,
200 &c
->client_fd
, c
->client_to_server_buffer
, &c
->server_fd
,
201 &c
->client_to_server_buffer_full
, &c
->client_to_server_buffer_size
,
202 &c
->client_event_source
, &c
->server_event_source
);
206 /* EOF on both sides? */
207 if (c
->server_fd
== -1 && c
->client_fd
== -1)
210 /* Server closed, and all data written to client? */
211 if (c
->server_fd
== -1 && c
->server_to_client_buffer_full
<= 0)
214 /* Client closed, and all data written to server? */
215 if (c
->client_fd
== -1 && c
->client_to_server_buffer_full
<= 0)
218 r
= connection_enable_event_sources(c
);
226 return 0; /* ignore errors, continue serving */
229 static int connection_enable_event_sources(Connection
*c
) {
230 uint32_t a
= 0, b
= 0;
235 if (c
->server_to_client_buffer_full
> 0)
237 if (c
->server_to_client_buffer_full
< c
->server_to_client_buffer_size
)
240 if (c
->client_to_server_buffer_full
> 0)
242 if (c
->client_to_server_buffer_full
< c
->client_to_server_buffer_size
)
245 if (c
->server_event_source
)
246 r
= sd_event_source_set_io_events(c
->server_event_source
, a
);
247 else if (c
->server_fd
>= 0)
248 r
= sd_event_add_io(c
->context
->event
, &c
->server_event_source
, c
->server_fd
, a
, traffic_cb
, c
);
253 return log_error_errno(r
, "Failed to set up server event source: %m");
255 if (c
->client_event_source
)
256 r
= sd_event_source_set_io_events(c
->client_event_source
, b
);
257 else if (c
->client_fd
>= 0)
258 r
= sd_event_add_io(c
->context
->event
, &c
->client_event_source
, c
->client_fd
, b
, traffic_cb
, c
);
263 return log_error_errno(r
, "Failed to set up client event source: %m");
268 static int connection_complete(Connection
*c
) {
273 r
= connection_create_pipes(c
, c
->server_to_client_buffer
, &c
->server_to_client_buffer_size
);
277 r
= connection_create_pipes(c
, c
->client_to_server_buffer
, &c
->client_to_server_buffer_size
);
281 r
= connection_enable_event_sources(c
);
289 return 0; /* ignore errors, continue serving */
292 static int connect_cb(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
293 Connection
*c
= userdata
;
301 solen
= sizeof(error
);
302 r
= getsockopt(fd
, SOL_SOCKET
, SO_ERROR
, &error
, &solen
);
304 log_error_errno(errno
, "Failed to issue SO_ERROR: %m");
309 log_error_errno(error
, "Failed to connect to remote host: %m");
313 c
->client_event_source
= sd_event_source_unref(c
->client_event_source
);
315 return connection_complete(c
);
319 return 0; /* ignore errors, continue serving */
322 static int connection_start(Connection
*c
, struct sockaddr
*sa
, socklen_t salen
) {
329 c
->client_fd
= socket(sa
->sa_family
, SOCK_STREAM
|SOCK_NONBLOCK
|SOCK_CLOEXEC
, 0);
330 if (c
->client_fd
< 0) {
331 log_error_errno(errno
, "Failed to get remote socket: %m");
335 r
= connect(c
->client_fd
, sa
, salen
);
337 if (errno
== EINPROGRESS
) {
338 r
= sd_event_add_io(c
->context
->event
, &c
->client_event_source
, c
->client_fd
, EPOLLOUT
, connect_cb
, c
);
340 log_error_errno(r
, "Failed to add connection socket: %m");
344 r
= sd_event_source_set_enabled(c
->client_event_source
, SD_EVENT_ONESHOT
);
346 log_error_errno(r
, "Failed to enable oneshot event source: %m");
350 log_error_errno(errno
, "Failed to connect to remote host: %m");
354 r
= connection_complete(c
);
363 return 0; /* ignore errors, continue serving */
366 static int resolve_cb(sd_resolve_query
*q
, int ret
, const struct addrinfo
*ai
, void *userdata
) {
367 Connection
*c
= userdata
;
373 log_error("Failed to resolve host: %s", gai_strerror(ret
));
377 c
->resolve_query
= sd_resolve_query_unref(c
->resolve_query
);
379 return connection_start(c
, ai
->ai_addr
, ai
->ai_addrlen
);
383 return 0; /* ignore errors, continue serving */
386 static int resolve_remote(Connection
*c
) {
388 static const struct addrinfo hints
= {
389 .ai_family
= AF_UNSPEC
,
390 .ai_socktype
= SOCK_STREAM
,
391 .ai_flags
= AI_ADDRCONFIG
394 union sockaddr_union sa
= {};
395 const char *node
, *service
;
398 if (path_is_absolute(arg_remote_host
)) {
399 sa
.un
.sun_family
= AF_UNIX
;
400 strncpy(sa
.un
.sun_path
, arg_remote_host
, sizeof(sa
.un
.sun_path
));
401 return connection_start(c
, &sa
.sa
, SOCKADDR_UN_LEN(sa
.un
));
404 if (arg_remote_host
[0] == '@') {
405 sa
.un
.sun_family
= AF_UNIX
;
406 sa
.un
.sun_path
[0] = 0;
407 strncpy(sa
.un
.sun_path
+1, arg_remote_host
+1, sizeof(sa
.un
.sun_path
)-1);
408 return connection_start(c
, &sa
.sa
, SOCKADDR_UN_LEN(sa
.un
));
411 service
= strrchr(arg_remote_host
, ':');
413 node
= strndupa(arg_remote_host
, service
- arg_remote_host
);
416 node
= arg_remote_host
;
420 log_debug("Looking up address info for %s:%s", node
, service
);
421 r
= sd_resolve_getaddrinfo(c
->context
->resolve
, &c
->resolve_query
, node
, service
, &hints
, resolve_cb
, c
);
423 log_error_errno(r
, "Failed to resolve remote host: %m");
431 return 0; /* ignore errors, continue serving */
434 static int add_connection_socket(Context
*context
, int fd
) {
441 if (set_size(context
->connections
) > arg_connections_max
) {
442 log_warning("Hit connection limit, refusing connection.");
447 r
= set_ensure_allocated(&context
->connections
, NULL
);
453 c
= new0(Connection
, 1);
459 c
->context
= context
;
462 c
->server_to_client_buffer
[0] = c
->server_to_client_buffer
[1] = -1;
463 c
->client_to_server_buffer
[0] = c
->client_to_server_buffer
[1] = -1;
465 r
= set_put(context
->connections
, c
);
472 return resolve_remote(c
);
475 static int accept_cb(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
476 _cleanup_free_
char *peer
= NULL
;
477 Context
*context
= userdata
;
482 assert(revents
& EPOLLIN
);
485 nfd
= accept4(fd
, NULL
, NULL
, SOCK_NONBLOCK
|SOCK_CLOEXEC
);
487 if (errno
!= -EAGAIN
)
488 log_warning_errno(errno
, "Failed to accept() socket: %m");
490 getpeername_pretty(nfd
, true, &peer
);
491 log_debug("New connection from %s", strna(peer
));
493 r
= add_connection_socket(context
, nfd
);
495 log_error_errno(r
, "Failed to accept connection, ignoring: %m");
500 r
= sd_event_source_set_enabled(s
, SD_EVENT_ONESHOT
);
502 log_error_errno(r
, "Error while re-enabling listener with ONESHOT: %m");
503 sd_event_exit(context
->event
, r
);
510 static int add_listen_socket(Context
*context
, int fd
) {
511 sd_event_source
*source
;
517 r
= set_ensure_allocated(&context
->listen
, NULL
);
523 r
= sd_is_socket(fd
, 0, SOCK_STREAM
, 1);
525 return log_error_errno(r
, "Failed to determine socket type: %m");
527 log_error("Passed in socket is not a stream socket.");
531 r
= fd_nonblock(fd
, true);
533 return log_error_errno(r
, "Failed to mark file descriptor non-blocking: %m");
535 r
= sd_event_add_io(context
->event
, &source
, fd
, EPOLLIN
, accept_cb
, context
);
537 return log_error_errno(r
, "Failed to add event source: %m");
539 r
= set_put(context
->listen
, source
);
541 log_error_errno(r
, "Failed to add source to set: %m");
542 sd_event_source_unref(source
);
546 /* Set the watcher to oneshot in case other processes are also
547 * watching to accept(). */
548 r
= sd_event_source_set_enabled(source
, SD_EVENT_ONESHOT
);
550 return log_error_errno(r
, "Failed to enable oneshot mode: %m");
555 static void help(void) {
556 printf("%1$s [HOST:PORT]\n"
558 "Bidirectionally proxy local sockets to another (possibly remote) socket.\n\n"
559 " -c --connections-max= Set the maximum number of connections to be accepted\n"
560 " -h --help Show this help\n"
561 " --version Show package version\n",
562 program_invocation_short_name
);
565 static int parse_argv(int argc
, char *argv
[]) {
572 static const struct option options
[] = {
573 { "connections-max", required_argument
, NULL
, 'c' },
574 { "help", no_argument
, NULL
, 'h' },
575 { "version", no_argument
, NULL
, ARG_VERSION
},
584 while ((c
= getopt_long(argc
, argv
, "c:h", options
, NULL
)) >= 0)
593 r
= safe_atou(optarg
, &arg_connections_max
);
595 log_error("Failed to parse --connections-max= argument: %s", optarg
);
599 if (arg_connections_max
< 1) {
600 log_error("Connection limit is too low.");
613 assert_not_reached("Unhandled option");
616 if (optind
>= argc
) {
617 log_error("Not enough parameters.");
621 if (argc
!= optind
+1) {
622 log_error("Too many parameters.");
626 arg_remote_host
= argv
[optind
];
630 int main(int argc
, char *argv
[]) {
631 Context context
= {};
634 log_parse_environment();
637 r
= parse_argv(argc
, argv
);
641 r
= sd_event_default(&context
.event
);
643 log_error_errno(r
, "Failed to allocate event loop: %m");
647 r
= sd_resolve_default(&context
.resolve
);
649 log_error_errno(r
, "Failed to allocate resolver: %m");
653 r
= sd_resolve_attach_event(context
.resolve
, context
.event
, 0);
655 log_error_errno(r
, "Failed to attach resolver: %m");
659 sd_event_set_watchdog(context
.event
, true);
661 n
= sd_listen_fds(1);
663 log_error("Failed to receive sockets from parent.");
667 log_error("Didn't get any sockets passed in.");
672 for (fd
= SD_LISTEN_FDS_START
; fd
< SD_LISTEN_FDS_START
+ n
; fd
++) {
673 r
= add_listen_socket(&context
, fd
);
678 r
= sd_event_loop(context
.event
);
680 log_error_errno(r
, "Failed to run event loop: %m");
685 context_free(&context
);
687 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;