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"
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 #define _cleanup_freeaddrinfo_ _cleanup_(freeaddrinfop)
51 DEFINE_TRIVIAL_CLEANUP_FUNC(struct addrinfo *, freeaddrinfo);
53 typedef struct Context {
61 typedef struct Connection {
64 int server_fd, client_fd;
65 int server_to_client_buffer[2]; /* a pipe */
66 int client_to_server_buffer[2]; /* a pipe */
68 size_t server_to_client_buffer_full, client_to_server_buffer_full;
69 size_t server_to_client_buffer_size, client_to_server_buffer_size;
71 sd_event_source *server_event_source, *client_event_source;
73 sd_resolve_query *resolve_query;
76 static void connection_free(Connection *c) {
80 set_remove(c->context->connections, c);
82 sd_event_source_unref(c->server_event_source);
83 sd_event_source_unref(c->client_event_source);
85 safe_close(c->server_fd);
86 safe_close(c->client_fd);
88 safe_close_pair(c->server_to_client_buffer);
89 safe_close_pair(c->client_to_server_buffer);
91 sd_resolve_query_unref(c->resolve_query);
96 static void context_free(Context *context) {
102 while ((es = set_steal_first(context->listen)))
103 sd_event_source_unref(es);
105 while ((c = set_first(context->connections)))
108 set_free(context->listen);
109 set_free(context->connections);
111 sd_event_unref(context->event);
112 sd_resolve_unref(context->resolve);
115 static int connection_create_pipes(Connection *c, int buffer[2], size_t *sz) {
125 r = pipe2(buffer, O_CLOEXEC|O_NONBLOCK);
127 log_error("Failed to allocate pipe buffer: %m");
131 fcntl(buffer[0], F_SETPIPE_SZ, BUFFER_SIZE);
133 r = fcntl(buffer[0], F_GETPIPE_SZ);
135 log_error("Failed to get pipe buffer size: %m");
145 static int connection_shovel(
147 int *from, int buffer[2], int *to,
148 size_t *full, size_t *sz,
149 sd_event_source **from_source, sd_event_source **to_source) {
156 assert(buffer[0] >= 0);
157 assert(buffer[1] >= 0);
169 if (*full < *sz && *from >= 0 && *to >= 0) {
170 z = splice(*from, NULL, buffer[1], NULL, *sz - *full, SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
174 } else if (z == 0 || errno == EPIPE || errno == ECONNRESET) {
175 *from_source = sd_event_source_unref(*from_source);
176 *from = safe_close(*from);
177 } else if (errno != EAGAIN && errno != EINTR) {
178 log_error("Failed to splice: %m");
183 if (*full > 0 && *to >= 0) {
184 z = splice(buffer[0], NULL, *to, NULL, *full, SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
188 } else if (z == 0 || errno == EPIPE || errno == ECONNRESET) {
189 *to_source = sd_event_source_unref(*to_source);
190 *to = safe_close(*to);
191 } else if (errno != EAGAIN && errno != EINTR) {
192 log_error("Failed to splice: %m");
201 static int connection_enable_event_sources(Connection *c);
203 static int traffic_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
204 Connection *c = userdata;
211 r = connection_shovel(c,
212 &c->server_fd, c->server_to_client_buffer, &c->client_fd,
213 &c->server_to_client_buffer_full, &c->server_to_client_buffer_size,
214 &c->server_event_source, &c->client_event_source);
218 r = connection_shovel(c,
219 &c->client_fd, c->client_to_server_buffer, &c->server_fd,
220 &c->client_to_server_buffer_full, &c->client_to_server_buffer_size,
221 &c->client_event_source, &c->server_event_source);
225 /* EOF on both sides? */
226 if (c->server_fd == -1 && c->client_fd == -1)
229 /* Server closed, and all data written to client? */
230 if (c->server_fd == -1 && c->server_to_client_buffer_full <= 0)
233 /* Client closed, and all data written to server? */
234 if (c->client_fd == -1 && c->client_to_server_buffer_full <= 0)
237 r = connection_enable_event_sources(c);
245 return 0; /* ignore errors, continue serving */
248 static int connection_enable_event_sources(Connection *c) {
249 uint32_t a = 0, b = 0;
254 if (c->server_to_client_buffer_full > 0)
256 if (c->server_to_client_buffer_full < c->server_to_client_buffer_size)
259 if (c->client_to_server_buffer_full > 0)
261 if (c->client_to_server_buffer_full < c->client_to_server_buffer_size)
264 if (c->server_event_source)
265 r = sd_event_source_set_io_events(c->server_event_source, a);
266 else if (c->server_fd >= 0)
267 r = sd_event_add_io(c->context->event, &c->server_event_source, c->server_fd, a, traffic_cb, c);
272 log_error("Failed to set up server event source: %s", strerror(-r));
276 if (c->client_event_source)
277 r = sd_event_source_set_io_events(c->client_event_source, b);
278 else if (c->client_fd >= 0)
279 r = sd_event_add_io(c->context->event, &c->client_event_source, c->client_fd, b, traffic_cb, c);
284 log_error("Failed to set up client event source: %s", strerror(-r));
291 static int connection_complete(Connection *c) {
296 r = connection_create_pipes(c, c->server_to_client_buffer, &c->server_to_client_buffer_size);
300 r = connection_create_pipes(c, c->client_to_server_buffer, &c->client_to_server_buffer_size);
304 r = connection_enable_event_sources(c);
312 return 0; /* ignore errors, continue serving */
315 static int connect_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
316 Connection *c = userdata;
324 solen = sizeof(error);
325 r = getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &solen);
327 log_error("Failed to issue SO_ERROR: %m");
332 log_error("Failed to connect to remote host: %s", strerror(error));
336 c->client_event_source = sd_event_source_unref(c->client_event_source);
338 return connection_complete(c);
342 return 0; /* ignore errors, continue serving */
345 static int connection_start(Connection *c, struct sockaddr *sa, socklen_t salen) {
352 c->client_fd = socket(sa->sa_family, SOCK_STREAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0);
353 if (c->client_fd < 0) {
354 log_error("Failed to get remote socket: %m");
358 r = connect(c->client_fd, sa, salen);
360 if (errno == EINPROGRESS) {
361 r = sd_event_add_io(c->context->event, &c->client_event_source, c->client_fd, EPOLLOUT, connect_cb, c);
363 log_error("Failed to add connection socket: %s", strerror(-r));
367 r = sd_event_source_set_enabled(c->client_event_source, SD_EVENT_ONESHOT);
369 log_error("Failed to enable oneshot event source: %s", strerror(-r));
373 log_error("Failed to connect to remote host: %m");
377 r = connection_complete(c);
386 return 0; /* ignore errors, continue serving */
389 static int resolve_cb(sd_resolve_query *q, int ret, const struct addrinfo *ai, void *userdata) {
390 Connection *c = userdata;
396 log_error("Failed to resolve host: %s", gai_strerror(ret));
400 c->resolve_query = sd_resolve_query_unref(c->resolve_query);
402 return connection_start(c, ai->ai_addr, ai->ai_addrlen);
406 return 0; /* ignore errors, continue serving */
409 static int resolve_remote(Connection *c) {
411 static const struct addrinfo hints = {
412 .ai_family = AF_UNSPEC,
413 .ai_socktype = SOCK_STREAM,
414 .ai_flags = AI_ADDRCONFIG
417 union sockaddr_union sa = {};
418 const char *node, *service;
422 if (path_is_absolute(arg_remote_host)) {
423 sa.un.sun_family = AF_UNIX;
424 strncpy(sa.un.sun_path, arg_remote_host, sizeof(sa.un.sun_path)-1);
425 sa.un.sun_path[sizeof(sa.un.sun_path)-1] = 0;
427 salen = offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path);
429 return connection_start(c, &sa.sa, salen);
432 if (arg_remote_host[0] == '@') {
433 sa.un.sun_family = AF_UNIX;
434 sa.un.sun_path[0] = 0;
435 strncpy(sa.un.sun_path+1, arg_remote_host+1, sizeof(sa.un.sun_path)-2);
436 sa.un.sun_path[sizeof(sa.un.sun_path)-1] = 0;
438 salen = offsetof(union sockaddr_union, un.sun_path) + 1 + strlen(sa.un.sun_path + 1);
440 return connection_start(c, &sa.sa, salen);
443 service = strrchr(arg_remote_host, ':');
445 node = strndupa(arg_remote_host, service - arg_remote_host);
448 node = arg_remote_host;
452 log_debug("Looking up address info for %s:%s", node, service);
453 r = sd_resolve_getaddrinfo(c->context->resolve, &c->resolve_query, node, service, &hints, resolve_cb, c);
455 log_error("Failed to resolve remote host: %s", strerror(-r));
463 return 0; /* ignore errors, continue serving */
466 static int add_connection_socket(Context *context, int fd) {
473 if (set_size(context->connections) > CONNECTIONS_MAX) {
474 log_warning("Hit connection limit, refusing connection.");
479 r = set_ensure_allocated(&context->connections, trivial_hash_func, trivial_compare_func);
485 c = new0(Connection, 1);
491 c->context = context;
494 c->server_to_client_buffer[0] = c->server_to_client_buffer[1] = -1;
495 c->client_to_server_buffer[0] = c->client_to_server_buffer[1] = -1;
497 r = set_put(context->connections, c);
504 return resolve_remote(c);
507 static int accept_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
508 _cleanup_free_ char *peer = NULL;
509 Context *context = userdata;
514 assert(revents & EPOLLIN);
517 nfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
519 if (errno != -EAGAIN)
520 log_warning("Failed to accept() socket: %m");
522 getpeername_pretty(nfd, &peer);
523 log_debug("New connection from %s", strna(peer));
525 r = add_connection_socket(context, nfd);
527 log_error("Failed to accept connection, ignoring: %s", strerror(-r));
532 r = sd_event_source_set_enabled(s, SD_EVENT_ONESHOT);
534 log_error("Error while re-enabling listener with ONESHOT: %s", strerror(-r));
535 sd_event_exit(context->event, r);
542 static int add_listen_socket(Context *context, int fd) {
543 sd_event_source *source;
549 r = set_ensure_allocated(&context->listen, trivial_hash_func, trivial_compare_func);
555 r = sd_is_socket(fd, 0, SOCK_STREAM, 1);
557 log_error("Failed to determine socket type: %s", strerror(-r));
561 log_error("Passed in socket is not a stream socket.");
565 r = fd_nonblock(fd, true);
567 log_error("Failed to mark file descriptor non-blocking: %s", strerror(-r));
571 r = sd_event_add_io(context->event, &source, fd, EPOLLIN, accept_cb, context);
573 log_error("Failed to add event source: %s", strerror(-r));
577 r = set_put(context->listen, source);
579 log_error("Failed to add source to set: %s", strerror(-r));
580 sd_event_source_unref(source);
584 /* Set the watcher to oneshot in case other processes are also
585 * watching to accept(). */
586 r = sd_event_source_set_enabled(source, SD_EVENT_ONESHOT);
588 log_error("Failed to enable oneshot mode: %s", strerror(-r));
595 static int help(void) {
597 printf("%s [HOST:PORT]\n"
599 "Bidirectionally proxy local sockets to another (possibly remote) socket.\n\n"
600 " -h --help Show this help\n"
601 " --version Show package version\n",
602 program_invocation_short_name,
603 program_invocation_short_name);
608 static int parse_argv(int argc, char *argv[]) {
615 static const struct option options[] = {
616 { "help", no_argument, NULL, 'h' },
617 { "version", no_argument, NULL, ARG_VERSION },
626 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
634 puts(PACKAGE_STRING);
635 puts(SYSTEMD_FEATURES);
642 assert_not_reached("Unhandled option");
646 if (optind >= argc) {
647 log_error("Not enough parameters.");
651 if (argc != optind+1) {
652 log_error("Too many parameters.");
656 arg_remote_host = argv[optind];
660 int main(int argc, char *argv[]) {
661 Context context = {};
664 log_parse_environment();
667 r = parse_argv(argc, argv);
671 r = sd_event_default(&context.event);
673 log_error("Failed to allocate event loop: %s", strerror(-r));
677 r = sd_resolve_default(&context.resolve);
679 log_error("Failed to allocate resolver: %s", strerror(-r));
683 r = sd_resolve_attach_event(context.resolve, context.event, 0);
685 log_error("Failed to attach resolver: %s", strerror(-r));
689 sd_event_set_watchdog(context.event, true);
691 n = sd_listen_fds(1);
693 log_error("Failed to receive sockets from parent.");
697 log_error("Didn't get any sockets passed in.");
702 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
703 r = add_listen_socket(&context, fd);
708 r = sd_event_loop(context.event);
710 log_error("Failed to run event loop: %s", strerror(-r));
715 context_free(&context);
717 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;