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
) {
100 while ((es
= set_steal_first(context
->listen
)))
101 sd_event_source_unref(es
);
103 while ((c
= set_first(context
->connections
)))
106 set_free(context
->listen
);
107 set_free(context
->connections
);
109 sd_event_unref(context
->event
);
110 sd_resolve_unref(context
->resolve
);
113 static int connection_create_pipes(Connection
*c
, int buffer
[2], size_t *sz
) {
123 r
= pipe2(buffer
, O_CLOEXEC
|O_NONBLOCK
);
125 return log_error_errno(errno
, "Failed to allocate pipe buffer: %m");
127 (void) fcntl(buffer
[0], F_SETPIPE_SZ
, BUFFER_SIZE
);
129 r
= fcntl(buffer
[0], F_GETPIPE_SZ
);
131 return log_error_errno(errno
, "Failed to get pipe buffer size: %m");
139 static int connection_shovel(
141 int *from
, int buffer
[2], int *to
,
142 size_t *full
, size_t *sz
,
143 sd_event_source
**from_source
, sd_event_source
**to_source
) {
150 assert(buffer
[0] >= 0);
151 assert(buffer
[1] >= 0);
163 if (*full
< *sz
&& *from
>= 0 && *to
>= 0) {
164 z
= splice(*from
, NULL
, buffer
[1], NULL
, *sz
- *full
, SPLICE_F_MOVE
|SPLICE_F_NONBLOCK
);
168 } else if (z
== 0 || IN_SET(errno
, EPIPE
, ECONNRESET
)) {
169 *from_source
= sd_event_source_unref(*from_source
);
170 *from
= safe_close(*from
);
171 } else if (!IN_SET(errno
, EAGAIN
, EINTR
))
172 return log_error_errno(errno
, "Failed to splice: %m");
175 if (*full
> 0 && *to
>= 0) {
176 z
= splice(buffer
[0], NULL
, *to
, NULL
, *full
, SPLICE_F_MOVE
|SPLICE_F_NONBLOCK
);
180 } else if (z
== 0 || IN_SET(errno
, EPIPE
, ECONNRESET
)) {
181 *to_source
= sd_event_source_unref(*to_source
);
182 *to
= safe_close(*to
);
183 } else if (!IN_SET(errno
, EAGAIN
, EINTR
))
184 return log_error_errno(errno
, "Failed to splice: %m");
191 static int connection_enable_event_sources(Connection
*c
);
193 static int traffic_cb(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
194 Connection
*c
= userdata
;
201 r
= connection_shovel(c
,
202 &c
->server_fd
, c
->server_to_client_buffer
, &c
->client_fd
,
203 &c
->server_to_client_buffer_full
, &c
->server_to_client_buffer_size
,
204 &c
->server_event_source
, &c
->client_event_source
);
208 r
= connection_shovel(c
,
209 &c
->client_fd
, c
->client_to_server_buffer
, &c
->server_fd
,
210 &c
->client_to_server_buffer_full
, &c
->client_to_server_buffer_size
,
211 &c
->client_event_source
, &c
->server_event_source
);
215 /* EOF on both sides? */
216 if (c
->server_fd
== -1 && c
->client_fd
== -1)
219 /* Server closed, and all data written to client? */
220 if (c
->server_fd
== -1 && c
->server_to_client_buffer_full
<= 0)
223 /* Client closed, and all data written to server? */
224 if (c
->client_fd
== -1 && c
->client_to_server_buffer_full
<= 0)
227 r
= connection_enable_event_sources(c
);
235 return 0; /* ignore errors, continue serving */
238 static int connection_enable_event_sources(Connection
*c
) {
239 uint32_t a
= 0, b
= 0;
244 if (c
->server_to_client_buffer_full
> 0)
246 if (c
->server_to_client_buffer_full
< c
->server_to_client_buffer_size
)
249 if (c
->client_to_server_buffer_full
> 0)
251 if (c
->client_to_server_buffer_full
< c
->client_to_server_buffer_size
)
254 if (c
->server_event_source
)
255 r
= sd_event_source_set_io_events(c
->server_event_source
, a
);
256 else if (c
->server_fd
>= 0)
257 r
= sd_event_add_io(c
->context
->event
, &c
->server_event_source
, c
->server_fd
, a
, traffic_cb
, c
);
262 return log_error_errno(r
, "Failed to set up server event source: %m");
264 if (c
->client_event_source
)
265 r
= sd_event_source_set_io_events(c
->client_event_source
, b
);
266 else if (c
->client_fd
>= 0)
267 r
= sd_event_add_io(c
->context
->event
, &c
->client_event_source
, c
->client_fd
, b
, traffic_cb
, c
);
272 return log_error_errno(r
, "Failed to set up client event source: %m");
277 static int connection_complete(Connection
*c
) {
282 r
= connection_create_pipes(c
, c
->server_to_client_buffer
, &c
->server_to_client_buffer_size
);
286 r
= connection_create_pipes(c
, c
->client_to_server_buffer
, &c
->client_to_server_buffer_size
);
290 r
= connection_enable_event_sources(c
);
298 return 0; /* ignore errors, continue serving */
301 static int connect_cb(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
302 Connection
*c
= userdata
;
310 solen
= sizeof(error
);
311 r
= getsockopt(fd
, SOL_SOCKET
, SO_ERROR
, &error
, &solen
);
313 log_error_errno(errno
, "Failed to issue SO_ERROR: %m");
318 log_error_errno(error
, "Failed to connect to remote host: %m");
322 c
->client_event_source
= sd_event_source_unref(c
->client_event_source
);
324 return connection_complete(c
);
328 return 0; /* ignore errors, continue serving */
331 static int connection_start(Connection
*c
, struct sockaddr
*sa
, socklen_t salen
) {
338 c
->client_fd
= socket(sa
->sa_family
, SOCK_STREAM
|SOCK_NONBLOCK
|SOCK_CLOEXEC
, 0);
339 if (c
->client_fd
< 0) {
340 log_error_errno(errno
, "Failed to get remote socket: %m");
344 r
= connect(c
->client_fd
, sa
, salen
);
346 if (errno
== EINPROGRESS
) {
347 r
= sd_event_add_io(c
->context
->event
, &c
->client_event_source
, c
->client_fd
, EPOLLOUT
, connect_cb
, c
);
349 log_error_errno(r
, "Failed to add connection socket: %m");
353 r
= sd_event_source_set_enabled(c
->client_event_source
, SD_EVENT_ONESHOT
);
355 log_error_errno(r
, "Failed to enable oneshot event source: %m");
359 log_error_errno(errno
, "Failed to connect to remote host: %m");
363 r
= connection_complete(c
);
372 return 0; /* ignore errors, continue serving */
375 static int resolve_cb(sd_resolve_query
*q
, int ret
, const struct addrinfo
*ai
, void *userdata
) {
376 Connection
*c
= userdata
;
382 log_error("Failed to resolve host: %s", gai_strerror(ret
));
386 c
->resolve_query
= sd_resolve_query_unref(c
->resolve_query
);
388 return connection_start(c
, ai
->ai_addr
, ai
->ai_addrlen
);
392 return 0; /* ignore errors, continue serving */
395 static int resolve_remote(Connection
*c
) {
397 static const struct addrinfo hints
= {
398 .ai_family
= AF_UNSPEC
,
399 .ai_socktype
= SOCK_STREAM
,
400 .ai_flags
= AI_ADDRCONFIG
403 union sockaddr_union sa
= {};
404 const char *node
, *service
;
407 if (path_is_absolute(arg_remote_host
)) {
408 sa
.un
.sun_family
= AF_UNIX
;
409 strncpy(sa
.un
.sun_path
, arg_remote_host
, sizeof(sa
.un
.sun_path
));
410 return connection_start(c
, &sa
.sa
, SOCKADDR_UN_LEN(sa
.un
));
413 if (arg_remote_host
[0] == '@') {
414 sa
.un
.sun_family
= AF_UNIX
;
415 sa
.un
.sun_path
[0] = 0;
416 strncpy(sa
.un
.sun_path
+1, arg_remote_host
+1, sizeof(sa
.un
.sun_path
)-1);
417 return connection_start(c
, &sa
.sa
, SOCKADDR_UN_LEN(sa
.un
));
420 service
= strrchr(arg_remote_host
, ':');
422 node
= strndupa(arg_remote_host
, service
- arg_remote_host
);
425 node
= arg_remote_host
;
429 log_debug("Looking up address info for %s:%s", node
, service
);
430 r
= sd_resolve_getaddrinfo(c
->context
->resolve
, &c
->resolve_query
, node
, service
, &hints
, resolve_cb
, c
);
432 log_error_errno(r
, "Failed to resolve remote host: %m");
440 return 0; /* ignore errors, continue serving */
443 static int add_connection_socket(Context
*context
, int fd
) {
450 if (set_size(context
->connections
) > arg_connections_max
) {
451 log_warning("Hit connection limit, refusing connection.");
456 r
= set_ensure_allocated(&context
->connections
, NULL
);
462 c
= new0(Connection
, 1);
468 c
->context
= context
;
471 c
->server_to_client_buffer
[0] = c
->server_to_client_buffer
[1] = -1;
472 c
->client_to_server_buffer
[0] = c
->client_to_server_buffer
[1] = -1;
474 r
= set_put(context
->connections
, c
);
481 return resolve_remote(c
);
484 static int accept_cb(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
485 _cleanup_free_
char *peer
= NULL
;
486 Context
*context
= userdata
;
491 assert(revents
& EPOLLIN
);
494 nfd
= accept4(fd
, NULL
, NULL
, SOCK_NONBLOCK
|SOCK_CLOEXEC
);
496 if (errno
!= -EAGAIN
)
497 log_warning_errno(errno
, "Failed to accept() socket: %m");
499 getpeername_pretty(nfd
, true, &peer
);
500 log_debug("New connection from %s", strna(peer
));
502 r
= add_connection_socket(context
, nfd
);
504 log_error_errno(r
, "Failed to accept connection, ignoring: %m");
509 r
= sd_event_source_set_enabled(s
, SD_EVENT_ONESHOT
);
511 log_error_errno(r
, "Error while re-enabling listener with ONESHOT: %m");
512 sd_event_exit(context
->event
, r
);
519 static int add_listen_socket(Context
*context
, int fd
) {
520 sd_event_source
*source
;
526 r
= set_ensure_allocated(&context
->listen
, NULL
);
532 r
= sd_is_socket(fd
, 0, SOCK_STREAM
, 1);
534 return log_error_errno(r
, "Failed to determine socket type: %m");
536 log_error("Passed in socket is not a stream socket.");
540 r
= fd_nonblock(fd
, true);
542 return log_error_errno(r
, "Failed to mark file descriptor non-blocking: %m");
544 r
= sd_event_add_io(context
->event
, &source
, fd
, EPOLLIN
, accept_cb
, context
);
546 return log_error_errno(r
, "Failed to add event source: %m");
548 r
= set_put(context
->listen
, source
);
550 log_error_errno(r
, "Failed to add source to set: %m");
551 sd_event_source_unref(source
);
555 /* Set the watcher to oneshot in case other processes are also
556 * watching to accept(). */
557 r
= sd_event_source_set_enabled(source
, SD_EVENT_ONESHOT
);
559 return log_error_errno(r
, "Failed to enable oneshot mode: %m");
564 static void help(void) {
565 printf("%1$s [HOST:PORT]\n"
567 "Bidirectionally proxy local sockets to another (possibly remote) socket.\n\n"
568 " -c --connections-max= Set the maximum number of connections to be accepted\n"
569 " -h --help Show this help\n"
570 " --version Show package version\n",
571 program_invocation_short_name
);
574 static int parse_argv(int argc
, char *argv
[]) {
581 static const struct option options
[] = {
582 { "connections-max", required_argument
, NULL
, 'c' },
583 { "help", no_argument
, NULL
, 'h' },
584 { "version", no_argument
, NULL
, ARG_VERSION
},
593 while ((c
= getopt_long(argc
, argv
, "c:h", options
, NULL
)) >= 0)
602 r
= safe_atou(optarg
, &arg_connections_max
);
604 log_error("Failed to parse --connections-max= argument: %s", optarg
);
608 if (arg_connections_max
< 1) {
609 log_error("Connection limit is too low.");
622 assert_not_reached("Unhandled option");
625 if (optind
>= argc
) {
626 log_error("Not enough parameters.");
630 if (argc
!= optind
+1) {
631 log_error("Too many parameters.");
635 arg_remote_host
= argv
[optind
];
639 int main(int argc
, char *argv
[]) {
640 Context context
= {};
643 log_parse_environment();
646 r
= parse_argv(argc
, argv
);
650 r
= sd_event_default(&context
.event
);
652 log_error_errno(r
, "Failed to allocate event loop: %m");
656 r
= sd_resolve_default(&context
.resolve
);
658 log_error_errno(r
, "Failed to allocate resolver: %m");
662 r
= sd_resolve_attach_event(context
.resolve
, context
.event
, 0);
664 log_error_errno(r
, "Failed to attach resolver: %m");
668 sd_event_set_watchdog(context
.event
, true);
670 n
= sd_listen_fds(1);
672 log_error("Failed to receive sockets from parent.");
676 log_error("Didn't get any sockets passed in.");
681 for (fd
= SD_LISTEN_FDS_START
; fd
< SD_LISTEN_FDS_START
+ n
; fd
++) {
682 r
= add_listen_socket(&context
, fd
);
687 r
= sd_event_loop(context
.event
);
689 log_error_errno(r
, "Failed to run event loop: %m");
694 context_free(&context
);
696 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;