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/>.
29 #include <sys/socket.h>
33 #include "sd-daemon.h"
35 #include "sd-resolve.h"
37 #include "socket-util.h"
41 #include "path-util.h"
43 #define BUFFER_SIZE (256 * 1024)
44 #define CONNECTIONS_MAX 256
46 static const char *arg_remote_host = NULL;
48 typedef struct Context {
56 typedef struct Connection {
59 int server_fd, client_fd;
60 int server_to_client_buffer[2]; /* a pipe */
61 int client_to_server_buffer[2]; /* a pipe */
63 size_t server_to_client_buffer_full, client_to_server_buffer_full;
64 size_t server_to_client_buffer_size, client_to_server_buffer_size;
66 sd_event_source *server_event_source, *client_event_source;
68 sd_resolve_query *resolve_query;
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 safe_close(c->server_fd);
81 safe_close(c->client_fd);
83 safe_close_pair(c->server_to_client_buffer);
84 safe_close_pair(c->client_to_server_buffer);
86 sd_resolve_query_unref(c->resolve_query);
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);
106 sd_event_unref(context->event);
107 sd_resolve_unref(context->resolve);
110 static int connection_create_pipes(Connection *c, int buffer[2], size_t *sz) {
120 r = pipe2(buffer, O_CLOEXEC|O_NONBLOCK);
122 return log_error_errno(errno, "Failed to allocate pipe buffer: %m");
124 (void) fcntl(buffer[0], F_SETPIPE_SZ, BUFFER_SIZE);
126 r = fcntl(buffer[0], F_GETPIPE_SZ);
128 return log_error_errno(errno, "Failed to get pipe buffer size: %m");
136 static int connection_shovel(
138 int *from, int buffer[2], int *to,
139 size_t *full, size_t *sz,
140 sd_event_source **from_source, sd_event_source **to_source) {
147 assert(buffer[0] >= 0);
148 assert(buffer[1] >= 0);
160 if (*full < *sz && *from >= 0 && *to >= 0) {
161 z = splice(*from, NULL, buffer[1], NULL, *sz - *full, SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
165 } else if (z == 0 || errno == EPIPE || errno == ECONNRESET) {
166 *from_source = sd_event_source_unref(*from_source);
167 *from = safe_close(*from);
168 } else if (errno != EAGAIN && errno != EINTR)
169 return log_error_errno(errno, "Failed to splice: %m");
172 if (*full > 0 && *to >= 0) {
173 z = splice(buffer[0], NULL, *to, NULL, *full, SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
177 } else if (z == 0 || errno == EPIPE || errno == ECONNRESET) {
178 *to_source = sd_event_source_unref(*to_source);
179 *to = safe_close(*to);
180 } else if (errno != EAGAIN && errno != EINTR)
181 return log_error_errno(errno, "Failed to splice: %m");
188 static int connection_enable_event_sources(Connection *c);
190 static int traffic_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
191 Connection *c = userdata;
198 r = connection_shovel(c,
199 &c->server_fd, c->server_to_client_buffer, &c->client_fd,
200 &c->server_to_client_buffer_full, &c->server_to_client_buffer_size,
201 &c->server_event_source, &c->client_event_source);
205 r = connection_shovel(c,
206 &c->client_fd, c->client_to_server_buffer, &c->server_fd,
207 &c->client_to_server_buffer_full, &c->client_to_server_buffer_size,
208 &c->client_event_source, &c->server_event_source);
212 /* EOF on both sides? */
213 if (c->server_fd == -1 && c->client_fd == -1)
216 /* Server closed, and all data written to client? */
217 if (c->server_fd == -1 && c->server_to_client_buffer_full <= 0)
220 /* Client closed, and all data written to server? */
221 if (c->client_fd == -1 && c->client_to_server_buffer_full <= 0)
224 r = connection_enable_event_sources(c);
232 return 0; /* ignore errors, continue serving */
235 static int connection_enable_event_sources(Connection *c) {
236 uint32_t a = 0, b = 0;
241 if (c->server_to_client_buffer_full > 0)
243 if (c->server_to_client_buffer_full < c->server_to_client_buffer_size)
246 if (c->client_to_server_buffer_full > 0)
248 if (c->client_to_server_buffer_full < c->client_to_server_buffer_size)
251 if (c->server_event_source)
252 r = sd_event_source_set_io_events(c->server_event_source, a);
253 else if (c->server_fd >= 0)
254 r = sd_event_add_io(c->context->event, &c->server_event_source, c->server_fd, a, traffic_cb, c);
259 return log_error_errno(r, "Failed to set up server event source: %m");
261 if (c->client_event_source)
262 r = sd_event_source_set_io_events(c->client_event_source, b);
263 else if (c->client_fd >= 0)
264 r = sd_event_add_io(c->context->event, &c->client_event_source, c->client_fd, b, traffic_cb, c);
269 return log_error_errno(r, "Failed to set up client event source: %m");
274 static int connection_complete(Connection *c) {
279 r = connection_create_pipes(c, c->server_to_client_buffer, &c->server_to_client_buffer_size);
283 r = connection_create_pipes(c, c->client_to_server_buffer, &c->client_to_server_buffer_size);
287 r = connection_enable_event_sources(c);
295 return 0; /* ignore errors, continue serving */
298 static int connect_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
299 Connection *c = userdata;
307 solen = sizeof(error);
308 r = getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &solen);
310 log_error_errno(errno, "Failed to issue SO_ERROR: %m");
315 log_error_errno(error, "Failed to connect to remote host: %m");
319 c->client_event_source = sd_event_source_unref(c->client_event_source);
321 return connection_complete(c);
325 return 0; /* ignore errors, continue serving */
328 static int connection_start(Connection *c, struct sockaddr *sa, socklen_t salen) {
335 c->client_fd = socket(sa->sa_family, SOCK_STREAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0);
336 if (c->client_fd < 0) {
337 log_error_errno(errno, "Failed to get remote socket: %m");
341 r = connect(c->client_fd, sa, salen);
343 if (errno == EINPROGRESS) {
344 r = sd_event_add_io(c->context->event, &c->client_event_source, c->client_fd, EPOLLOUT, connect_cb, c);
346 log_error_errno(r, "Failed to add connection socket: %m");
350 r = sd_event_source_set_enabled(c->client_event_source, SD_EVENT_ONESHOT);
352 log_error_errno(r, "Failed to enable oneshot event source: %m");
356 log_error_errno(errno, "Failed to connect to remote host: %m");
360 r = connection_complete(c);
369 return 0; /* ignore errors, continue serving */
372 static int resolve_cb(sd_resolve_query *q, int ret, const struct addrinfo *ai, void *userdata) {
373 Connection *c = userdata;
379 log_error("Failed to resolve host: %s", gai_strerror(ret));
383 c->resolve_query = sd_resolve_query_unref(c->resolve_query);
385 return connection_start(c, ai->ai_addr, ai->ai_addrlen);
389 return 0; /* ignore errors, continue serving */
392 static int resolve_remote(Connection *c) {
394 static const struct addrinfo hints = {
395 .ai_family = AF_UNSPEC,
396 .ai_socktype = SOCK_STREAM,
397 .ai_flags = AI_ADDRCONFIG
400 union sockaddr_union sa = {};
401 const char *node, *service;
405 if (path_is_absolute(arg_remote_host)) {
406 sa.un.sun_family = AF_UNIX;
407 strncpy(sa.un.sun_path, arg_remote_host, sizeof(sa.un.sun_path)-1);
408 sa.un.sun_path[sizeof(sa.un.sun_path)-1] = 0;
410 salen = offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path);
412 return connection_start(c, &sa.sa, salen);
415 if (arg_remote_host[0] == '@') {
416 sa.un.sun_family = AF_UNIX;
417 sa.un.sun_path[0] = 0;
418 strncpy(sa.un.sun_path+1, arg_remote_host+1, sizeof(sa.un.sun_path)-2);
419 sa.un.sun_path[sizeof(sa.un.sun_path)-1] = 0;
421 salen = offsetof(union sockaddr_union, un.sun_path) + 1 + strlen(sa.un.sun_path + 1);
423 return connection_start(c, &sa.sa, salen);
426 service = strrchr(arg_remote_host, ':');
428 node = strndupa(arg_remote_host, service - arg_remote_host);
431 node = arg_remote_host;
435 log_debug("Looking up address info for %s:%s", node, service);
436 r = sd_resolve_getaddrinfo(c->context->resolve, &c->resolve_query, node, service, &hints, resolve_cb, c);
438 log_error_errno(r, "Failed to resolve remote host: %m");
446 return 0; /* ignore errors, continue serving */
449 static int add_connection_socket(Context *context, int fd) {
456 if (set_size(context->connections) > CONNECTIONS_MAX) {
457 log_warning("Hit connection limit, refusing connection.");
462 r = set_ensure_allocated(&context->connections, NULL);
468 c = new0(Connection, 1);
474 c->context = context;
477 c->server_to_client_buffer[0] = c->server_to_client_buffer[1] = -1;
478 c->client_to_server_buffer[0] = c->client_to_server_buffer[1] = -1;
480 r = set_put(context->connections, c);
487 return resolve_remote(c);
490 static int accept_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
491 _cleanup_free_ char *peer = NULL;
492 Context *context = userdata;
497 assert(revents & EPOLLIN);
500 nfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
502 if (errno != -EAGAIN)
503 log_warning_errno(errno, "Failed to accept() socket: %m");
505 getpeername_pretty(nfd, &peer);
506 log_debug("New connection from %s", strna(peer));
508 r = add_connection_socket(context, nfd);
510 log_error_errno(r, "Failed to accept connection, ignoring: %m");
515 r = sd_event_source_set_enabled(s, SD_EVENT_ONESHOT);
517 log_error_errno(r, "Error while re-enabling listener with ONESHOT: %m");
518 sd_event_exit(context->event, r);
525 static int add_listen_socket(Context *context, int fd) {
526 sd_event_source *source;
532 r = set_ensure_allocated(&context->listen, NULL);
538 r = sd_is_socket(fd, 0, SOCK_STREAM, 1);
540 return log_error_errno(r, "Failed to determine socket type: %m");
542 log_error("Passed in socket is not a stream socket.");
546 r = fd_nonblock(fd, true);
548 return log_error_errno(r, "Failed to mark file descriptor non-blocking: %m");
550 r = sd_event_add_io(context->event, &source, fd, EPOLLIN, accept_cb, context);
552 return log_error_errno(r, "Failed to add event source: %m");
554 r = set_put(context->listen, source);
556 log_error_errno(r, "Failed to add source to set: %m");
557 sd_event_source_unref(source);
561 /* Set the watcher to oneshot in case other processes are also
562 * watching to accept(). */
563 r = sd_event_source_set_enabled(source, SD_EVENT_ONESHOT);
565 return log_error_errno(r, "Failed to enable oneshot mode: %m");
570 static void help(void) {
571 printf("%1$s [HOST:PORT]\n"
573 "Bidirectionally proxy local sockets to another (possibly remote) socket.\n\n"
574 " -h --help Show this help\n"
575 " --version Show package version\n",
576 program_invocation_short_name);
579 static int parse_argv(int argc, char *argv[]) {
586 static const struct option options[] = {
587 { "help", no_argument, NULL, 'h' },
588 { "version", no_argument, NULL, ARG_VERSION },
597 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
606 puts(PACKAGE_STRING);
607 puts(SYSTEMD_FEATURES);
614 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 Context context = {};
635 log_parse_environment();
638 r = parse_argv(argc, argv);
642 r = sd_event_default(&context.event);
644 log_error_errno(r, "Failed to allocate event loop: %m");
648 r = sd_resolve_default(&context.resolve);
650 log_error_errno(r, "Failed to allocate resolver: %m");
654 r = sd_resolve_attach_event(context.resolve, context.event, 0);
656 log_error_errno(r, "Failed to attach resolver: %m");
660 sd_event_set_watchdog(context.event, true);
662 n = sd_listen_fds(1);
664 log_error("Failed to receive sockets from parent.");
668 log_error("Didn't get any sockets passed in.");
673 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
674 r = add_listen_socket(&context, fd);
679 r = sd_event_loop(context.event);
681 log_error_errno(r, "Failed to run event loop: %m");
686 context_free(&context);
688 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;