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"
41 #define BUFFER_SIZE 16384
42 #define _cleanup_freeaddrinfo_ _cleanup_(freeaddrinfop)
44 unsigned int total_clients = 0;
46 DEFINE_TRIVIAL_CLEANUP_FUNC(struct addrinfo *, freeaddrinfo);
52 const char *remote_host;
53 const char *remote_service;
60 struct connection *c_destination;
61 size_t buffer_filled_len;
62 size_t buffer_sent_len;
63 char buffer[BUFFER_SIZE];
66 static void free_connection(struct connection *c) {
67 log_debug("Freeing fd=%d (conn %p).", c->fd, c);
68 sd_event_source_unref(c->w);
69 close_nointr_nofail(c->fd);
73 static int add_event_to_connection(struct connection *c, uint32_t events) {
76 log_debug("Have revents=%d. Adding revents=%d.", c->events, events);
80 r = sd_event_source_set_io_events(c->w, c->events);
82 log_error("Error %d setting revents: %s", r, strerror(-r));
86 r = sd_event_source_set_enabled(c->w, SD_EVENT_ON);
88 log_error("Error %d enabling source: %s", r, strerror(-r));
95 static int remove_event_from_connection(struct connection *c, uint32_t events) {
98 log_debug("Have revents=%d. Removing revents=%d.", c->events, events);
100 c->events &= ~events;
102 r = sd_event_source_set_io_events(c->w, c->events);
104 log_error("Error %d setting revents: %s", r, strerror(-r));
108 if (c->events == 0) {
109 r = sd_event_source_set_enabled(c->w, SD_EVENT_OFF);
111 log_error("Error %d disabling source: %s", r, strerror(-r));
119 static int send_buffer(struct connection *sender) {
120 struct connection *receiver = sender->c_destination;
124 /* We cannot assume that even a partial send() indicates that
125 * the next send() will block. Loop until it does. */
126 while (sender->buffer_filled_len > sender->buffer_sent_len) {
127 len = send(receiver->fd, sender->buffer + sender->buffer_sent_len, sender->buffer_filled_len - sender->buffer_sent_len, 0);
128 log_debug("send(%d, ...)=%ld", receiver->fd, len);
130 if (errno != EWOULDBLOCK && errno != EAGAIN) {
131 log_error("Error %d in send to fd=%d: %m", errno, receiver->fd);
135 /* send() is in a blocking state. */
140 /* len < 0 can't occur here. len == 0 is possible but
141 * undefined behavior for nonblocking send(). */
143 sender->buffer_sent_len += len;
146 log_debug("send(%d, ...) completed with %lu bytes still buffered.", receiver->fd, sender->buffer_filled_len - sender->buffer_sent_len);
148 /* Detect a would-block state or partial send. */
149 if (sender->buffer_filled_len > sender->buffer_sent_len) {
151 /* If the buffer is full, disable events coming for recv. */
152 if (sender->buffer_filled_len == BUFFER_SIZE) {
153 r = remove_event_from_connection(sender, EPOLLIN);
155 log_error("Error %d disabling EPOLLIN for fd=%d: %s", r, sender->fd, strerror(-r));
160 /* Watch for when the recipient can be sent data again. */
161 r = add_event_to_connection(receiver, EPOLLOUT);
163 log_error("Error %d enabling EPOLLOUT for fd=%d: %s", r, receiver->fd, strerror(-r));
166 log_debug("Done with recv for fd=%d. Waiting on send for fd=%d.", sender->fd, receiver->fd);
170 /* If we sent everything without blocking, the buffer is now empty. */
171 sender->buffer_filled_len = 0;
172 sender->buffer_sent_len = 0;
174 /* Enable the sender's receive watcher, in case the buffer was
175 * full and we disabled it. */
176 r = add_event_to_connection(sender, EPOLLIN);
178 log_error("Error %d enabling EPOLLIN for fd=%d: %s", r, sender->fd, strerror(-r));
182 /* Disable the other side's send watcher, as we have no data to send now. */
183 r = remove_event_from_connection(receiver, EPOLLOUT);
185 log_error("Error %d disabling EPOLLOUT for fd=%d: %s", r, receiver->fd, strerror(-r));
192 static int transfer_data_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
193 struct connection *c = (struct connection *) userdata;
197 assert(revents & (EPOLLIN | EPOLLOUT));
201 log_debug("Got event revents=%d from fd=%d (conn %p).", revents, fd, c);
203 if (revents & EPOLLIN) {
204 log_debug("About to recv up to %lu bytes from fd=%d (%lu/BUFFER_SIZE).", BUFFER_SIZE - c->buffer_filled_len, fd, c->buffer_filled_len);
206 /* Receive until the buffer's full, there's no more data,
207 * or the client/server disconnects. */
208 while (c->buffer_filled_len < BUFFER_SIZE) {
209 len = recv(fd, c->buffer + c->buffer_filled_len, BUFFER_SIZE - c->buffer_filled_len, 0);
210 log_debug("recv(%d, ...)=%ld", fd, len);
212 if (errno != EWOULDBLOCK && errno != EAGAIN) {
213 log_error("Error %d in recv from fd=%d: %m", errno, fd);
217 /* recv() is in a blocking state. */
222 log_debug("Clean disconnection from fd=%d", fd);
224 free_connection(c->c_destination);
230 log_debug("Recording that the buffer got %ld more bytes full.", len);
231 c->buffer_filled_len += len;
232 log_debug("Buffer now has %ld bytes full.", c->buffer_filled_len);
235 /* Try sending the data immediately. */
236 return send_buffer(c);
239 return send_buffer(c->c_destination);
245 /* Once sending to the server is unblocked, set up the real watchers. */
246 static int connected_to_server_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
247 struct sd_event *e = NULL;
248 struct connection *c_server_to_client = (struct connection *) userdata;
249 struct connection *c_client_to_server = c_server_to_client->c_destination;
252 assert(revents & EPOLLOUT);
256 /* Cancel the initial write watcher for the server. */
257 sd_event_source_unref(s);
259 log_debug("Connected to server. Initializing watchers for receiving data.");
261 /* A recv watcher for the server. */
262 r = sd_event_add_io(e, c_server_to_client->fd, EPOLLIN, transfer_data_cb, c_server_to_client, &c_server_to_client->w);
264 log_error("Error %d creating recv watcher for fd=%d: %s", r, c_server_to_client->fd, strerror(-r));
267 c_server_to_client->events = EPOLLIN;
269 /* A recv watcher for the client. */
270 r = sd_event_add_io(e, c_client_to_server->fd, EPOLLIN, transfer_data_cb, c_client_to_server, &c_client_to_server->w);
272 log_error("Error %d creating recv watcher for fd=%d: %s", r, c_client_to_server->fd, strerror(-r));
275 c_client_to_server->events = EPOLLIN;
280 free_connection(c_client_to_server);
281 free_connection(c_server_to_client);
287 static int get_server_connection_fd(const struct proxy *proxy) {
292 if (proxy->remote_is_inet) {
294 _cleanup_freeaddrinfo_ struct addrinfo *result = NULL;
295 struct addrinfo hints = {.ai_family = AF_UNSPEC,
296 .ai_socktype = SOCK_STREAM,
297 .ai_flags = AI_PASSIVE};
299 log_debug("Looking up address info for %s:%s", proxy->remote_host, proxy->remote_service);
300 s = getaddrinfo(proxy->remote_host, proxy->remote_service, &hints, &result);
302 log_error("getaddrinfo error (%d): %s", s, gai_strerror(s));
306 if (result == NULL) {
307 log_error("getaddrinfo: no result");
311 /* @TODO: Try connecting to all results instead of just the first. */
312 server_fd = socket(result->ai_family, result->ai_socktype | SOCK_NONBLOCK, result->ai_protocol);
314 log_error("Error %d creating socket: %m", errno);
318 r = connect(server_fd, result->ai_addr, result->ai_addrlen);
319 /* Ignore EINPROGRESS errors because they're expected for a nonblocking socket. */
320 if (r < 0 && errno != EINPROGRESS) {
321 log_error("Error %d while connecting to socket %s:%s: %m", errno, proxy->remote_host, proxy->remote_service);
326 struct sockaddr_un remote;
328 server_fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0);
330 log_error("Error %d creating socket: %m", errno);
334 remote.sun_family = AF_UNIX;
335 strncpy(remote.sun_path, proxy->remote_host, sizeof(remote.sun_path));
336 len = strlen(remote.sun_path) + sizeof(remote.sun_family);
337 r = connect(server_fd, (struct sockaddr *) &remote, len);
338 if (r < 0 && errno != EINPROGRESS) {
339 log_error("Error %d while connecting to Unix domain socket %s: %m", errno, proxy->remote_host);
344 log_debug("Server connection is fd=%d", server_fd);
348 static int accept_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
349 struct proxy *proxy = (struct proxy *) userdata;
350 struct connection *c_server_to_client;
351 struct connection *c_client_to_server = NULL;
353 union sockaddr_union sa;
354 socklen_t salen = sizeof(sa);
356 assert(revents & EPOLLIN);
358 c_server_to_client = new0(struct connection, 1);
359 if (c_server_to_client == NULL) {
364 c_client_to_server = new0(struct connection, 1);
365 if (c_client_to_server == NULL) {
370 c_server_to_client->fd = get_server_connection_fd(proxy);
371 if (c_server_to_client->fd < 0) {
372 log_error("Error initiating server connection.");
376 c_client_to_server->fd = accept4(fd, (struct sockaddr *) &sa, &salen, SOCK_NONBLOCK|SOCK_CLOEXEC);
377 if (c_client_to_server->fd < 0) {
378 log_error("Error accepting client connection.");
383 if (sa.sa.sa_family == AF_INET || sa.sa.sa_family == AF_INET6) {
384 char sa_str[INET6_ADDRSTRLEN];
387 success = inet_ntop(sa.sa.sa_family, &sa.in6.sin6_addr, sa_str, INET6_ADDRSTRLEN);
389 log_warning("Error %d calling inet_ntop: %m", errno);
391 log_debug("Accepted client connection from %s as fd=%d", sa_str, c_client_to_server->fd);
394 log_debug("Accepted client connection (non-IP) as fd=%d", c_client_to_server->fd);
398 log_debug("Client fd=%d (conn %p) successfully connected. Total clients: %u", c_client_to_server->fd, c_client_to_server, total_clients);
399 log_debug("Server fd=%d (conn %p) successfully initialized.", c_server_to_client->fd, c_server_to_client);
401 /* Initialize watcher for send to server; this shows connectivity. */
402 r = sd_event_add_io(sd_event_get(s), c_server_to_client->fd, EPOLLOUT, connected_to_server_cb, c_server_to_client, &c_server_to_client->w);
404 log_error("Error %d creating connectivity watcher for fd=%d: %s", r, c_server_to_client->fd, strerror(-r));
408 /* Allow lookups of the opposite connection. */
409 c_server_to_client->c_destination = c_client_to_server;
410 c_client_to_server->c_destination = c_server_to_client;
415 log_warning("Accepting a client connection or connecting to the server failed.");
416 free_connection(c_client_to_server);
417 free_connection(c_server_to_client);
420 /* Preserve the main loop even if a single proxy setup fails. */
424 static int run_main_loop(struct proxy *proxy) {
425 _cleanup_event_source_unref_ sd_event_source *w_accept = NULL;
426 _cleanup_event_unref_ sd_event *e = NULL;
427 int r = EXIT_SUCCESS;
429 r = sd_event_new(&e);
431 log_error("Failed to allocate event loop: %s", strerror(-r));
435 r = fd_nonblock(proxy->listen_fd, true);
437 log_error("Failed to make listen file descriptor non-blocking: %s", strerror(-r));
441 log_debug("Initializing main listener fd=%d", proxy->listen_fd);
443 r = sd_event_add_io(e, proxy->listen_fd, EPOLLIN, accept_cb, proxy, &w_accept);
445 log_error("Failed to add event IO source: %s", strerror(-r));
449 log_debug("Initialized main listener. Entering loop.");
451 return sd_event_loop(e);
454 static int help(void) {
456 printf("%s hostname-or-ip port-or-service\n"
457 "%s unix-domain-socket-path\n\n"
458 "Inherit a socket. Bidirectionally proxy.\n\n"
459 " -h --help Show this help\n"
460 " --version Print version and exit\n"
461 " --ignore-env Ignore expected systemd environment\n",
462 program_invocation_short_name,
463 program_invocation_short_name);
468 static void version(void) {
469 puts(PACKAGE_STRING " socket-proxyd");
472 static int parse_argv(int argc, char *argv[], struct proxy *p) {
479 static const struct option options[] = {
480 { "help", no_argument, NULL, 'h' },
481 { "version", no_argument, NULL, ARG_VERSION },
482 { "ignore-env", no_argument, NULL, ARG_IGNORE_ENV},
491 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
507 p->ignore_env = true;
511 log_error("Unknown option code %c", c);
516 if (optind + 1 != argc && optind + 2 != argc) {
517 log_error("Incorrect number of positional arguments.");
522 p->remote_host = argv[optind];
523 assert(p->remote_host);
525 p->remote_is_inet = p->remote_host[0] != '/';
527 if (optind == argc - 2) {
528 if (!p->remote_is_inet) {
529 log_error("A port or service is not allowed for Unix socket destinations.");
533 p->remote_service = argv[optind + 1];
534 assert(p->remote_service);
535 } else if (p->remote_is_inet) {
536 log_error("A port or service is required for IP destinations.");
544 int main(int argc, char *argv[]) {
548 log_parse_environment();
551 r = parse_argv(argc, argv, &p);
555 p.listen_fd = SD_LISTEN_FDS_START;
559 n = sd_listen_fds(1);
561 log_error("Found zero inheritable sockets. Are you sure this is running as a socket-activated service?");
565 log_error("Error %d while finding inheritable sockets: %s", n, strerror(-n));
569 log_error("Can't listen on more than one socket.");
575 /* @TODO: Check if this proxy can work with datagram sockets. */
576 r = sd_is_socket(p.listen_fd, 0, SOCK_STREAM, 1);
578 log_error("Error %d while checking inherited socket: %s", r, strerror(-r));
582 log_info("Starting the socket activation proxy with listener fd=%d.", p.listen_fd);
584 r = run_main_loop(&p);
587 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;