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>
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 (void) 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 return log_error_errno(r, "Failed to set up server event source: %m");
271 if (c->client_event_source)
272 r = sd_event_source_set_io_events(c->client_event_source, b);
273 else if (c->client_fd >= 0)
274 r = sd_event_add_io(c->context->event, &c->client_event_source, c->client_fd, b, traffic_cb, c);
279 return log_error_errno(r, "Failed to set up client event source: %m");
284 static int connection_complete(Connection *c) {
289 r = connection_create_pipes(c, c->server_to_client_buffer, &c->server_to_client_buffer_size);
293 r = connection_create_pipes(c, c->client_to_server_buffer, &c->client_to_server_buffer_size);
297 r = connection_enable_event_sources(c);
305 return 0; /* ignore errors, continue serving */
308 static int connect_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
309 Connection *c = userdata;
317 solen = sizeof(error);
318 r = getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &solen);
320 log_error("Failed to issue SO_ERROR: %m");
325 log_error_errno(error, "Failed to connect to remote host: %m");
329 c->client_event_source = sd_event_source_unref(c->client_event_source);
331 return connection_complete(c);
335 return 0; /* ignore errors, continue serving */
338 static int connection_start(Connection *c, struct sockaddr *sa, socklen_t salen) {
345 c->client_fd = socket(sa->sa_family, SOCK_STREAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0);
346 if (c->client_fd < 0) {
347 log_error("Failed to get remote socket: %m");
351 r = connect(c->client_fd, sa, salen);
353 if (errno == EINPROGRESS) {
354 r = sd_event_add_io(c->context->event, &c->client_event_source, c->client_fd, EPOLLOUT, connect_cb, c);
356 log_error_errno(r, "Failed to add connection socket: %m");
360 r = sd_event_source_set_enabled(c->client_event_source, SD_EVENT_ONESHOT);
362 log_error_errno(r, "Failed to enable oneshot event source: %m");
366 log_error("Failed to connect to remote host: %m");
370 r = connection_complete(c);
379 return 0; /* ignore errors, continue serving */
382 static int resolve_cb(sd_resolve_query *q, int ret, const struct addrinfo *ai, void *userdata) {
383 Connection *c = userdata;
389 log_error("Failed to resolve host: %s", gai_strerror(ret));
393 c->resolve_query = sd_resolve_query_unref(c->resolve_query);
395 return connection_start(c, ai->ai_addr, ai->ai_addrlen);
399 return 0; /* ignore errors, continue serving */
402 static int resolve_remote(Connection *c) {
404 static const struct addrinfo hints = {
405 .ai_family = AF_UNSPEC,
406 .ai_socktype = SOCK_STREAM,
407 .ai_flags = AI_ADDRCONFIG
410 union sockaddr_union sa = {};
411 const char *node, *service;
415 if (path_is_absolute(arg_remote_host)) {
416 sa.un.sun_family = AF_UNIX;
417 strncpy(sa.un.sun_path, arg_remote_host, sizeof(sa.un.sun_path)-1);
418 sa.un.sun_path[sizeof(sa.un.sun_path)-1] = 0;
420 salen = offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path);
422 return connection_start(c, &sa.sa, salen);
425 if (arg_remote_host[0] == '@') {
426 sa.un.sun_family = AF_UNIX;
427 sa.un.sun_path[0] = 0;
428 strncpy(sa.un.sun_path+1, arg_remote_host+1, sizeof(sa.un.sun_path)-2);
429 sa.un.sun_path[sizeof(sa.un.sun_path)-1] = 0;
431 salen = offsetof(union sockaddr_union, un.sun_path) + 1 + strlen(sa.un.sun_path + 1);
433 return connection_start(c, &sa.sa, salen);
436 service = strrchr(arg_remote_host, ':');
438 node = strndupa(arg_remote_host, service - arg_remote_host);
441 node = arg_remote_host;
445 log_debug("Looking up address info for %s:%s", node, service);
446 r = sd_resolve_getaddrinfo(c->context->resolve, &c->resolve_query, node, service, &hints, resolve_cb, c);
448 log_error_errno(r, "Failed to resolve remote host: %m");
456 return 0; /* ignore errors, continue serving */
459 static int add_connection_socket(Context *context, int fd) {
466 if (set_size(context->connections) > CONNECTIONS_MAX) {
467 log_warning("Hit connection limit, refusing connection.");
472 r = set_ensure_allocated(&context->connections, NULL);
478 c = new0(Connection, 1);
484 c->context = context;
487 c->server_to_client_buffer[0] = c->server_to_client_buffer[1] = -1;
488 c->client_to_server_buffer[0] = c->client_to_server_buffer[1] = -1;
490 r = set_put(context->connections, c);
497 return resolve_remote(c);
500 static int accept_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
501 _cleanup_free_ char *peer = NULL;
502 Context *context = userdata;
507 assert(revents & EPOLLIN);
510 nfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
512 if (errno != -EAGAIN)
513 log_warning("Failed to accept() socket: %m");
515 getpeername_pretty(nfd, &peer);
516 log_debug("New connection from %s", strna(peer));
518 r = add_connection_socket(context, nfd);
520 log_error_errno(r, "Failed to accept connection, ignoring: %m");
525 r = sd_event_source_set_enabled(s, SD_EVENT_ONESHOT);
527 log_error_errno(r, "Error while re-enabling listener with ONESHOT: %m");
528 sd_event_exit(context->event, r);
535 static int add_listen_socket(Context *context, int fd) {
536 sd_event_source *source;
542 r = set_ensure_allocated(&context->listen, NULL);
548 r = sd_is_socket(fd, 0, SOCK_STREAM, 1);
550 return log_error_errno(r, "Failed to determine socket type: %m");
552 log_error("Passed in socket is not a stream socket.");
556 r = fd_nonblock(fd, true);
558 return log_error_errno(r, "Failed to mark file descriptor non-blocking: %m");
560 r = sd_event_add_io(context->event, &source, fd, EPOLLIN, accept_cb, context);
562 return log_error_errno(r, "Failed to add event source: %m");
564 r = set_put(context->listen, source);
566 log_error_errno(r, "Failed to add source to set: %m");
567 sd_event_source_unref(source);
571 /* Set the watcher to oneshot in case other processes are also
572 * watching to accept(). */
573 r = sd_event_source_set_enabled(source, SD_EVENT_ONESHOT);
575 return log_error_errno(r, "Failed to enable oneshot mode: %m");
580 static void help(void) {
581 printf("%1$s [HOST:PORT]\n"
583 "Bidirectionally proxy local sockets to another (possibly remote) socket.\n\n"
584 " -h --help Show this help\n"
585 " --version Show package version\n",
586 program_invocation_short_name);
589 static int parse_argv(int argc, char *argv[]) {
596 static const struct option options[] = {
597 { "help", no_argument, NULL, 'h' },
598 { "version", no_argument, NULL, ARG_VERSION },
607 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
616 puts(PACKAGE_STRING);
617 puts(SYSTEMD_FEATURES);
624 assert_not_reached("Unhandled option");
627 if (optind >= argc) {
628 log_error("Not enough parameters.");
632 if (argc != optind+1) {
633 log_error("Too many parameters.");
637 arg_remote_host = argv[optind];
641 int main(int argc, char *argv[]) {
642 Context context = {};
645 log_parse_environment();
648 r = parse_argv(argc, argv);
652 r = sd_event_default(&context.event);
654 log_error_errno(r, "Failed to allocate event loop: %m");
658 r = sd_resolve_default(&context.resolve);
660 log_error_errno(r, "Failed to allocate resolver: %m");
664 r = sd_resolve_attach_event(context.resolve, context.event, 0);
666 log_error_errno(r, "Failed to attach resolver: %m");
670 sd_event_set_watchdog(context.event, true);
672 n = sd_listen_fds(1);
674 log_error("Failed to receive sockets from parent.");
678 log_error("Didn't get any sockets passed in.");
683 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
684 r = add_listen_socket(&context, fd);
689 r = sd_event_loop(context.event);
691 log_error_errno(r, "Failed to run event loop: %m");
696 context_free(&context);
698 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;