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;
69 static int arg_listener = -1;
71 static void connection_free(Connection *c) {
75 set_remove(c->context->connections, c);
77 sd_event_source_unref(c->server_event_source);
78 sd_event_source_unref(c->client_event_source);
80 if (c->server_fd >= 0)
81 close_nointr_nofail(c->server_fd);
82 if (c->client_fd >= 0)
83 close_nointr_nofail(c->client_fd);
85 close_pipe(c->server_to_client_buffer);
86 close_pipe(c->client_to_server_buffer);
91 static void context_free(Context *context) {
97 while ((es = set_steal_first(context->listen)))
98 sd_event_source_unref(es);
100 while ((c = set_first(context->connections)))
103 set_free(context->listen);
104 set_free(context->connections);
107 static int get_remote_sockaddr(union sockaddr_union *sa, socklen_t *salen) {
113 if (path_is_absolute(arg_remote_host)) {
114 sa->un.sun_family = AF_UNIX;
115 strncpy(sa->un.sun_path, arg_remote_host, sizeof(sa->un.sun_path)-1);
116 sa->un.sun_path[sizeof(sa->un.sun_path)-1] = 0;
118 *salen = offsetof(union sockaddr_union, un.sun_path) + strlen(sa->un.sun_path);
120 } else if (arg_remote_host[0] == '@') {
121 sa->un.sun_family = AF_UNIX;
122 sa->un.sun_path[0] = 0;
123 strncpy(sa->un.sun_path+1, arg_remote_host+1, sizeof(sa->un.sun_path)-2);
124 sa->un.sun_path[sizeof(sa->un.sun_path)-1] = 0;
126 *salen = offsetof(union sockaddr_union, un.sun_path) + 1 + strlen(sa->un.sun_path + 1);
129 _cleanup_freeaddrinfo_ struct addrinfo *result = NULL;
130 const char *node, *service;
132 struct addrinfo hints = {
133 .ai_family = AF_UNSPEC,
134 .ai_socktype = SOCK_STREAM,
135 .ai_flags = AI_ADDRCONFIG
138 service = strrchr(arg_remote_host, ':');
140 node = strndupa(arg_remote_host, service - arg_remote_host);
143 node = arg_remote_host;
147 log_debug("Looking up address info for %s:%s", node, service);
148 r = getaddrinfo(node, service, &hints, &result);
150 log_error("Failed to resolve host %s:%s: %s", node, service, gai_strerror(r));
151 return -EHOSTUNREACH;
155 if (result->ai_addrlen > sizeof(union sockaddr_union)) {
156 log_error("Address too long.");
160 memcpy(sa, result->ai_addr, result->ai_addrlen);
161 *salen = result->ai_addrlen;
167 static int connection_create_pipes(Connection *c, int buffer[2], size_t *sz) {
177 r = pipe2(buffer, O_CLOEXEC|O_NONBLOCK);
179 log_error("Failed to allocate pipe buffer: %m");
183 fcntl(buffer[0], F_SETPIPE_SZ, BUFFER_SIZE);
185 r = fcntl(buffer[0], F_GETPIPE_SZ);
187 log_error("Failed to get pipe buffer size: %m");
197 static int connection_shovel(
199 int *from, int buffer[2], int *to,
200 size_t *full, size_t *sz,
201 sd_event_source **from_source, sd_event_source **to_source) {
208 assert(buffer[0] >= 0);
209 assert(buffer[1] >= 0);
221 if (*full < *sz && *from >= 0 && *to >= 0) {
222 z = splice(*from, NULL, buffer[1], NULL, *sz - *full, SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
226 } else if (z == 0 || errno == EPIPE || errno == ECONNRESET) {
227 *from_source = sd_event_source_unref(*from_source);
228 close_nointr_nofail(*from);
230 } else if (errno != EAGAIN && errno != EINTR) {
231 log_error("Failed to splice: %m");
236 if (*full > 0 && *to >= 0) {
237 z = splice(buffer[0], NULL, *to, NULL, *full, SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
241 } else if (z == 0 || errno == EPIPE || errno == ECONNRESET) {
242 *to_source = sd_event_source_unref(*to_source);
243 close_nointr_nofail(*to);
245 } else if (errno != EAGAIN && errno != EINTR) {
246 log_error("Failed to splice: %m");
255 static int connection_enable_event_sources(Connection *c, sd_event *event);
257 static int traffic_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
258 Connection *c = userdata;
265 r = connection_shovel(c,
266 &c->server_fd, c->server_to_client_buffer, &c->client_fd,
267 &c->server_to_client_buffer_full, &c->server_to_client_buffer_size,
268 &c->server_event_source, &c->client_event_source);
272 r = connection_shovel(c,
273 &c->client_fd, c->client_to_server_buffer, &c->server_fd,
274 &c->client_to_server_buffer_full, &c->client_to_server_buffer_size,
275 &c->client_event_source, &c->server_event_source);
279 /* EOF on both sides? */
280 if (c->server_fd == -1 && c->client_fd == -1)
283 /* Server closed, and all data written to client? */
284 if (c->server_fd == -1 && c->server_to_client_buffer_full <= 0)
287 /* Client closed, and all data written to server? */
288 if (c->client_fd == -1 && c->client_to_server_buffer_full <= 0)
291 r = connection_enable_event_sources(c, sd_event_source_get_event(s));
299 return 0; /* ignore errors, continue serving */
302 static int connection_enable_event_sources(Connection *c, sd_event *event) {
303 uint32_t a = 0, b = 0;
309 if (c->server_to_client_buffer_full > 0)
311 if (c->server_to_client_buffer_full < c->server_to_client_buffer_size)
314 if (c->client_to_server_buffer_full > 0)
316 if (c->client_to_server_buffer_full < c->client_to_server_buffer_size)
319 if (c->server_event_source)
320 r = sd_event_source_set_io_events(c->server_event_source, a);
321 else if (c->server_fd >= 0)
322 r = sd_event_add_io(event, c->server_fd, a, traffic_cb, c, &c->server_event_source);
327 log_error("Failed to set up server event source: %s", strerror(-r));
331 if (c->client_event_source)
332 r = sd_event_source_set_io_events(c->client_event_source, b);
333 else if (c->client_fd >= 0)
334 r = sd_event_add_io(event, c->client_fd, b, traffic_cb, c, &c->client_event_source);
339 log_error("Failed to set up client event source: %s", strerror(-r));
346 static int connect_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
347 Connection *c = userdata;
355 solen = sizeof(error);
356 r = getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &solen);
358 log_error("Failed to issue SO_ERROR: %m");
363 log_error("Failed to connect to remote host: %s", strerror(error));
367 c->client_event_source = sd_event_source_unref(c->client_event_source);
369 r = connection_create_pipes(c, c->server_to_client_buffer, &c->server_to_client_buffer_size);
373 r = connection_create_pipes(c, c->client_to_server_buffer, &c->client_to_server_buffer_size);
377 r = connection_enable_event_sources(c, sd_event_source_get_event(s));
385 return 0; /* ignore errors, continue serving */
388 static int add_connection_socket(Context *context, sd_event *event, int fd) {
389 union sockaddr_union sa = {};
398 if (set_size(context->connections) > CONNECTIONS_MAX) {
399 log_warning("Hit connection limit, refusing connection.");
400 close_nointr_nofail(fd);
404 r = set_ensure_allocated(&context->connections, trivial_hash_func, trivial_compare_func);
408 c = new0(Connection, 1);
412 c->context = context;
415 c->server_to_client_buffer[0] = c->server_to_client_buffer[1] = -1;
416 c->client_to_server_buffer[0] = c->client_to_server_buffer[1] = -1;
418 r = set_put(context->connections, c);
424 r = get_remote_sockaddr(&sa, &salen);
428 c->client_fd = socket(sa.sa.sa_family, SOCK_STREAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0);
429 if (c->client_fd < 0) {
430 log_error("Failed to get remote socket: %m");
434 r = connect(c->client_fd, &sa.sa, salen);
436 if (errno == EINPROGRESS) {
437 r = sd_event_add_io(event, c->client_fd, EPOLLOUT, connect_cb, c, &c->client_event_source);
439 log_error("Failed to add connection socket: %s", strerror(-r));
443 r = sd_event_source_set_enabled(c->client_event_source, SD_EVENT_ONESHOT);
445 log_error("Failed to enable oneshot event source: %s", strerror(-r));
449 log_error("Failed to connect to remote host: %m");
453 r = connection_enable_event_sources(c, event);
462 return 0; /* ignore non-OOM errors, continue serving */
465 static int accept_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
466 Context *context = userdata;
471 assert(revents & EPOLLIN);
474 nfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
476 _cleanup_free_ char *peer = NULL;
478 getpeername_pretty(nfd, &peer);
479 log_debug("New connection from %s", strna(peer));
481 r = add_connection_socket(context, sd_event_source_get_event(s), nfd);
483 close_nointr_nofail(fd);
487 } else if (errno != -EAGAIN)
488 log_warning("Failed to accept() socket: %m");
490 r = sd_event_source_set_enabled(s, SD_EVENT_ONESHOT);
492 log_error("Error %d while re-enabling listener with ONESHOT: %s", r, strerror(-r));
499 static int add_listen_socket(Context *context, sd_event *event, int fd) {
500 sd_event_source *source;
507 r = set_ensure_allocated(&context->listen, trivial_hash_func, trivial_compare_func);
513 r = sd_is_socket(fd, 0, SOCK_STREAM, 1);
515 log_error("Failed to determine socket type: %s", strerror(-r));
519 log_error("Passed in socket is not a stream socket.");
523 r = fd_nonblock(fd, true);
525 log_error("Failed to mark file descriptor non-blocking: %s", strerror(-r));
529 r = sd_event_add_io(event, fd, EPOLLIN, accept_cb, context, &source);
531 log_error("Failed to add event source: %s", strerror(-r));
535 r = set_put(context->listen, source);
537 log_error("Failed to add source to set: %s", strerror(-r));
538 sd_event_source_unref(source);
542 /* Set the watcher to oneshot in case other processes are also
543 * watching to accept(). */
544 r = sd_event_source_set_enabled(source, SD_EVENT_ONESHOT);
546 log_error("Failed to enable oneshot mode: %s", strerror(-r));
553 static int help(void) {
555 printf("%s [HOST:PORT]\n"
557 "Bidirectionally proxy local sockets to another (possibly remote) socket.\n\n"
558 " -l --listener=FD Listen on a specific, single file descriptor.\n"
559 " -h --help Show this help\n"
560 " --version Show package version\n",
561 program_invocation_short_name,
562 program_invocation_short_name);
567 static int parse_argv(int argc, char *argv[]) {
573 static const struct option options[] = {
574 { "help", no_argument, NULL, 'h' },
575 { "version", no_argument, NULL, ARG_VERSION },
576 { "listener", required_argument, NULL, 'l' },
585 while ((c = getopt_long(argc, argv, "hl:", options, NULL)) >= 0) {
593 puts(PACKAGE_STRING);
594 puts(SYSTEMD_FEATURES);
598 if (safe_atoi(optarg, &fd) < 0) {
599 log_error("Failed to parse listener file descriptor: %s", optarg);
602 if (fd < SD_LISTEN_FDS_START) {
603 log_error("Listener file descriptor must be at least %d.", SD_LISTEN_FDS_START);
613 assert_not_reached("Unhandled option");
617 if (optind >= argc) {
618 log_error("Not enough parameters.");
622 if (argc != optind+1) {
623 log_error("Too many parameters.");
627 arg_remote_host = argv[optind];
631 int main(int argc, char *argv[]) {
632 _cleanup_event_unref_ sd_event *event = NULL;
633 Context context = {};
636 log_parse_environment();
639 r = parse_argv(argc, argv);
643 r = sd_event_default(&event);
645 log_error("Failed to allocate event loop: %s", strerror(-r));
649 if (arg_listener == -1) {
650 n = sd_listen_fds(1);
652 log_error("Failed to receive sockets from parent.");
656 log_error("Didn't get any sockets passed in.");
660 log_info("Listening on %d inherited socket(s), starting with fd=%d.", n, SD_LISTEN_FDS_START);
661 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
662 r = add_listen_socket(&context, event, fd);
667 log_info("Listening on single inherited socket fd=%d.", arg_listener);
668 r = add_listen_socket(&context, event, arg_listener);
673 r = sd_event_loop(event);
675 log_error("Failed to run event loop: %s", strerror(-r));
680 context_free(&context);
682 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;