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"
36 #include "sd-resolve.h"
38 #include "socket-util.h"
40 #include "event-util.h"
43 #include "path-util.h"
45 #define BUFFER_SIZE (256 * 1024)
46 #define CONNECTIONS_MAX 256
48 static const char *arg_remote_host = NULL;
50 typedef struct Context {
58 typedef struct Connection {
61 int server_fd, client_fd;
62 int server_to_client_buffer[2]; /* a pipe */
63 int client_to_server_buffer[2]; /* a pipe */
65 size_t server_to_client_buffer_full, client_to_server_buffer_full;
66 size_t server_to_client_buffer_size, client_to_server_buffer_size;
68 sd_event_source *server_event_source, *client_event_source;
70 sd_resolve_query *resolve_query;
73 static void connection_free(Connection *c) {
77 set_remove(c->context->connections, c);
79 sd_event_source_unref(c->server_event_source);
80 sd_event_source_unref(c->client_event_source);
82 safe_close(c->server_fd);
83 safe_close(c->client_fd);
85 safe_close_pair(c->server_to_client_buffer);
86 safe_close_pair(c->client_to_server_buffer);
88 sd_resolve_query_unref(c->resolve_query);
93 static void context_free(Context *context) {
99 while ((es = set_steal_first(context->listen)))
100 sd_event_source_unref(es);
102 while ((c = set_first(context->connections)))
105 set_free(context->listen);
106 set_free(context->connections);
108 sd_event_unref(context->event);
109 sd_resolve_unref(context->resolve);
112 static int connection_create_pipes(Connection *c, int buffer[2], size_t *sz) {
122 r = pipe2(buffer, O_CLOEXEC|O_NONBLOCK);
124 log_error("Failed to allocate pipe buffer: %m");
128 fcntl(buffer[0], F_SETPIPE_SZ, BUFFER_SIZE);
130 r = fcntl(buffer[0], F_GETPIPE_SZ);
132 log_error("Failed to get pipe buffer size: %m");
142 static int connection_shovel(
144 int *from, int buffer[2], int *to,
145 size_t *full, size_t *sz,
146 sd_event_source **from_source, sd_event_source **to_source) {
153 assert(buffer[0] >= 0);
154 assert(buffer[1] >= 0);
166 if (*full < *sz && *from >= 0 && *to >= 0) {
167 z = splice(*from, NULL, buffer[1], NULL, *sz - *full, SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
171 } else if (z == 0 || errno == EPIPE || errno == ECONNRESET) {
172 *from_source = sd_event_source_unref(*from_source);
173 *from = safe_close(*from);
174 } else if (errno != EAGAIN && errno != EINTR) {
175 log_error("Failed to splice: %m");
180 if (*full > 0 && *to >= 0) {
181 z = splice(buffer[0], NULL, *to, NULL, *full, SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
185 } else if (z == 0 || errno == EPIPE || errno == ECONNRESET) {
186 *to_source = sd_event_source_unref(*to_source);
187 *to = safe_close(*to);
188 } else if (errno != EAGAIN && errno != EINTR) {
189 log_error("Failed to splice: %m");
198 static int connection_enable_event_sources(Connection *c);
200 static int traffic_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
201 Connection *c = userdata;
208 r = connection_shovel(c,
209 &c->server_fd, c->server_to_client_buffer, &c->client_fd,
210 &c->server_to_client_buffer_full, &c->server_to_client_buffer_size,
211 &c->server_event_source, &c->client_event_source);
215 r = connection_shovel(c,
216 &c->client_fd, c->client_to_server_buffer, &c->server_fd,
217 &c->client_to_server_buffer_full, &c->client_to_server_buffer_size,
218 &c->client_event_source, &c->server_event_source);
222 /* EOF on both sides? */
223 if (c->server_fd == -1 && c->client_fd == -1)
226 /* Server closed, and all data written to client? */
227 if (c->server_fd == -1 && c->server_to_client_buffer_full <= 0)
230 /* Client closed, and all data written to server? */
231 if (c->client_fd == -1 && c->client_to_server_buffer_full <= 0)
234 r = connection_enable_event_sources(c);
242 return 0; /* ignore errors, continue serving */
245 static int connection_enable_event_sources(Connection *c) {
246 uint32_t a = 0, b = 0;
251 if (c->server_to_client_buffer_full > 0)
253 if (c->server_to_client_buffer_full < c->server_to_client_buffer_size)
256 if (c->client_to_server_buffer_full > 0)
258 if (c->client_to_server_buffer_full < c->client_to_server_buffer_size)
261 if (c->server_event_source)
262 r = sd_event_source_set_io_events(c->server_event_source, a);
263 else if (c->server_fd >= 0)
264 r = sd_event_add_io(c->context->event, &c->server_event_source, c->server_fd, a, traffic_cb, c);
269 log_error("Failed to set up server event source: %s", strerror(-r));
273 if (c->client_event_source)
274 r = sd_event_source_set_io_events(c->client_event_source, b);
275 else if (c->client_fd >= 0)
276 r = sd_event_add_io(c->context->event, &c->client_event_source, c->client_fd, b, traffic_cb, c);
281 log_error("Failed to set up client event source: %s", strerror(-r));
288 static int connection_complete(Connection *c) {
293 r = connection_create_pipes(c, c->server_to_client_buffer, &c->server_to_client_buffer_size);
297 r = connection_create_pipes(c, c->client_to_server_buffer, &c->client_to_server_buffer_size);
301 r = connection_enable_event_sources(c);
309 return 0; /* ignore errors, continue serving */
312 static int connect_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
313 Connection *c = userdata;
321 solen = sizeof(error);
322 r = getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &solen);
324 log_error("Failed to issue SO_ERROR: %m");
329 log_error("Failed to connect to remote host: %s", strerror(error));
333 c->client_event_source = sd_event_source_unref(c->client_event_source);
335 return connection_complete(c);
339 return 0; /* ignore errors, continue serving */
342 static int connection_start(Connection *c, struct sockaddr *sa, socklen_t salen) {
349 c->client_fd = socket(sa->sa_family, SOCK_STREAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0);
350 if (c->client_fd < 0) {
351 log_error("Failed to get remote socket: %m");
355 r = connect(c->client_fd, sa, salen);
357 if (errno == EINPROGRESS) {
358 r = sd_event_add_io(c->context->event, &c->client_event_source, c->client_fd, EPOLLOUT, connect_cb, c);
360 log_error("Failed to add connection socket: %s", strerror(-r));
364 r = sd_event_source_set_enabled(c->client_event_source, SD_EVENT_ONESHOT);
366 log_error("Failed to enable oneshot event source: %s", strerror(-r));
370 log_error("Failed to connect to remote host: %m");
374 r = connection_complete(c);
383 return 0; /* ignore errors, continue serving */
386 static int resolve_cb(sd_resolve_query *q, int ret, const struct addrinfo *ai, void *userdata) {
387 Connection *c = userdata;
393 log_error("Failed to resolve host: %s", gai_strerror(ret));
397 c->resolve_query = sd_resolve_query_unref(c->resolve_query);
399 return connection_start(c, ai->ai_addr, ai->ai_addrlen);
403 return 0; /* ignore errors, continue serving */
406 static int resolve_remote(Connection *c) {
408 static const struct addrinfo hints = {
409 .ai_family = AF_UNSPEC,
410 .ai_socktype = SOCK_STREAM,
411 .ai_flags = AI_ADDRCONFIG
414 union sockaddr_union sa = {};
415 const char *node, *service;
419 if (path_is_absolute(arg_remote_host)) {
420 sa.un.sun_family = AF_UNIX;
421 strncpy(sa.un.sun_path, arg_remote_host, sizeof(sa.un.sun_path)-1);
422 sa.un.sun_path[sizeof(sa.un.sun_path)-1] = 0;
424 salen = offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path);
426 return connection_start(c, &sa.sa, salen);
429 if (arg_remote_host[0] == '@') {
430 sa.un.sun_family = AF_UNIX;
431 sa.un.sun_path[0] = 0;
432 strncpy(sa.un.sun_path+1, arg_remote_host+1, sizeof(sa.un.sun_path)-2);
433 sa.un.sun_path[sizeof(sa.un.sun_path)-1] = 0;
435 salen = offsetof(union sockaddr_union, un.sun_path) + 1 + strlen(sa.un.sun_path + 1);
437 return connection_start(c, &sa.sa, salen);
440 service = strrchr(arg_remote_host, ':');
442 node = strndupa(arg_remote_host, service - arg_remote_host);
445 node = arg_remote_host;
449 log_debug("Looking up address info for %s:%s", node, service);
450 r = sd_resolve_getaddrinfo(c->context->resolve, &c->resolve_query, node, service, &hints, resolve_cb, c);
452 log_error("Failed to resolve remote host: %s", strerror(-r));
460 return 0; /* ignore errors, continue serving */
463 static int add_connection_socket(Context *context, int fd) {
470 if (set_size(context->connections) > CONNECTIONS_MAX) {
471 log_warning("Hit connection limit, refusing connection.");
476 r = set_ensure_allocated(&context->connections, trivial_hash_func, trivial_compare_func);
482 c = new0(Connection, 1);
488 c->context = context;
491 c->server_to_client_buffer[0] = c->server_to_client_buffer[1] = -1;
492 c->client_to_server_buffer[0] = c->client_to_server_buffer[1] = -1;
494 r = set_put(context->connections, c);
501 return resolve_remote(c);
504 static int accept_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
505 _cleanup_free_ char *peer = NULL;
506 Context *context = userdata;
511 assert(revents & EPOLLIN);
514 nfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
516 if (errno != -EAGAIN)
517 log_warning("Failed to accept() socket: %m");
519 getpeername_pretty(nfd, &peer);
520 log_debug("New connection from %s", strna(peer));
522 r = add_connection_socket(context, nfd);
524 log_error("Failed to accept connection, ignoring: %s", strerror(-r));
529 r = sd_event_source_set_enabled(s, SD_EVENT_ONESHOT);
531 log_error("Error while re-enabling listener with ONESHOT: %s", strerror(-r));
532 sd_event_exit(context->event, r);
539 static int add_listen_socket(Context *context, int fd) {
540 sd_event_source *source;
546 r = set_ensure_allocated(&context->listen, trivial_hash_func, trivial_compare_func);
552 r = sd_is_socket(fd, 0, SOCK_STREAM, 1);
554 log_error("Failed to determine socket type: %s", strerror(-r));
558 log_error("Passed in socket is not a stream socket.");
562 r = fd_nonblock(fd, true);
564 log_error("Failed to mark file descriptor non-blocking: %s", strerror(-r));
568 r = sd_event_add_io(context->event, &source, fd, EPOLLIN, accept_cb, context);
570 log_error("Failed to add event source: %s", strerror(-r));
574 r = set_put(context->listen, source);
576 log_error("Failed to add source to set: %s", strerror(-r));
577 sd_event_source_unref(source);
581 /* Set the watcher to oneshot in case other processes are also
582 * watching to accept(). */
583 r = sd_event_source_set_enabled(source, SD_EVENT_ONESHOT);
585 log_error("Failed to enable oneshot mode: %s", strerror(-r));
592 static int help(void) {
594 printf("%s [HOST:PORT]\n"
596 "Bidirectionally proxy local sockets to another (possibly remote) socket.\n\n"
597 " -h --help Show this help\n"
598 " --version Show package version\n",
599 program_invocation_short_name,
600 program_invocation_short_name);
605 static int parse_argv(int argc, char *argv[]) {
612 static const struct option options[] = {
613 { "help", no_argument, NULL, 'h' },
614 { "version", no_argument, NULL, ARG_VERSION },
623 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
631 puts(PACKAGE_STRING);
632 puts(SYSTEMD_FEATURES);
639 assert_not_reached("Unhandled option");
643 if (optind >= argc) {
644 log_error("Not enough parameters.");
648 if (argc != optind+1) {
649 log_error("Too many parameters.");
653 arg_remote_host = argv[optind];
657 int main(int argc, char *argv[]) {
658 Context context = {};
661 log_parse_environment();
664 r = parse_argv(argc, argv);
668 r = sd_event_default(&context.event);
670 log_error("Failed to allocate event loop: %s", strerror(-r));
674 r = sd_resolve_default(&context.resolve);
676 log_error("Failed to allocate resolver: %s", strerror(-r));
680 r = sd_resolve_attach_event(context.resolve, context.event, 0);
682 log_error("Failed to attach resolver: %s", strerror(-r));
686 sd_event_set_watchdog(context.event, true);
688 n = sd_listen_fds(1);
690 log_error("Failed to receive sockets from parent.");
694 log_error("Didn't get any sockets passed in.");
699 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
700 r = add_listen_socket(&context, fd);
705 r = sd_event_loop(context.event);
707 log_error("Failed to run event loop: %s", strerror(-r));
712 context_free(&context);
714 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;