shared: extract socket_forward_new() helper from socket-proxyd
This commit extracts the socket forwarding code from the existing socket-proxyd into a new shared helper that will be used by the varlinkctl protocol upgrade support code and is used as is in the socket-proxyd.c. It tries to keep the changes as small as possible, its mostly renaming like: * connection_create_pipes -> socket_forward_create_pipes * connection_shovel -> socket_forward_shovel * connection_enable_event_sources -> socket_forward_enable_event_sources * traffic_cb -> socket_forward_traffic_cb and a new socket_forward_new() that creates/starts the forwarding. All log_error_errno() got downgraded to log_debug_errno().
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
#include <netdb.h>
|
||||
#include <stdio.h>
|
||||
@@ -22,12 +21,11 @@
|
||||
#include "pretty-print.h"
|
||||
#include "resolve-private.h"
|
||||
#include "set.h"
|
||||
#include "socket-forward.h"
|
||||
#include "socket-util.h"
|
||||
#include "string-util.h"
|
||||
#include "time-util.h"
|
||||
|
||||
#define BUFFER_SIZE (256 * 1024)
|
||||
|
||||
static unsigned arg_connections_max = 256;
|
||||
static const char *arg_remote_host = NULL;
|
||||
static usec_t arg_exit_idle_time = USEC_INFINITY;
|
||||
@@ -45,13 +43,10 @@ typedef struct Connection {
|
||||
Context *context;
|
||||
|
||||
int server_fd, client_fd;
|
||||
int server_to_client_buffer[2]; /* a pipe */
|
||||
int client_to_server_buffer[2]; /* a pipe */
|
||||
|
||||
size_t server_to_client_buffer_full, client_to_server_buffer_full;
|
||||
size_t server_to_client_buffer_size, client_to_server_buffer_size;
|
||||
sd_event_source *connect_event_source;
|
||||
|
||||
sd_event_source *server_event_source, *client_event_source;
|
||||
SocketForward *forward;
|
||||
|
||||
sd_resolve_query *resolve_query;
|
||||
} Connection;
|
||||
@@ -63,15 +58,12 @@ static Connection* connection_free(Connection *c) {
|
||||
if (c->context)
|
||||
set_remove(c->context->connections, c);
|
||||
|
||||
sd_event_source_unref(c->server_event_source);
|
||||
sd_event_source_unref(c->client_event_source);
|
||||
sd_event_source_unref(c->connect_event_source);
|
||||
socket_forward_free(c->forward);
|
||||
|
||||
safe_close(c->server_fd);
|
||||
safe_close(c->client_fd);
|
||||
|
||||
safe_close_pair(c->server_to_client_buffer);
|
||||
safe_close_pair(c->client_to_server_buffer);
|
||||
|
||||
sd_resolve_query_unref(c->resolve_query);
|
||||
|
||||
return mfree(c);
|
||||
@@ -134,185 +126,29 @@ static void connection_release(Connection *c) {
|
||||
context_reset_timer(context);
|
||||
}
|
||||
|
||||
static int connection_create_pipes(Connection *c, int buffer[static 2], size_t *sz) {
|
||||
int r;
|
||||
|
||||
assert(c);
|
||||
assert(buffer);
|
||||
assert(sz);
|
||||
|
||||
if (buffer[0] >= 0)
|
||||
return 0;
|
||||
|
||||
r = pipe2(buffer, O_CLOEXEC|O_NONBLOCK);
|
||||
if (r < 0)
|
||||
return log_error_errno(errno, "Failed to allocate pipe buffer: %m");
|
||||
|
||||
(void) fcntl(buffer[0], F_SETPIPE_SZ, BUFFER_SIZE);
|
||||
|
||||
r = fcntl(buffer[0], F_GETPIPE_SZ);
|
||||
if (r < 0)
|
||||
return log_error_errno(errno, "Failed to get pipe buffer size: %m");
|
||||
|
||||
assert(r > 0);
|
||||
*sz = r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int connection_shovel(
|
||||
Connection *c,
|
||||
int *from, int buffer[2], int *to,
|
||||
size_t *full, size_t *sz,
|
||||
sd_event_source **from_source, sd_event_source **to_source) {
|
||||
|
||||
bool shoveled;
|
||||
|
||||
assert(c);
|
||||
assert(from);
|
||||
assert(buffer);
|
||||
assert(buffer[0] >= 0);
|
||||
assert(buffer[1] >= 0);
|
||||
assert(to);
|
||||
assert(full);
|
||||
assert(sz);
|
||||
assert(from_source);
|
||||
assert(to_source);
|
||||
|
||||
do {
|
||||
ssize_t z;
|
||||
|
||||
shoveled = false;
|
||||
|
||||
if (*full < *sz && *from >= 0 && *to >= 0) {
|
||||
z = splice(*from, NULL, buffer[1], NULL, *sz - *full, SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
|
||||
if (z > 0) {
|
||||
*full += z;
|
||||
shoveled = true;
|
||||
} else if (z == 0 || ERRNO_IS_DISCONNECT(errno)) {
|
||||
*from_source = sd_event_source_unref(*from_source);
|
||||
*from = safe_close(*from);
|
||||
} else if (!ERRNO_IS_TRANSIENT(errno))
|
||||
return log_error_errno(errno, "Failed to splice: %m");
|
||||
}
|
||||
|
||||
if (*full > 0 && *to >= 0) {
|
||||
z = splice(buffer[0], NULL, *to, NULL, *full, SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
|
||||
if (z > 0) {
|
||||
*full -= z;
|
||||
shoveled = true;
|
||||
} else if (z == 0 || ERRNO_IS_DISCONNECT(errno)) {
|
||||
*to_source = sd_event_source_unref(*to_source);
|
||||
*to = safe_close(*to);
|
||||
} else if (!ERRNO_IS_TRANSIENT(errno))
|
||||
return log_error_errno(errno, "Failed to splice: %m");
|
||||
}
|
||||
} while (shoveled);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int connection_enable_event_sources(Connection *c);
|
||||
|
||||
static int traffic_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
|
||||
static int connection_forward_done(SocketForward *sf, int error, void *userdata) {
|
||||
Connection *c = ASSERT_PTR(userdata);
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
assert(fd >= 0);
|
||||
if (error < 0)
|
||||
log_error_errno(error, "Forwarding failed: %m");
|
||||
|
||||
r = connection_shovel(c,
|
||||
&c->server_fd, c->server_to_client_buffer, &c->client_fd,
|
||||
&c->server_to_client_buffer_full, &c->server_to_client_buffer_size,
|
||||
&c->server_event_source, &c->client_event_source);
|
||||
if (r < 0)
|
||||
goto quit;
|
||||
|
||||
r = connection_shovel(c,
|
||||
&c->client_fd, c->client_to_server_buffer, &c->server_fd,
|
||||
&c->client_to_server_buffer_full, &c->client_to_server_buffer_size,
|
||||
&c->client_event_source, &c->server_event_source);
|
||||
if (r < 0)
|
||||
goto quit;
|
||||
|
||||
/* EOF on both sides? */
|
||||
if (c->server_fd < 0 && c->client_fd < 0)
|
||||
goto quit;
|
||||
|
||||
/* Server closed, and all data written to client? */
|
||||
if (c->server_fd < 0 && c->server_to_client_buffer_full <= 0)
|
||||
goto quit;
|
||||
|
||||
/* Client closed, and all data written to server? */
|
||||
if (c->client_fd < 0 && c->client_to_server_buffer_full <= 0)
|
||||
goto quit;
|
||||
|
||||
r = connection_enable_event_sources(c);
|
||||
if (r < 0)
|
||||
goto quit;
|
||||
|
||||
return 1;
|
||||
|
||||
quit:
|
||||
connection_release(c);
|
||||
return 0; /* ignore errors, continue serving */
|
||||
}
|
||||
|
||||
static int connection_enable_event_sources(Connection *c) {
|
||||
uint32_t a = 0, b = 0;
|
||||
int r;
|
||||
|
||||
assert(c);
|
||||
|
||||
if (c->server_to_client_buffer_full > 0)
|
||||
b |= EPOLLOUT;
|
||||
if (c->server_to_client_buffer_full < c->server_to_client_buffer_size)
|
||||
a |= EPOLLIN;
|
||||
|
||||
if (c->client_to_server_buffer_full > 0)
|
||||
a |= EPOLLOUT;
|
||||
if (c->client_to_server_buffer_full < c->client_to_server_buffer_size)
|
||||
b |= EPOLLIN;
|
||||
|
||||
if (c->server_event_source)
|
||||
r = sd_event_source_set_io_events(c->server_event_source, a);
|
||||
else if (c->server_fd >= 0)
|
||||
r = sd_event_add_io(c->context->event, &c->server_event_source, c->server_fd, a, traffic_cb, c);
|
||||
else
|
||||
r = 0;
|
||||
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set up server event source: %m");
|
||||
|
||||
if (c->client_event_source)
|
||||
r = sd_event_source_set_io_events(c->client_event_source, b);
|
||||
else if (c->client_fd >= 0)
|
||||
r = sd_event_add_io(c->context->event, &c->client_event_source, c->client_fd, b, traffic_cb, c);
|
||||
else
|
||||
r = 0;
|
||||
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set up client event source: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int connection_complete(Connection *c) {
|
||||
int r;
|
||||
|
||||
assert(c);
|
||||
|
||||
r = connection_create_pipes(c, c->server_to_client_buffer, &c->server_to_client_buffer_size);
|
||||
r = socket_forward_new(
|
||||
c->context->event,
|
||||
TAKE_FD(c->server_fd),
|
||||
TAKE_FD(c->client_fd),
|
||||
connection_forward_done, c,
|
||||
&c->forward);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = connection_create_pipes(c, c->client_to_server_buffer, &c->client_to_server_buffer_size);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = connection_enable_event_sources(c);
|
||||
if (r < 0)
|
||||
return r;
|
||||
return log_error_errno(r, "Failed to set up forwarding: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -336,7 +172,7 @@ static int connect_cb(sd_event_source *s, int fd, uint32_t revents, void *userda
|
||||
goto fail;
|
||||
}
|
||||
|
||||
c->client_event_source = sd_event_source_unref(c->client_event_source);
|
||||
c->connect_event_source = sd_event_source_unref(c->connect_event_source);
|
||||
|
||||
if (connection_complete(c) < 0)
|
||||
goto fail;
|
||||
@@ -364,11 +200,11 @@ static int connection_start(Connection *c, struct sockaddr *sa, socklen_t salen)
|
||||
if (errno != EINPROGRESS)
|
||||
return log_error_errno(errno, "Failed to connect to remote host: %m");
|
||||
|
||||
r = sd_event_add_io(c->context->event, &c->client_event_source, c->client_fd, EPOLLOUT, connect_cb, c);
|
||||
r = sd_event_add_io(c->context->event, &c->connect_event_source, c->client_fd, EPOLLOUT, connect_cb, c);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to add connection socket: %m");
|
||||
|
||||
r = sd_event_source_set_enabled(c->client_event_source, SD_EVENT_ONESHOT);
|
||||
r = sd_event_source_set_enabled(c->connect_event_source, SD_EVENT_ONESHOT);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to enable oneshot event source: %m");
|
||||
|
||||
@@ -472,8 +308,6 @@ static int context_add_connection(Context *context, int fd) {
|
||||
*c = (Connection) {
|
||||
.server_fd = TAKE_FD(nfd),
|
||||
.client_fd = -EBADF,
|
||||
.server_to_client_buffer = EBADF_PAIR,
|
||||
.client_to_server_buffer = EBADF_PAIR,
|
||||
};
|
||||
|
||||
r = set_ensure_put(&context->connections, &connection_hash_ops, c);
|
||||
|
||||
Reference in New Issue
Block a user