1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2013 David Strauss
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include <arpa/inet.h>
29 #include <sys/fcntl.h>
30 #include <sys/socket.h>
34 #include "sd-daemon.h"
37 #include "socket-util.h"
39 #include "event-util.h"
42 #include "path-util.h"
44 #define BUFFER_SIZE (256 * 1024)
45 #define CONNECTIONS_MAX 256
47 #define _cleanup_freeaddrinfo_ _cleanup_(freeaddrinfop)
48 DEFINE_TRIVIAL_CLEANUP_FUNC(struct addrinfo
*, freeaddrinfo
);
50 typedef struct Context
{
55 typedef struct Connection
{
58 int server_fd
, client_fd
;
59 int server_to_client_buffer
[2]; /* a pipe */
60 int client_to_server_buffer
[2]; /* a pipe */
62 size_t server_to_client_buffer_full
, client_to_server_buffer_full
;
63 size_t server_to_client_buffer_size
, client_to_server_buffer_size
;
65 sd_event_source
*server_event_source
, *client_event_source
;
68 static const char *arg_remote_host
= NULL
;
70 static void connection_free(Connection
*c
) {
74 set_remove(c
->context
->connections
, c
);
76 sd_event_source_unref(c
->server_event_source
);
77 sd_event_source_unref(c
->client_event_source
);
79 if (c
->server_fd
>= 0)
80 close_nointr_nofail(c
->server_fd
);
81 if (c
->client_fd
>= 0)
82 close_nointr_nofail(c
->client_fd
);
84 close_pipe(c
->server_to_client_buffer
);
85 close_pipe(c
->client_to_server_buffer
);
90 static void context_free(Context
*context
) {
96 while ((es
= set_steal_first(context
->listen
)))
97 sd_event_source_unref(es
);
99 while ((c
= set_first(context
->connections
)))
102 set_free(context
->listen
);
103 set_free(context
->connections
);
106 static int get_remote_sockaddr(union sockaddr_union
*sa
, socklen_t
*salen
) {
112 if (path_is_absolute(arg_remote_host
)) {
113 sa
->un
.sun_family
= AF_UNIX
;
114 strncpy(sa
->un
.sun_path
, arg_remote_host
, sizeof(sa
->un
.sun_path
)-1);
115 sa
->un
.sun_path
[sizeof(sa
->un
.sun_path
)-1] = 0;
117 *salen
= offsetof(union sockaddr_union
, un
.sun_path
) + strlen(sa
->un
.sun_path
);
119 } else if (arg_remote_host
[0] == '@') {
120 sa
->un
.sun_family
= AF_UNIX
;
121 sa
->un
.sun_path
[0] = 0;
122 strncpy(sa
->un
.sun_path
+1, arg_remote_host
+1, sizeof(sa
->un
.sun_path
)-2);
123 sa
->un
.sun_path
[sizeof(sa
->un
.sun_path
)-1] = 0;
125 *salen
= offsetof(union sockaddr_union
, un
.sun_path
) + 1 + strlen(sa
->un
.sun_path
+ 1);
128 _cleanup_freeaddrinfo_
struct addrinfo
*result
= NULL
;
129 const char *node
, *service
;
131 struct addrinfo hints
= {
132 .ai_family
= AF_UNSPEC
,
133 .ai_socktype
= SOCK_STREAM
,
134 .ai_flags
= AI_ADDRCONFIG
137 service
= strrchr(arg_remote_host
, ':');
139 node
= strndupa(arg_remote_host
, service
- arg_remote_host
);
142 node
= arg_remote_host
;
146 log_debug("Looking up address info for %s:%s", node
, service
);
147 r
= getaddrinfo(node
, service
, &hints
, &result
);
149 log_error("Failed to resolve host %s:%s: %s", node
, service
, gai_strerror(r
));
150 return -EHOSTUNREACH
;
154 if (result
->ai_addrlen
> sizeof(union sockaddr_union
)) {
155 log_error("Address too long.");
159 memcpy(sa
, result
->ai_addr
, result
->ai_addrlen
);
160 *salen
= result
->ai_addrlen
;
166 static int connection_create_pipes(Connection
*c
, int buffer
[2], size_t *sz
) {
176 r
= pipe2(buffer
, O_CLOEXEC
|O_NONBLOCK
);
178 log_error("Failed to allocate pipe buffer: %m");
182 fcntl(buffer
[0], F_SETPIPE_SZ
, BUFFER_SIZE
);
184 r
= fcntl(buffer
[0], F_GETPIPE_SZ
);
186 log_error("Failed to get pipe buffer size: %m");
196 static int connection_shovel(
198 int *from
, int buffer
[2], int *to
,
199 size_t *full
, size_t *sz
,
200 sd_event_source
**from_source
, sd_event_source
**to_source
) {
207 assert(buffer
[0] >= 0);
208 assert(buffer
[1] >= 0);
220 if (*full
< *sz
&& *from
>= 0 && *to
>= 0) {
221 z
= splice(*from
, NULL
, buffer
[1], NULL
, *sz
- *full
, SPLICE_F_MOVE
|SPLICE_F_NONBLOCK
);
225 } else if (z
== 0 || errno
== EPIPE
|| errno
== ECONNRESET
) {
226 *from_source
= sd_event_source_unref(*from_source
);
227 close_nointr_nofail(*from
);
229 } else if (errno
!= EAGAIN
&& errno
!= EINTR
) {
230 log_error("Failed to splice: %m");
235 if (*full
> 0 && *to
>= 0) {
236 z
= splice(buffer
[0], NULL
, *to
, NULL
, *full
, SPLICE_F_MOVE
|SPLICE_F_NONBLOCK
);
240 } else if (z
== 0 || errno
== EPIPE
|| errno
== ECONNRESET
) {
241 *to_source
= sd_event_source_unref(*to_source
);
242 close_nointr_nofail(*to
);
244 } else if (errno
!= EAGAIN
&& errno
!= EINTR
) {
245 log_error("Failed to splice: %m");
254 static int connection_enable_event_sources(Connection
*c
, sd_event
*event
);
256 static int traffic_cb(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
257 Connection
*c
= userdata
;
264 r
= connection_shovel(c
,
265 &c
->server_fd
, c
->server_to_client_buffer
, &c
->client_fd
,
266 &c
->server_to_client_buffer_full
, &c
->server_to_client_buffer_size
,
267 &c
->server_event_source
, &c
->client_event_source
);
271 r
= connection_shovel(c
,
272 &c
->client_fd
, c
->client_to_server_buffer
, &c
->server_fd
,
273 &c
->client_to_server_buffer_full
, &c
->client_to_server_buffer_size
,
274 &c
->client_event_source
, &c
->server_event_source
);
278 /* EOF on both sides? */
279 if (c
->server_fd
== -1 && c
->client_fd
== -1)
282 /* Server closed, and all data written to client? */
283 if (c
->server_fd
== -1 && c
->server_to_client_buffer_full
<= 0)
286 /* Client closed, and all data written to server? */
287 if (c
->client_fd
== -1 && c
->client_to_server_buffer_full
<= 0)
290 r
= connection_enable_event_sources(c
, sd_event_source_get_event(s
));
298 return 0; /* ignore errors, continue serving */
301 static int connection_enable_event_sources(Connection
*c
, sd_event
*event
) {
302 uint32_t a
= 0, b
= 0;
308 if (c
->server_to_client_buffer_full
> 0)
310 if (c
->server_to_client_buffer_full
< c
->server_to_client_buffer_size
)
313 if (c
->client_to_server_buffer_full
> 0)
315 if (c
->client_to_server_buffer_full
< c
->client_to_server_buffer_size
)
318 if (c
->server_event_source
)
319 r
= sd_event_source_set_io_events(c
->server_event_source
, a
);
320 else if (c
->server_fd
>= 0)
321 r
= sd_event_add_io(event
, c
->server_fd
, a
, traffic_cb
, c
, &c
->server_event_source
);
326 log_error("Failed to set up server event source: %s", strerror(-r
));
330 if (c
->client_event_source
)
331 r
= sd_event_source_set_io_events(c
->client_event_source
, b
);
332 else if (c
->client_fd
>= 0)
333 r
= sd_event_add_io(event
, c
->client_fd
, b
, traffic_cb
, c
, &c
->client_event_source
);
338 log_error("Failed to set up client event source: %s", strerror(-r
));
345 static int connect_cb(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
346 Connection
*c
= userdata
;
354 solen
= sizeof(error
);
355 r
= getsockopt(fd
, SOL_SOCKET
, SO_ERROR
, &error
, &solen
);
357 log_error("Failed to issue SO_ERROR: %m");
362 log_error("Failed to connect to remote host: %s", strerror(error
));
366 c
->client_event_source
= sd_event_source_unref(c
->client_event_source
);
368 r
= connection_create_pipes(c
, c
->server_to_client_buffer
, &c
->server_to_client_buffer_size
);
372 r
= connection_create_pipes(c
, c
->client_to_server_buffer
, &c
->client_to_server_buffer_size
);
376 r
= connection_enable_event_sources(c
, sd_event_source_get_event(s
));
384 return 0; /* ignore errors, continue serving */
387 static int add_connection_socket(Context
*context
, sd_event
*event
, int fd
) {
388 union sockaddr_union sa
= {};
397 if (set_size(context
->connections
) > CONNECTIONS_MAX
) {
398 log_warning("Hit connection limit, refusing connection.");
399 close_nointr_nofail(fd
);
403 r
= set_ensure_allocated(&context
->connections
, trivial_hash_func
, trivial_compare_func
);
407 c
= new0(Connection
, 1);
411 c
->context
= context
;
414 c
->server_to_client_buffer
[0] = c
->server_to_client_buffer
[1] = -1;
415 c
->client_to_server_buffer
[0] = c
->client_to_server_buffer
[1] = -1;
417 r
= set_put(context
->connections
, c
);
423 r
= get_remote_sockaddr(&sa
, &salen
);
427 c
->client_fd
= socket(sa
.sa
.sa_family
, SOCK_STREAM
|SOCK_NONBLOCK
|SOCK_CLOEXEC
, 0);
428 if (c
->client_fd
< 0) {
429 log_error("Failed to get remote socket: %m");
433 r
= connect(c
->client_fd
, &sa
.sa
, salen
);
435 if (errno
== EINPROGRESS
) {
436 r
= sd_event_add_io(event
, c
->client_fd
, EPOLLOUT
, connect_cb
, c
, &c
->client_event_source
);
438 log_error("Failed to add connection socket: %s", strerror(-r
));
442 r
= sd_event_source_set_enabled(c
->client_event_source
, SD_EVENT_ONESHOT
);
444 log_error("Failed to enable oneshot event source: %s", strerror(-r
));
448 log_error("Failed to connect to remote host: %m");
452 r
= connection_enable_event_sources(c
, event
);
461 return 0; /* ignore non-OOM errors, continue serving */
464 static int accept_cb(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
465 Context
*context
= userdata
;
470 assert(revents
& EPOLLIN
);
473 nfd
= accept4(fd
, NULL
, NULL
, SOCK_NONBLOCK
|SOCK_CLOEXEC
);
475 _cleanup_free_
char *peer
= NULL
;
477 getpeername_pretty(nfd
, &peer
);
478 log_debug("New connection from %s", strna(peer
));
480 r
= add_connection_socket(context
, sd_event_source_get_event(s
), nfd
);
482 close_nointr_nofail(fd
);
486 } else if (errno
!= -EAGAIN
)
487 log_warning("Failed to accept() socket: %m");
489 r
= sd_event_source_set_enabled(s
, SD_EVENT_ONESHOT
);
491 log_error("Error %d while re-enabling listener with ONESHOT: %s", r
, strerror(-r
));
498 static int add_listen_socket(Context
*context
, sd_event
*event
, int fd
) {
499 sd_event_source
*source
;
506 r
= set_ensure_allocated(&context
->listen
, trivial_hash_func
, trivial_compare_func
);
512 r
= sd_is_socket(fd
, 0, SOCK_STREAM
, 1);
514 log_error("Failed to determine socket type: %s", strerror(-r
));
518 log_error("Passed in socket is not a stream socket.");
522 r
= fd_nonblock(fd
, true);
524 log_error("Failed to mark file descriptor non-blocking: %s", strerror(-r
));
528 r
= sd_event_add_io(event
, fd
, EPOLLIN
, accept_cb
, context
, &source
);
530 log_error("Failed to add event source: %s", strerror(-r
));
534 r
= set_put(context
->listen
, source
);
536 log_error("Failed to add source to set: %s", strerror(-r
));
537 sd_event_source_unref(source
);
541 /* Set the watcher to oneshot in case other processes are also
542 * watching to accept(). */
543 r
= sd_event_source_set_enabled(source
, SD_EVENT_ONESHOT
);
545 log_error("Failed to enable oneshot mode: %s", strerror(-r
));
552 static int help(void) {
554 printf("%s [HOST:PORT]\n"
556 "Bidirectionally proxy local sockets to another (possibly remote) socket.\n\n"
557 " -h --help Show this help\n"
558 " --version Show package version\n",
559 program_invocation_short_name
,
560 program_invocation_short_name
);
565 static int parse_argv(int argc
, char *argv
[]) {
572 static const struct option options
[] = {
573 { "help", no_argument
, NULL
, 'h' },
574 { "version", no_argument
, NULL
, ARG_VERSION
},
583 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0) {
591 puts(PACKAGE_STRING
);
592 puts(SYSTEMD_FEATURES
);
599 assert_not_reached("Unhandled option");
603 if (optind
>= argc
) {
604 log_error("Not enough parameters.");
608 if (argc
!= optind
+1) {
609 log_error("Too many parameters.");
613 arg_remote_host
= argv
[optind
];
617 int main(int argc
, char *argv
[]) {
618 _cleanup_event_unref_ sd_event
*event
= NULL
;
619 Context context
= {};
622 log_parse_environment();
625 r
= parse_argv(argc
, argv
);
629 r
= sd_event_default(&event
);
631 log_error("Failed to allocate event loop: %s", strerror(-r
));
635 n
= sd_listen_fds(1);
637 log_error("Failed to receive sockets from parent.");
641 log_error("Didn't get any sockets passed in.");
646 for (fd
= SD_LISTEN_FDS_START
; fd
< SD_LISTEN_FDS_START
+ n
; fd
++) {
647 r
= add_listen_socket(&context
, event
, fd
);
652 r
= sd_event_loop(event
);
654 log_error("Failed to run event loop: %s", strerror(-r
));
659 context_free(&context
);
661 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;