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 return EAGAIN or EWOULDBLOCK. Loop until
127 while (sender->buffer_filled_len > sender->buffer_sent_len) {
128 len = send(receiver->fd, sender->buffer + sender->buffer_sent_len, sender->buffer_filled_len - sender->buffer_sent_len, 0);
129 log_debug("send(%d, ...)=%ld", receiver->fd, len);
131 if (errno != EWOULDBLOCK && errno != EAGAIN) {
132 log_error("Error %d in send to fd=%d: %m", errno, receiver->fd);
136 /* send() is in a would-block state. */
141 /* len < 0 can't occur here. len == 0 is possible but
142 * undefined behavior for nonblocking send(). */
144 sender->buffer_sent_len += len;
147 log_debug("send(%d, ...) completed with %lu bytes still buffered.", receiver->fd, sender->buffer_filled_len - sender->buffer_sent_len);
149 /* Detect a would-block state or partial send. */
150 if (sender->buffer_filled_len > sender->buffer_sent_len) {
152 /* If the buffer is full, disable events coming for recv. */
153 if (sender->buffer_filled_len == BUFFER_SIZE) {
154 r = remove_event_from_connection(sender, EPOLLIN);
156 log_error("Error %d disabling EPOLLIN for fd=%d: %s", r, sender->fd, strerror(-r));
161 /* Watch for when the recipient can be sent data again. */
162 r = add_event_to_connection(receiver, EPOLLOUT);
164 log_error("Error %d enabling EPOLLOUT for fd=%d: %s", r, receiver->fd, strerror(-r));
167 log_debug("Done with recv for fd=%d. Waiting on send for fd=%d.", sender->fd, receiver->fd);
171 /* If we sent everything without any issues (would-block or
172 * partial send), the buffer is now empty. */
173 sender->buffer_filled_len = 0;
174 sender->buffer_sent_len = 0;
176 /* Enable the sender's receive watcher, in case the buffer was
177 * full and we disabled it. */
178 r = add_event_to_connection(sender, EPOLLIN);
180 log_error("Error %d enabling EPOLLIN for fd=%d: %s", r, sender->fd, strerror(-r));
184 /* Disable the other side's send watcher, as we have no data to send now. */
185 r = remove_event_from_connection(receiver, EPOLLOUT);
187 log_error("Error %d disabling EPOLLOUT for fd=%d: %s", r, receiver->fd, strerror(-r));
194 static int transfer_data_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
195 struct connection *c = (struct connection *) userdata;
199 assert(revents & (EPOLLIN | EPOLLOUT));
203 log_debug("Got event revents=%d from fd=%d (conn %p).", revents, fd, c);
205 if (revents & EPOLLIN) {
206 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);
208 /* Receive until the buffer's full, there's no more data,
209 * or the client/server disconnects. */
210 while (c->buffer_filled_len < BUFFER_SIZE) {
211 len = recv(fd, c->buffer + c->buffer_filled_len, BUFFER_SIZE - c->buffer_filled_len, 0);
212 log_debug("recv(%d, ...)=%ld", fd, len);
214 if (errno != EWOULDBLOCK && errno != EAGAIN) {
215 log_error("Error %d in recv from fd=%d: %m", errno, fd);
219 /* recv() is in a blocking state. */
224 log_debug("Clean disconnection from fd=%d", fd);
226 free_connection(c->c_destination);
232 log_debug("Recording that the buffer got %ld more bytes full.", len);
233 c->buffer_filled_len += len;
234 log_debug("Buffer now has %ld bytes full.", c->buffer_filled_len);
237 /* Try sending the data immediately. */
238 return send_buffer(c);
241 return send_buffer(c->c_destination);
247 /* Once sending to the server is ready, set up the real watchers. */
248 static int connected_to_server_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
249 struct sd_event *e = NULL;
250 struct connection *c_server_to_client = (struct connection *) userdata;
251 struct connection *c_client_to_server = c_server_to_client->c_destination;
254 assert(revents & EPOLLOUT);
258 /* Cancel the initial write watcher for the server. */
259 sd_event_source_unref(s);
261 log_debug("Connected to server. Initializing watchers for receiving data.");
263 /* A recv watcher for the server. */
264 r = sd_event_add_io(e, c_server_to_client->fd, EPOLLIN, transfer_data_cb, c_server_to_client, &c_server_to_client->w);
266 log_error("Error %d creating recv watcher for fd=%d: %s", r, c_server_to_client->fd, strerror(-r));
269 c_server_to_client->events = EPOLLIN;
271 /* A recv watcher for the client. */
272 r = sd_event_add_io(e, c_client_to_server->fd, EPOLLIN, transfer_data_cb, c_client_to_server, &c_client_to_server->w);
274 log_error("Error %d creating recv watcher for fd=%d: %s", r, c_client_to_server->fd, strerror(-r));
277 c_client_to_server->events = EPOLLIN;
282 free_connection(c_client_to_server);
283 free_connection(c_server_to_client);
289 static int get_server_connection_fd(const struct proxy *proxy) {
294 if (proxy->remote_is_inet) {
296 _cleanup_freeaddrinfo_ struct addrinfo *result = NULL;
297 struct addrinfo hints = {.ai_family = AF_UNSPEC,
298 .ai_socktype = SOCK_STREAM,
299 .ai_flags = AI_PASSIVE};
301 log_debug("Looking up address info for %s:%s", proxy->remote_host, proxy->remote_service);
302 s = getaddrinfo(proxy->remote_host, proxy->remote_service, &hints, &result);
304 log_error("getaddrinfo error (%d): %s", s, gai_strerror(s));
308 if (result == NULL) {
309 log_error("getaddrinfo: no result");
313 /* @TODO: Try connecting to all results instead of just the first. */
314 server_fd = socket(result->ai_family, result->ai_socktype | SOCK_NONBLOCK, result->ai_protocol);
316 log_error("Error %d creating socket: %m", errno);
320 r = connect(server_fd, result->ai_addr, result->ai_addrlen);
321 /* Ignore EINPROGRESS errors because they're expected for a nonblocking socket. */
322 if (r < 0 && errno != EINPROGRESS) {
323 log_error("Error %d while connecting to socket %s:%s: %m", errno, proxy->remote_host, proxy->remote_service);
328 struct sockaddr_un remote;
330 server_fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0);
332 log_error("Error %d creating socket: %m", errno);
336 remote.sun_family = AF_UNIX;
337 strncpy(remote.sun_path, proxy->remote_host, sizeof(remote.sun_path));
338 len = strlen(remote.sun_path) + sizeof(remote.sun_family);
339 r = connect(server_fd, (struct sockaddr *) &remote, len);
340 if (r < 0 && errno != EINPROGRESS) {
341 log_error("Error %d while connecting to Unix domain socket %s: %m", errno, proxy->remote_host);
346 log_debug("Server connection is fd=%d", server_fd);
350 static int accept_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
351 struct proxy *proxy = (struct proxy *) userdata;
352 struct connection *c_server_to_client;
353 struct connection *c_client_to_server = NULL;
355 union sockaddr_union sa;
356 socklen_t salen = sizeof(sa);
358 assert(revents & EPOLLIN);
360 c_server_to_client = new0(struct connection, 1);
361 if (c_server_to_client == NULL) {
366 c_client_to_server = new0(struct connection, 1);
367 if (c_client_to_server == NULL) {
372 c_server_to_client->fd = get_server_connection_fd(proxy);
373 if (c_server_to_client->fd < 0) {
374 log_error("Error initiating server connection.");
378 c_client_to_server->fd = accept4(fd, (struct sockaddr *) &sa, &salen, SOCK_NONBLOCK|SOCK_CLOEXEC);
379 if (c_client_to_server->fd < 0) {
380 log_error("Error accepting client connection.");
385 if (sa.sa.sa_family == AF_INET || sa.sa.sa_family == AF_INET6) {
386 char sa_str[INET6_ADDRSTRLEN];
389 success = inet_ntop(sa.sa.sa_family, &sa.in6.sin6_addr, sa_str, INET6_ADDRSTRLEN);
391 log_warning("Error %d calling inet_ntop: %m", errno);
393 log_debug("Accepted client connection from %s as fd=%d", sa_str, c_client_to_server->fd);
396 log_debug("Accepted client connection (non-IP) as fd=%d", c_client_to_server->fd);
400 log_debug("Client fd=%d (conn %p) successfully connected. Total clients: %u", c_client_to_server->fd, c_client_to_server, total_clients);
401 log_debug("Server fd=%d (conn %p) successfully initialized.", c_server_to_client->fd, c_server_to_client);
403 /* Initialize watcher for send to server; this shows connectivity. */
404 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);
406 log_error("Error %d creating connectivity watcher for fd=%d: %s", r, c_server_to_client->fd, strerror(-r));
410 /* Allow lookups of the opposite connection. */
411 c_server_to_client->c_destination = c_client_to_server;
412 c_client_to_server->c_destination = c_server_to_client;
417 log_warning("Accepting a client connection or connecting to the server failed.");
418 free_connection(c_client_to_server);
419 free_connection(c_server_to_client);
422 /* Preserve the main loop even if a single proxy setup fails. */
426 static int run_main_loop(struct proxy *proxy) {
427 _cleanup_event_source_unref_ sd_event_source *w_accept = NULL;
428 _cleanup_event_unref_ sd_event *e = NULL;
429 int r = EXIT_SUCCESS;
431 r = sd_event_new(&e);
433 log_error("Failed to allocate event loop: %s", strerror(-r));
437 r = fd_nonblock(proxy->listen_fd, true);
439 log_error("Failed to make listen file descriptor nonblocking: %s", strerror(-r));
443 log_debug("Initializing main listener fd=%d", proxy->listen_fd);
445 r = sd_event_add_io(e, proxy->listen_fd, EPOLLIN, accept_cb, proxy, &w_accept);
447 log_error("Failed to add event IO source: %s", strerror(-r));
451 log_debug("Initialized main listener. Entering loop.");
453 return sd_event_loop(e);
456 static int help(void) {
458 printf("%s hostname-or-ip port-or-service\n"
459 "%s unix-domain-socket-path\n\n"
460 "Inherit a socket. Bidirectionally proxy.\n\n"
461 " -h --help Show this help\n"
462 " --version Print version and exit\n"
463 " --ignore-env Ignore expected systemd environment\n",
464 program_invocation_short_name,
465 program_invocation_short_name);
470 static void version(void) {
471 puts(PACKAGE_STRING " socket-proxyd");
474 static int parse_argv(int argc, char *argv[], struct proxy *p) {
481 static const struct option options[] = {
482 { "help", no_argument, NULL, 'h' },
483 { "version", no_argument, NULL, ARG_VERSION },
484 { "ignore-env", no_argument, NULL, ARG_IGNORE_ENV},
493 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
509 p->ignore_env = true;
513 log_error("Unknown option code %c", c);
518 if (optind + 1 != argc && optind + 2 != argc) {
519 log_error("Incorrect number of positional arguments.");
524 p->remote_host = argv[optind];
525 assert(p->remote_host);
527 p->remote_is_inet = p->remote_host[0] != '/';
529 if (optind == argc - 2) {
530 if (!p->remote_is_inet) {
531 log_error("A port or service is not allowed for Unix socket destinations.");
535 p->remote_service = argv[optind + 1];
536 assert(p->remote_service);
537 } else if (p->remote_is_inet) {
538 log_error("A port or service is required for IP destinations.");
546 int main(int argc, char *argv[]) {
550 log_parse_environment();
553 r = parse_argv(argc, argv, &p);
557 p.listen_fd = SD_LISTEN_FDS_START;
561 n = sd_listen_fds(1);
563 log_error("Found zero inheritable sockets. Are you sure this is running as a socket-activated service?");
567 log_error("Error %d while finding inheritable sockets: %s", n, strerror(-n));
571 log_error("Can't listen on more than one socket.");
577 r = sd_is_socket(p.listen_fd, 0, SOCK_STREAM, 1);
579 log_error("Error %d while checking inherited socket: %s", r, strerror(-r));
583 log_info("Starting the socket activation proxy with listener fd=%d.", p.listen_fd);
585 r = run_main_loop(&p);
588 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;