socket-proxy: several cleanups (#37107)
This commit is contained in:
@@ -12,12 +12,10 @@
|
||||
#define SI_FLAG_FORWARD (INT32_C(1) << 30)
|
||||
#define SI_FLAG_POSITIVE (INT32_C(1) << 29)
|
||||
|
||||
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
|
||||
DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
|
||||
event_source_hash_ops,
|
||||
sd_event_source,
|
||||
(void (*)(const sd_event_source*, struct siphash*)) trivial_hash_func,
|
||||
(int (*)(const sd_event_source*, const sd_event_source*)) trivial_compare_func,
|
||||
sd_event_source_disable_unref);
|
||||
void, trivial_hash_func, trivial_compare_func,
|
||||
sd_event_source, sd_event_source_disable_unref);
|
||||
|
||||
int event_reset_time(
|
||||
sd_event *e,
|
||||
|
||||
+111
-133
@@ -16,6 +16,7 @@
|
||||
#include "alloc-util.h"
|
||||
#include "build.h"
|
||||
#include "daemon-util.h"
|
||||
#include "event-util.h"
|
||||
#include "errno-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "log.h"
|
||||
@@ -58,8 +59,9 @@ typedef struct Connection {
|
||||
sd_resolve_query *resolve_query;
|
||||
} Connection;
|
||||
|
||||
static void connection_free(Connection *c) {
|
||||
assert(c);
|
||||
static Connection* connection_free(Connection *c) {
|
||||
if (!c)
|
||||
return NULL;
|
||||
|
||||
if (c->context)
|
||||
set_remove(c->context->connections, c);
|
||||
@@ -75,7 +77,25 @@ static void connection_free(Connection *c) {
|
||||
|
||||
sd_resolve_query_unref(c->resolve_query);
|
||||
|
||||
free(c);
|
||||
return mfree(c);
|
||||
}
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(Connection*, connection_free);
|
||||
|
||||
DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
|
||||
connection_hash_ops,
|
||||
void, trivial_hash_func, trivial_compare_func,
|
||||
Connection, connection_free);
|
||||
|
||||
static void context_done(Context *context) {
|
||||
assert(context);
|
||||
|
||||
set_free(context->listen);
|
||||
set_free(context->connections);
|
||||
|
||||
sd_event_unref(context->event);
|
||||
sd_resolve_unref(context->resolve);
|
||||
sd_event_source_unref(context->idle_time);
|
||||
}
|
||||
|
||||
static int idle_time_cb(sd_event_source *s, uint64_t usec, void *userdata) {
|
||||
@@ -95,42 +115,26 @@ static int idle_time_cb(sd_event_source *s, uint64_t usec, void *userdata) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int connection_release(Connection *c) {
|
||||
Context *context = ASSERT_PTR(ASSERT_PTR(c)->context);
|
||||
static void context_reset_timer(Context *context) {
|
||||
int r;
|
||||
|
||||
connection_free(c);
|
||||
|
||||
if (arg_exit_idle_time < USEC_INFINITY && set_isempty(context->connections)) {
|
||||
if (context->idle_time) {
|
||||
r = sd_event_source_set_time_relative(context->idle_time, arg_exit_idle_time);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Error while setting idle time: %m");
|
||||
|
||||
r = sd_event_source_set_enabled(context->idle_time, SD_EVENT_ONESHOT);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Error while enabling idle time: %m");
|
||||
} else {
|
||||
r = sd_event_add_time_relative(
|
||||
context->event, &context->idle_time, CLOCK_MONOTONIC,
|
||||
arg_exit_idle_time, 0, idle_time_cb, context);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create idle timer: %m");
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void context_clear(Context *context) {
|
||||
assert(context);
|
||||
|
||||
set_free_with_destructor(context->listen, sd_event_source_unref);
|
||||
set_free_with_destructor(context->connections, connection_free);
|
||||
if (arg_exit_idle_time < USEC_INFINITY && set_isempty(context->connections)) {
|
||||
r = event_reset_time_relative(
|
||||
context->event, &context->idle_time, CLOCK_MONOTONIC,
|
||||
arg_exit_idle_time, 0, idle_time_cb, context,
|
||||
SD_EVENT_PRIORITY_NORMAL, "idle-timer", /* force = */ true);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to reset idle timer, ignoring: %m");
|
||||
}
|
||||
}
|
||||
|
||||
sd_event_unref(context->event);
|
||||
sd_resolve_unref(context->resolve);
|
||||
sd_event_source_unref(context->idle_time);
|
||||
static void connection_release(Connection *c) {
|
||||
assert(c);
|
||||
|
||||
connection_free(c);
|
||||
context_reset_timer(c->context);
|
||||
}
|
||||
|
||||
static int connection_create_pipes(Connection *c, int buffer[static 2], size_t *sz) {
|
||||
@@ -303,34 +307,29 @@ static int connection_complete(Connection *c) {
|
||||
|
||||
r = connection_create_pipes(c, c->server_to_client_buffer, &c->server_to_client_buffer_size);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
return r;
|
||||
|
||||
r = connection_create_pipes(c, c->client_to_server_buffer, &c->client_to_server_buffer_size);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
return r;
|
||||
|
||||
r = connection_enable_event_sources(c);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
connection_release(c);
|
||||
return 0; /* ignore errors, continue serving */
|
||||
}
|
||||
|
||||
static int connect_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
|
||||
Connection *c = ASSERT_PTR(userdata);
|
||||
socklen_t solen;
|
||||
int error, r;
|
||||
int error;
|
||||
|
||||
assert(s);
|
||||
assert(fd >= 0);
|
||||
|
||||
solen = sizeof(error);
|
||||
r = getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &solen);
|
||||
if (r < 0) {
|
||||
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &solen) < 0) {
|
||||
log_error_errno(errno, "Failed to issue SO_ERROR: %m");
|
||||
goto fail;
|
||||
}
|
||||
@@ -342,7 +341,10 @@ static int connect_cb(sd_event_source *s, int fd, uint32_t revents, void *userda
|
||||
|
||||
c->client_event_source = sd_event_source_unref(c->client_event_source);
|
||||
|
||||
return connection_complete(c);
|
||||
if (connection_complete(c) < 0)
|
||||
goto fail;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
connection_release(c);
|
||||
@@ -357,40 +359,26 @@ static int connection_start(Connection *c, struct sockaddr *sa, socklen_t salen)
|
||||
assert(salen);
|
||||
|
||||
c->client_fd = socket(sa->sa_family, SOCK_STREAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0);
|
||||
if (c->client_fd < 0) {
|
||||
log_error_errno(errno, "Failed to get remote socket: %m");
|
||||
goto fail;
|
||||
}
|
||||
if (c->client_fd < 0)
|
||||
return log_error_errno(errno, "Failed to get remote socket: %m");
|
||||
|
||||
r = connect(c->client_fd, sa, salen);
|
||||
if (r < 0) {
|
||||
if (errno == EINPROGRESS) {
|
||||
r = sd_event_add_io(c->context->event, &c->client_event_source, c->client_fd, EPOLLOUT, connect_cb, c);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to add connection socket: %m");
|
||||
goto fail;
|
||||
}
|
||||
if (errno != EINPROGRESS)
|
||||
return log_error_errno(errno, "Failed to connect to remote host: %m");
|
||||
|
||||
r = sd_event_source_set_enabled(c->client_event_source, SD_EVENT_ONESHOT);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to enable oneshot event source: %m");
|
||||
goto fail;
|
||||
}
|
||||
} else {
|
||||
log_error_errno(errno, "Failed to connect to remote host: %m");
|
||||
goto fail;
|
||||
}
|
||||
} else {
|
||||
r = connection_complete(c);
|
||||
r = sd_event_add_io(c->context->event, &c->client_event_source, c->client_fd, EPOLLOUT, connect_cb, c);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
return log_error_errno(r, "Failed to add connection socket: %m");
|
||||
|
||||
r = sd_event_source_set_enabled(c->client_event_source, SD_EVENT_ONESHOT);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to enable oneshot event source: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
connection_release(c);
|
||||
return 0; /* ignore errors, continue serving */
|
||||
return connection_complete(c);
|
||||
}
|
||||
|
||||
static int resolve_handler(sd_resolve_query *q, int ret, const struct addrinfo *ai, Connection *c) {
|
||||
@@ -404,7 +392,10 @@ static int resolve_handler(sd_resolve_query *q, int ret, const struct addrinfo *
|
||||
|
||||
c->resolve_query = sd_resolve_query_unref(c->resolve_query);
|
||||
|
||||
return connection_start(c, ai->ai_addr, ai->ai_addrlen);
|
||||
if (connection_start(c, ai->ai_addr, ai->ai_addrlen) < 0)
|
||||
goto fail;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
connection_release(c);
|
||||
@@ -426,10 +417,8 @@ static int resolve_remote(Connection *c) {
|
||||
int sa_len;
|
||||
|
||||
r = sockaddr_un_set_path(&sa.un, arg_remote_host);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Specified address doesn't fit in an AF_UNIX address, refusing: %m");
|
||||
goto fail;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Specified address doesn't fit in an AF_UNIX address, refusing: %m");
|
||||
sa_len = r;
|
||||
|
||||
return connection_start(c, &sa.sa, sa_len);
|
||||
@@ -447,82 +436,73 @@ static int resolve_remote(Connection *c) {
|
||||
|
||||
log_debug("Looking up address info for %s:%s", node, service);
|
||||
r = resolve_getaddrinfo(c->context->resolve, &c->resolve_query, node, service, &hints, resolve_handler, NULL, c);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to resolve remote host: %m");
|
||||
goto fail;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to resolve remote host: %m");
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
connection_release(c);
|
||||
return 0; /* ignore errors, continue serving */
|
||||
}
|
||||
|
||||
static int add_connection_socket(Context *context, int fd) {
|
||||
Connection *c;
|
||||
static int context_add_connection(Context *context, int fd) {
|
||||
int r;
|
||||
|
||||
assert(context);
|
||||
assert(fd >= 0);
|
||||
|
||||
if (set_size(context->connections) > arg_connections_max) {
|
||||
log_warning("Hit connection limit, refusing connection.");
|
||||
safe_close(fd);
|
||||
return 0;
|
||||
_cleanup_close_ int nfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
|
||||
if (nfd < 0) {
|
||||
if (!ERRNO_IS_ACCEPT_AGAIN(errno))
|
||||
log_warning_errno(errno, "Failed to accept() socket, ignoring: %m");
|
||||
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (DEBUG_LOGGING) {
|
||||
_cleanup_free_ char *peer = NULL;
|
||||
(void) getpeername_pretty(nfd, true, &peer);
|
||||
log_debug("New connection from %s", strna(peer));
|
||||
}
|
||||
|
||||
if (set_size(context->connections) > arg_connections_max)
|
||||
return log_warning_errno(SYNTHETIC_ERRNO(EBUSY), "Hit connection limit, refusing connection.");
|
||||
|
||||
r = sd_event_source_set_enabled(context->idle_time, SD_EVENT_OFF);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Unable to disable idle timer, continuing: %m");
|
||||
|
||||
c = new(Connection, 1);
|
||||
if (!c) {
|
||||
log_oom();
|
||||
return 0;
|
||||
}
|
||||
_cleanup_(connection_freep) Connection *c = new(Connection, 1);
|
||||
if (!c)
|
||||
return log_oom();
|
||||
|
||||
*c = (Connection) {
|
||||
.context = context,
|
||||
.server_fd = fd,
|
||||
.client_fd = -EBADF,
|
||||
.server_to_client_buffer = EBADF_PAIR,
|
||||
.client_to_server_buffer = EBADF_PAIR,
|
||||
.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, NULL, c);
|
||||
if (r < 0) {
|
||||
free(c);
|
||||
log_oom();
|
||||
return 0;
|
||||
}
|
||||
r = set_ensure_put(&context->connections, &connection_hash_ops, c);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
return resolve_remote(c);
|
||||
c->context = context;
|
||||
|
||||
r = resolve_remote(c);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
TAKE_PTR(c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int accept_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
|
||||
_cleanup_free_ char *peer = NULL;
|
||||
Context *context = ASSERT_PTR(userdata);
|
||||
int nfd = -EBADF, r;
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
assert(fd >= 0);
|
||||
assert(revents & EPOLLIN);
|
||||
|
||||
nfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
|
||||
if (nfd < 0) {
|
||||
if (!ERRNO_IS_ACCEPT_AGAIN(errno))
|
||||
log_warning_errno(errno, "Failed to accept() socket: %m");
|
||||
} else {
|
||||
(void) getpeername_pretty(nfd, true, &peer);
|
||||
log_debug("New connection from %s", strna(peer));
|
||||
|
||||
r = add_connection_socket(context, nfd);
|
||||
if (r < 0) {
|
||||
log_warning_errno(r, "Failed to accept connection, ignoring: %m");
|
||||
safe_close(nfd);
|
||||
}
|
||||
}
|
||||
if (context_add_connection(context, fd) < 0)
|
||||
context_reset_timer(context);
|
||||
|
||||
r = sd_event_source_set_enabled(s, SD_EVENT_ONESHOT);
|
||||
if (r < 0)
|
||||
@@ -532,7 +512,6 @@ static int accept_cb(sd_event_source *s, int fd, uint32_t revents, void *userdat
|
||||
}
|
||||
|
||||
static int add_listen_socket(Context *context, int fd) {
|
||||
sd_event_source *source;
|
||||
int r;
|
||||
|
||||
assert(context);
|
||||
@@ -549,16 +528,11 @@ static int add_listen_socket(Context *context, int fd) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to mark file descriptor non-blocking: %m");
|
||||
|
||||
_cleanup_(sd_event_source_unrefp) sd_event_source *source = NULL;
|
||||
r = sd_event_add_io(context->event, &source, fd, EPOLLIN, accept_cb, context);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to add event source: %m");
|
||||
|
||||
r = set_ensure_put(&context->listen, NULL, source);
|
||||
if (r < 0) {
|
||||
sd_event_source_unref(source);
|
||||
return log_error_errno(r, "Failed to add source to set: %m");
|
||||
}
|
||||
|
||||
r = sd_event_source_set_exit_on_failure(source, true);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to enable exit-on-failure logic: %m");
|
||||
@@ -569,6 +543,10 @@ static int add_listen_socket(Context *context, int fd) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to enable oneshot mode: %m");
|
||||
|
||||
r = set_ensure_consume(&context->listen, &event_source_hash_ops, TAKE_PTR(source));
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to add source to set: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -672,7 +650,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
}
|
||||
|
||||
static int run(int argc, char *argv[]) {
|
||||
_cleanup_(context_clear) Context context = {};
|
||||
_cleanup_(context_done) Context context = {};
|
||||
_unused_ _cleanup_(notify_on_cleanup) const char *notify_stop = NULL;
|
||||
int r, n, fd;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user