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 {
56 int server_fd, client_fd;
57 int server_to_client_buffer[2]; /* a pipe */
58 int client_to_server_buffer[2]; /* a pipe */
60 size_t server_to_client_buffer_full, client_to_server_buffer_full;
61 size_t server_to_client_buffer_size, client_to_server_buffer_size;
63 sd_event_source *server_event_source, *client_event_source;
68 struct sockaddr_un un;
69 struct sockaddr_in in;
70 struct sockaddr_in6 in6;
71 struct sockaddr_storage storage;
74 static const char *arg_remote_host = NULL;
76 static void connection_free(Connection *c) {
79 sd_event_source_unref(c->server_event_source);
80 sd_event_source_unref(c->client_event_source);
82 if (c->server_fd >= 0)
83 close_nointr_nofail(c->server_fd);
84 if (c->client_fd >= 0)
85 close_nointr_nofail(c->client_fd);
87 close_pipe(c->server_to_client_buffer);
88 close_pipe(c->client_to_server_buffer);
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_steal_first(context->connections)))
105 set_free(context->listen);
106 set_free(context->connections);
109 static int get_remote_sockaddr(union sockaddr_any *sa, socklen_t *salen) {
115 if (path_is_absolute(arg_remote_host)) {
116 sa->un.sun_family = AF_UNIX;
117 strncpy(sa->un.sun_path, arg_remote_host, sizeof(sa->un.sun_path)-1);
118 sa->un.sun_path[sizeof(sa->un.sun_path)-1] = 0;
120 *salen = offsetof(union sockaddr_any, un.sun_path) + strlen(sa->un.sun_path);
122 } else if (arg_remote_host[0] == '@') {
123 sa->un.sun_family = AF_UNIX;
124 sa->un.sun_path[0] = 0;
125 strncpy(sa->un.sun_path+1, arg_remote_host+1, sizeof(sa->un.sun_path)-2);
126 sa->un.sun_path[sizeof(sa->un.sun_path)-1] = 0;
128 *salen = offsetof(union sockaddr_any, un.sun_path) + 1 + strlen(sa->un.sun_path + 1);
131 _cleanup_freeaddrinfo_ struct addrinfo *result = NULL;
132 const char *node, *service;
134 struct addrinfo hints = {
135 .ai_family = AF_UNSPEC,
136 .ai_socktype = SOCK_STREAM,
137 .ai_flags = AI_ADDRCONFIG
140 service = strrchr(arg_remote_host, ':');
142 node = strndupa(arg_remote_host, service - arg_remote_host);
145 node = arg_remote_host;
149 log_debug("Looking up address info for %s:%s", node, service);
150 r = getaddrinfo(node, service, &hints, &result);
152 log_error("Failed to resolve host %s:%s: %s", node, service, gai_strerror(r));
153 return -EHOSTUNREACH;
157 if (result->ai_addrlen > sizeof(union sockaddr_any)) {
158 log_error("Address too long.");
162 memcpy(sa, result->ai_addr, result->ai_addrlen);
163 *salen = result->ai_addrlen;
169 static int connection_create_pipes(Connection *c, int buffer[2], size_t *sz) {
179 r = pipe2(buffer, O_CLOEXEC|O_NONBLOCK);
181 log_error("Failed to allocate pipe buffer: %m");
185 fcntl(buffer[0], F_SETPIPE_SZ, BUFFER_SIZE);
187 r = fcntl(buffer[0], F_GETPIPE_SZ);
189 log_error("Failed to get pipe buffer size: %m");
199 static int connection_shovel(
201 int *from, int buffer[2], int *to,
202 size_t *full, size_t *sz,
203 sd_event_source **from_source, sd_event_source **to_source) {
210 assert(buffer[0] >= 0);
211 assert(buffer[1] >= 0);
223 if (*full < *sz && *from >= 0 && *to >= 0) {
224 z = splice(*from, NULL, buffer[1], NULL, *sz - *full, SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
228 } else if (z == 0 || errno == EPIPE || errno == ECONNRESET) {
229 *from_source = sd_event_source_unref(*from_source);
230 close_nointr_nofail(*from);
232 } else if (errno != EAGAIN && errno != EINTR) {
233 log_error("Failed to splice: %m");
238 if (*full > 0 && *to >= 0) {
239 z = splice(buffer[0], NULL, *to, NULL, *full, SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
243 } else if (z == 0 || errno == EPIPE || errno == ECONNRESET) {
244 *to_source = sd_event_source_unref(*to_source);
245 close_nointr_nofail(*to);
247 } else if (errno != EAGAIN && errno != EINTR) {
248 log_error("Failed to splice: %m");
257 static int connection_enable_event_sources(Connection *c, sd_event *event);
259 static int traffic_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
260 Connection *c = userdata;
267 r = connection_shovel(c,
268 &c->server_fd, c->server_to_client_buffer, &c->client_fd,
269 &c->server_to_client_buffer_full, &c->server_to_client_buffer_size,
270 &c->server_event_source, &c->client_event_source);
274 r = connection_shovel(c,
275 &c->client_fd, c->client_to_server_buffer, &c->server_fd,
276 &c->client_to_server_buffer_full, &c->client_to_server_buffer_size,
277 &c->client_event_source, &c->server_event_source);
281 /* EOF on both sides? */
282 if (c->server_fd == -1 && c->client_fd == -1)
285 /* Server closed, and all data written to client? */
286 if (c->server_fd == -1 && c->server_to_client_buffer_full <= 0)
289 /* Client closed, and all data written to server? */
290 if (c->client_fd == -1 && c->client_to_server_buffer_full <= 0)
293 r = connection_enable_event_sources(c, sd_event_get(s));
301 return 0; /* ignore errors, continue serving */
304 static int connection_enable_event_sources(Connection *c, sd_event *event) {
305 uint32_t a = 0, b = 0;
311 if (c->server_to_client_buffer_full > 0)
313 if (c->server_to_client_buffer_full < c->server_to_client_buffer_size)
316 if (c->client_to_server_buffer_full > 0)
318 if (c->client_to_server_buffer_full < c->client_to_server_buffer_size)
321 if (c->server_event_source)
322 r = sd_event_source_set_io_events(c->server_event_source, a);
323 else if (c->server_fd >= 0)
324 r = sd_event_add_io(event, c->server_fd, a, traffic_cb, c, &c->server_event_source);
329 log_error("Failed to set up server event source: %s", strerror(-r));
333 if (c->client_event_source)
334 r = sd_event_source_set_io_events(c->client_event_source, b);
335 else if (c->client_fd >= 0)
336 r = sd_event_add_io(event, c->client_fd, b, traffic_cb, c, &c->client_event_source);
341 log_error("Failed to set up server event source: %s", strerror(-r));
348 static int connect_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
349 Connection *c = userdata;
357 solen = sizeof(error);
358 r = getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &solen);
360 log_error("Failed to issue SO_ERROR: %m");
365 log_error("Failed to connect to remote host: %s", strerror(error));
369 c->client_event_source = sd_event_source_unref(c->client_event_source);
371 r = connection_create_pipes(c, c->server_to_client_buffer, &c->server_to_client_buffer_size);
375 r = connection_create_pipes(c, c->client_to_server_buffer, &c->client_to_server_buffer_size);
379 r = connection_enable_event_sources(c, sd_event_get(s));
387 return 0; /* ignore errors, continue serving */
390 static int add_connection_socket(Context *context, sd_event *event, int fd) {
391 union sockaddr_any sa = {};
400 if (set_size(context->connections) > CONNECTIONS_MAX) {
401 log_warning("Hit connection limit, refusing connection.");
402 close_nointr_nofail(fd);
406 r = set_ensure_allocated(&context->connections, trivial_hash_func, trivial_compare_func);
410 c = new0(Connection, 1);
416 c->server_to_client_buffer[0] = c->server_to_client_buffer[1] = -1;
417 c->client_to_server_buffer[0] = c->client_to_server_buffer[1] = -1;
419 r = get_remote_sockaddr(&sa, &salen);
423 c->client_fd = socket(sa.sa.sa_family, SOCK_STREAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0);
424 if (c->client_fd < 0) {
425 log_error("Failed to get remote socket: %m");
429 r = connect(c->client_fd, &sa.sa, salen);
431 if (errno == EINPROGRESS) {
432 r = sd_event_add_io(event, c->client_fd, EPOLLOUT, connect_cb, c, &c->client_event_source);
434 log_error("Failed to add connection socket: %s", strerror(-r));
438 log_error("Failed to connect to remote host: %m");
442 r = connection_enable_event_sources(c, event);
451 return 0; /* ignore non-OOM errors, continue serving */
454 static int accept_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
455 Context *context = userdata;
460 assert(revents & EPOLLIN);
463 nfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
465 _cleanup_free_ char *peer = NULL;
467 getpeername_pretty(nfd, &peer);
468 log_debug("New connection from %s", strna(peer));
470 r = add_connection_socket(context, sd_event_get(s), nfd);
472 close_nointr_nofail(fd);
476 } else if (errno != -EAGAIN)
477 log_warning("Failed to accept() socket: %m");
479 r = sd_event_source_set_enabled(s, SD_EVENT_ONESHOT);
481 log_error("Error %d while re-enabling listener with ONESHOT: %s", r, strerror(-r));
488 static int add_listen_socket(Context *context, sd_event *event, int fd) {
489 sd_event_source *source;
496 log_info("Listening on %i", fd);
498 r = set_ensure_allocated(&context->listen, trivial_hash_func, trivial_compare_func);
504 r = sd_is_socket(fd, 0, SOCK_STREAM, 1);
506 log_error("Failed to determine socket type: %s", strerror(-r));
510 log_error("Passed in socket is not a stream socket.");
514 r = fd_nonblock(fd, true);
516 log_error("Failed to mark file descriptor non-blocking: %s", strerror(-r));
520 r = sd_event_add_io(event, fd, EPOLLIN, accept_cb, context, &source);
522 log_error("Failed to add event source: %s", strerror(-r));
526 r = set_put(context->listen, source);
528 log_error("Failed to add source to set: %s", strerror(-r));
529 sd_event_source_unref(source);
533 /* Set the watcher to oneshot in case other processes are also
534 * watching to accept(). */
535 r = sd_event_source_set_enabled(source, SD_EVENT_ONESHOT);
537 log_error("Failed to enable oneshot mode: %s", strerror(-r));
544 static int help(void) {
546 printf("%s [HOST:PORT]\n"
548 "Bidirectionally proxy local sockets to another (possibly remote) socket.\n\n"
549 " -h --help Show this help\n"
550 " --version Show package version\n",
551 program_invocation_short_name,
552 program_invocation_short_name);
557 static int parse_argv(int argc, char *argv[]) {
564 static const struct option options[] = {
565 { "help", no_argument, NULL, 'h' },
566 { "version", no_argument, NULL, ARG_VERSION },
575 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
583 puts(PACKAGE_STRING);
584 puts(SYSTEMD_FEATURES);
591 assert_not_reached("Unhandled option");
595 if (optind >= argc) {
596 log_error("Not enough parameters.");
600 if (argc != optind+1) {
601 log_error("Too many parameters.");
605 arg_remote_host = argv[optind];
609 int main(int argc, char *argv[]) {
610 _cleanup_event_unref_ sd_event *event = NULL;
611 Context context = {};
614 log_parse_environment();
617 r = parse_argv(argc, argv);
621 r = sd_event_new(&event);
623 log_error("Failed to allocate event loop: %s", strerror(-r));
627 n = sd_listen_fds(1);
629 log_error("Failed to receive sockets from parent.");
633 log_error("Didn't get any sockets passed in.");
638 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
639 r = add_listen_socket(&context, event, fd);
644 r = sd_event_loop(event);
646 log_error("Failed to run event loop: %s", strerror(-r));
651 context_free(&context);
653 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;