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 return log_error_errno(errno, "Failed to allocate pipe buffer: %m");
126 (void) fcntl(buffer[0], F_SETPIPE_SZ, BUFFER_SIZE);
128 r = fcntl(buffer[0], F_GETPIPE_SZ);
130 return log_error_errno(errno, "Failed to get pipe buffer size: %m");
138 static int connection_shovel(
140 int *from, int buffer[2], int *to,
141 size_t *full, size_t *sz,
142 sd_event_source **from_source, sd_event_source **to_source) {
149 assert(buffer[0] >= 0);
150 assert(buffer[1] >= 0);
162 if (*full < *sz && *from >= 0 && *to >= 0) {
163 z = splice(*from, NULL, buffer[1], NULL, *sz - *full, SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
167 } else if (z == 0 || errno == EPIPE || errno == ECONNRESET) {
168 *from_source = sd_event_source_unref(*from_source);
169 *from = safe_close(*from);
170 } else if (errno != EAGAIN && errno != EINTR)
171 return log_error_errno(errno, "Failed to splice: %m");
174 if (*full > 0 && *to >= 0) {
175 z = splice(buffer[0], NULL, *to, NULL, *full, SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
179 } else if (z == 0 || errno == EPIPE || errno == ECONNRESET) {
180 *to_source = sd_event_source_unref(*to_source);
181 *to = safe_close(*to);
182 } else if (errno != EAGAIN && errno != EINTR)
183 return log_error_errno(errno, "Failed to splice: %m");
190 static int connection_enable_event_sources(Connection *c);
192 static int traffic_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
193 Connection *c = userdata;
200 r = connection_shovel(c,
201 &c->server_fd, c->server_to_client_buffer, &c->client_fd,
202 &c->server_to_client_buffer_full, &c->server_to_client_buffer_size,
203 &c->server_event_source, &c->client_event_source);
207 r = connection_shovel(c,
208 &c->client_fd, c->client_to_server_buffer, &c->server_fd,
209 &c->client_to_server_buffer_full, &c->client_to_server_buffer_size,
210 &c->client_event_source, &c->server_event_source);
214 /* EOF on both sides? */
215 if (c->server_fd == -1 && c->client_fd == -1)
218 /* Server closed, and all data written to client? */
219 if (c->server_fd == -1 && c->server_to_client_buffer_full <= 0)
222 /* Client closed, and all data written to server? */
223 if (c->client_fd == -1 && c->client_to_server_buffer_full <= 0)
226 r = connection_enable_event_sources(c);
234 return 0; /* ignore errors, continue serving */
237 static int connection_enable_event_sources(Connection *c) {
238 uint32_t a = 0, b = 0;
243 if (c->server_to_client_buffer_full > 0)
245 if (c->server_to_client_buffer_full < c->server_to_client_buffer_size)
248 if (c->client_to_server_buffer_full > 0)
250 if (c->client_to_server_buffer_full < c->client_to_server_buffer_size)
253 if (c->server_event_source)
254 r = sd_event_source_set_io_events(c->server_event_source, a);
255 else if (c->server_fd >= 0)
256 r = sd_event_add_io(c->context->event, &c->server_event_source, c->server_fd, a, traffic_cb, c);
261 return log_error_errno(r, "Failed to set up server event source: %m");
263 if (c->client_event_source)
264 r = sd_event_source_set_io_events(c->client_event_source, b);
265 else if (c->client_fd >= 0)
266 r = sd_event_add_io(c->context->event, &c->client_event_source, c->client_fd, b, traffic_cb, c);
271 return log_error_errno(r, "Failed to set up client event source: %m");
276 static int connection_complete(Connection *c) {
281 r = connection_create_pipes(c, c->server_to_client_buffer, &c->server_to_client_buffer_size);
285 r = connection_create_pipes(c, c->client_to_server_buffer, &c->client_to_server_buffer_size);
289 r = connection_enable_event_sources(c);
297 return 0; /* ignore errors, continue serving */
300 static int connect_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
301 Connection *c = userdata;
309 solen = sizeof(error);
310 r = getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &solen);
312 log_error_errno(errno, "Failed to issue SO_ERROR: %m");
317 log_error_errno(error, "Failed to connect to remote host: %m");
321 c->client_event_source = sd_event_source_unref(c->client_event_source);
323 return connection_complete(c);
327 return 0; /* ignore errors, continue serving */
330 static int connection_start(Connection *c, struct sockaddr *sa, socklen_t salen) {
337 c->client_fd = socket(sa->sa_family, SOCK_STREAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0);
338 if (c->client_fd < 0) {
339 log_error_errno(errno, "Failed to get remote socket: %m");
343 r = connect(c->client_fd, sa, salen);
345 if (errno == EINPROGRESS) {
346 r = sd_event_add_io(c->context->event, &c->client_event_source, c->client_fd, EPOLLOUT, connect_cb, c);
348 log_error_errno(r, "Failed to add connection socket: %m");
352 r = sd_event_source_set_enabled(c->client_event_source, SD_EVENT_ONESHOT);
354 log_error_errno(r, "Failed to enable oneshot event source: %m");
358 log_error_errno(errno, "Failed to connect to remote host: %m");
362 r = connection_complete(c);
371 return 0; /* ignore errors, continue serving */
374 static int resolve_cb(sd_resolve_query *q, int ret, const struct addrinfo *ai, void *userdata) {
375 Connection *c = userdata;
381 log_error("Failed to resolve host: %s", gai_strerror(ret));
385 c->resolve_query = sd_resolve_query_unref(c->resolve_query);
387 return connection_start(c, ai->ai_addr, ai->ai_addrlen);
391 return 0; /* ignore errors, continue serving */
394 static int resolve_remote(Connection *c) {
396 static const struct addrinfo hints = {
397 .ai_family = AF_UNSPEC,
398 .ai_socktype = SOCK_STREAM,
399 .ai_flags = AI_ADDRCONFIG
402 union sockaddr_union sa = {};
403 const char *node, *service;
407 if (path_is_absolute(arg_remote_host)) {
408 sa.un.sun_family = AF_UNIX;
409 strncpy(sa.un.sun_path, arg_remote_host, sizeof(sa.un.sun_path)-1);
410 sa.un.sun_path[sizeof(sa.un.sun_path)-1] = 0;
412 salen = offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path);
414 return connection_start(c, &sa.sa, salen);
417 if (arg_remote_host[0] == '@') {
418 sa.un.sun_family = AF_UNIX;
419 sa.un.sun_path[0] = 0;
420 strncpy(sa.un.sun_path+1, arg_remote_host+1, sizeof(sa.un.sun_path)-2);
421 sa.un.sun_path[sizeof(sa.un.sun_path)-1] = 0;
423 salen = offsetof(union sockaddr_union, un.sun_path) + 1 + strlen(sa.un.sun_path + 1);
425 return connection_start(c, &sa.sa, salen);
428 service = strrchr(arg_remote_host, ':');
430 node = strndupa(arg_remote_host, service - arg_remote_host);
433 node = arg_remote_host;
437 log_debug("Looking up address info for %s:%s", node, service);
438 r = sd_resolve_getaddrinfo(c->context->resolve, &c->resolve_query, node, service, &hints, resolve_cb, c);
440 log_error_errno(r, "Failed to resolve remote host: %m");
448 return 0; /* ignore errors, continue serving */
451 static int add_connection_socket(Context *context, int fd) {
458 if (set_size(context->connections) > CONNECTIONS_MAX) {
459 log_warning("Hit connection limit, refusing connection.");
464 r = set_ensure_allocated(&context->connections, NULL);
470 c = new0(Connection, 1);
476 c->context = context;
479 c->server_to_client_buffer[0] = c->server_to_client_buffer[1] = -1;
480 c->client_to_server_buffer[0] = c->client_to_server_buffer[1] = -1;
482 r = set_put(context->connections, c);
489 return resolve_remote(c);
492 static int accept_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
493 _cleanup_free_ char *peer = NULL;
494 Context *context = userdata;
499 assert(revents & EPOLLIN);
502 nfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
504 if (errno != -EAGAIN)
505 log_warning_errno(errno, "Failed to accept() socket: %m");
507 getpeername_pretty(nfd, &peer);
508 log_debug("New connection from %s", strna(peer));
510 r = add_connection_socket(context, nfd);
512 log_error_errno(r, "Failed to accept connection, ignoring: %m");
517 r = sd_event_source_set_enabled(s, SD_EVENT_ONESHOT);
519 log_error_errno(r, "Error while re-enabling listener with ONESHOT: %m");
520 sd_event_exit(context->event, r);
527 static int add_listen_socket(Context *context, int fd) {
528 sd_event_source *source;
534 r = set_ensure_allocated(&context->listen, NULL);
540 r = sd_is_socket(fd, 0, SOCK_STREAM, 1);
542 return log_error_errno(r, "Failed to determine socket type: %m");
544 log_error("Passed in socket is not a stream socket.");
548 r = fd_nonblock(fd, true);
550 return log_error_errno(r, "Failed to mark file descriptor non-blocking: %m");
552 r = sd_event_add_io(context->event, &source, fd, EPOLLIN, accept_cb, context);
554 return log_error_errno(r, "Failed to add event source: %m");
556 r = set_put(context->listen, source);
558 log_error_errno(r, "Failed to add source to set: %m");
559 sd_event_source_unref(source);
563 /* Set the watcher to oneshot in case other processes are also
564 * watching to accept(). */
565 r = sd_event_source_set_enabled(source, SD_EVENT_ONESHOT);
567 return log_error_errno(r, "Failed to enable oneshot mode: %m");
572 static void help(void) {
573 printf("%1$s [HOST:PORT]\n"
575 "Bidirectionally proxy local sockets to another (possibly remote) socket.\n\n"
576 " -h --help Show this help\n"
577 " --version Show package version\n",
578 program_invocation_short_name);
581 static int parse_argv(int argc, char *argv[]) {
588 static const struct option options[] = {
589 { "help", no_argument, NULL, 'h' },
590 { "version", no_argument, NULL, ARG_VERSION },
599 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
608 puts(PACKAGE_STRING);
609 puts(SYSTEMD_FEATURES);
616 assert_not_reached("Unhandled option");
619 if (optind >= argc) {
620 log_error("Not enough parameters.");
624 if (argc != optind+1) {
625 log_error("Too many parameters.");
629 arg_remote_host = argv[optind];
633 int main(int argc, char *argv[]) {
634 Context context = {};
637 log_parse_environment();
640 r = parse_argv(argc, argv);
644 r = sd_event_default(&context.event);
646 log_error_errno(r, "Failed to allocate event loop: %m");
650 r = sd_resolve_default(&context.resolve);
652 log_error_errno(r, "Failed to allocate resolver: %m");
656 r = sd_resolve_attach_event(context.resolve, context.event, 0);
658 log_error_errno(r, "Failed to attach resolver: %m");
662 sd_event_set_watchdog(context.event, true);
664 n = sd_listen_fds(1);
666 log_error("Failed to receive sockets from parent.");
670 log_error("Didn't get any sockets passed in.");
675 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
676 r = add_listen_socket(&context, fd);
681 r = sd_event_loop(context.event);
683 log_error_errno(r, "Failed to run event loop: %m");
688 context_free(&context);
690 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;