Apply PR #143 content on top of latest main

This commit is contained in:
2026-07-29 18:18:07 +02:00
parent 3b621e591a
commit e68e33c0c5
33 changed files with 1114 additions and 1145 deletions
+78 -166
View File
@@ -3,36 +3,25 @@
#include "protocol.h"
#include <arpa/inet.h>
#include <errno.h>
#include <netdb.h>
#include <openssl/ssl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
bool set_socket_timeouts(int fd) {
struct timeval tv;
tv.tv_sec = 30;
tv.tv_usec = 0;
static volatile sig_atomic_t g_active_connections = 0;
int keepalive = 1;
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &keepalive, sizeof(keepalive)) < 0) {
perror("Could not set SO_KEEPALIVE");
return false;
static void sigchld_handler(int sig) {
(void)sig;
int saved_errno = errno;
while (waitpid(-1, NULL, WNOHANG) > 0) {
if (g_active_connections > 0)
g_active_connections--;
}
if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) {
perror("Could not set SO_RCVTIMEO");
return false;
}
if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0) {
perror("Could not set SO_SNDTIMEO");
return false;
}
return true;
errno = saved_errno;
}
Server* server_create(int port) {
@@ -41,29 +30,14 @@ Server* server_create(int port) {
perror("Could not allocate space for Server");
return NULL;
}
memset(&server->address, 0, sizeof(server->address));
// Try IPv6 first, fall back to IPv4
int fd = socket(AF_INET6, SOCK_STREAM, 0);
sa_family_t domain = AF_INET6;
if (fd < 0) {
fd = socket(AF_INET, SOCK_STREAM, 0);
domain = AF_INET;
}
if (fd < 0) {
int file_descriptor = socket(AF_INET, SOCK_STREAM, 0);
if (file_descriptor < 0) {
perror("Could not create Socket!");
free(server);
return NULL;
}
if (!set_socket_timeouts(fd)) {
close(fd);
free(server);
return NULL;
}
server->file_descriptor = fd;
server->ssl_ctx = NULL;
server->file_descriptor = file_descriptor;
int opt = 1;
if (setsockopt(server->file_descriptor, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) {
perror("Error setting a socket option!");
@@ -72,60 +46,20 @@ Server* server_create(int port) {
return NULL;
}
// Use the domain from the socket we actually created
struct sockaddr_storage* addr = &server->address;
struct sockaddr_in* addr4 = (struct sockaddr_in*)addr;
struct sockaddr_in6* addr6 = (struct sockaddr_in6*)addr;
if (domain == AF_INET6) {
addr6->sin6_family = AF_INET6;
addr6->sin6_addr = in6addr_any;
addr6->sin6_port = htons(port);
addr->ss_family = AF_INET6;
server->address_length = sizeof(struct sockaddr_in6);
} else {
addr4->sin_family = AF_INET;
addr4->sin_addr.s_addr = INADDR_ANY;
addr4->sin_port = htons(port);
addr->ss_family = AF_INET;
server->address_length = sizeof(struct sockaddr_in);
}
server->address.sin_family = AF_INET;
server->address.sin_addr.s_addr = INADDR_ANY;
server->address.sin_port = htons(port);
server->address_length = sizeof(server->address);
server->ssl_ctx = NULL;
server->max_connections = 100;
server->active_connections = 0;
if (bind(server->file_descriptor, (struct sockaddr*)&server->address, server->address_length) <
0) {
// If IPv6 bind failed (maybe no IPv6), try IPv4
if (domain == AF_INET6) {
close(fd);
fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0) {
perror("Could not create IPv4 Socket!");
free(server);
return NULL;
}
if (!set_socket_timeouts(fd)) {
close(fd);
free(server);
return NULL;
}
server->file_descriptor = fd;
setsockopt(server->file_descriptor, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
memset(addr, 0, sizeof(*addr));
addr4->sin_family = AF_INET;
addr4->sin_addr.s_addr = INADDR_ANY;
addr4->sin_port = htons(port);
server->address_length = sizeof(struct sockaddr_in);
if (bind(server->file_descriptor, (struct sockaddr*)addr, server->address_length) < 0) {
perror("Could not bind server");
close(server->file_descriptor);
free(server);
return NULL;
}
} else {
perror("Could not bind server");
close(server->file_descriptor);
free(server);
return NULL;
}
perror("Could not bind server");
close(server->file_descriptor);
free(server);
return NULL;
}
return server;
@@ -143,36 +77,27 @@ void server_delete(Server** server) {
*server = NULL;
}
/* Flag set by server_request_shutdown() to request graceful shutdown
of the accept loop. Accessed only from transport_tcp.c so it won't
cause linker errors when this file is compiled into client/test targets. */
static volatile sig_atomic_t g_tcp_cleanup_requested = 0;
void server_request_shutdown(void) {
g_tcp_cleanup_requested = 1;
}
static void accept_loop(Server* server, void (*child_fn)(int, void*), void* child_ctx,
const char* log_fmt) {
if (listen(server->file_descriptor, SOMAXCONN) < 0) {
perror("Could not listen on port!");
return;
}
signal(SIGCHLD, SIG_IGN);
while (!g_tcp_cleanup_requested) {
struct sockaddr_storage client_addr;
signal(SIGCHLD, sigchld_handler);
while (1) {
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
int fd = accept(server->file_descriptor, (struct sockaddr*)&client_addr, &client_len);
if (fd < 0) {
if (errno == EINTR) {
if (g_tcp_cleanup_requested)
break;
continue;
}
perror("Could not accept the connection");
continue;
}
set_socket_timeouts(fd);
if ((unsigned int)g_active_connections >= server->max_connections) {
log_message(LOG_LEVEL_WARNING, "Max connections (%u) reached, rejecting",
server->max_connections);
close(fd);
continue;
}
log_message(LOG_LEVEL_INFO, "%s", log_fmt);
pid_t pid = fork();
if (pid == 0) {
@@ -180,6 +105,8 @@ static void accept_loop(Server* server, void (*child_fn)(int, void*), void* chil
child_fn(fd, child_ctx);
close(fd);
_exit(0);
} else if (pid > 0) {
g_active_connections++;
}
close(fd);
}
@@ -194,11 +121,7 @@ static void plain_child_fn(int fd, void* ctx) {
}
bool server_listen(Server* server, void (*handler)(int file_descriptor)) {
struct sockaddr_in* addr4 = (struct sockaddr_in*)&server->address;
int port = (server->address.ss_family == AF_INET6)
? ntohs(((struct sockaddr_in6*)&server->address)->sin6_port)
: ntohs(addr4->sin_port);
log_message(LOG_LEVEL_INFO, "Start Listening on Port: %d", port);
log_message(LOG_LEVEL_INFO, "Start Listening on Port: %d", ntohs(server->address.sin_port));
struct plain_ctx ctx = {handler};
accept_loop(server, plain_child_fn, &ctx, "Received Connection");
return true;
@@ -206,23 +129,43 @@ bool server_listen(Server* server, void (*handler)(int file_descriptor)) {
void server_accept_loop(Server* server, void (*child_fn)(int, void*), void* child_ctx,
const char* log_fmt) {
struct sockaddr_in* addr4 = (struct sockaddr_in*)&server->address;
int port = (server->address.ss_family == AF_INET6)
? ntohs(((struct sockaddr_in6*)&server->address)->sin6_port)
: ntohs(addr4->sin_port);
log_message(LOG_LEVEL_INFO, "Start TLS Listening on Port: %d", port);
log_message(LOG_LEVEL_INFO, "Start TLS Listening on Port: %d", ntohs(server->address.sin_port));
accept_loop(server, child_fn, child_ctx, log_fmt);
}
static int g_timeout_sec = 30;
static int g_contimeout_sec = 10;
void tcp_set_timeouts(int timeout_sec, int contimeout_sec) {
if (timeout_sec > 0)
g_timeout_sec = timeout_sec;
if (contimeout_sec > 0)
g_contimeout_sec = contimeout_sec;
}
static void tcp_apply_socket_timeout(int fd) {
struct timeval tv;
tv.tv_sec = g_timeout_sec;
tv.tv_usec = 0;
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
}
Client* client_create() {
Client* client = (Client*)malloc(sizeof(Client));
if (client == NULL) {
int file_descriptor = socket(AF_INET, SOCK_STREAM, 0);
if (file_descriptor < 0) {
perror("Could not create Socket!");
return NULL;
}
memset(&client->address, 0, sizeof(client->address));
client->address.ss_family = AF_UNSPEC;
Client* client = (Client*)malloc(sizeof(Client));
if (client == NULL) {
close(file_descriptor);
return NULL;
}
client->file_descriptor = file_descriptor;
client->address.sin_family = AF_INET;
client->address_length = sizeof(client->address);
client->file_descriptor = -1;
client->ssh_child_pid = -1;
client->ssl = NULL;
client->ssl_ctx = NULL;
@@ -230,55 +173,28 @@ Client* client_create() {
}
bool client_connect(Client* client, char* host, int port) {
struct addrinfo hints, *res, *rp;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
client->address.sin_port = htons(port);
client->address.sin_family = AF_INET;
client->address_length = sizeof(client->address);
char port_str[16];
snprintf(port_str, sizeof(port_str), "%d", port);
int gai_err = getaddrinfo(host, port_str, &hints, &res);
if (gai_err != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(gai_err));
if (inet_pton(AF_INET, host, &client->address.sin_addr) <= 0) {
perror("Could not convert host address!");
return false;
}
// Try IPv6 first, then IPv4
int fd = -1;
for (rp = res; rp != NULL; rp = rp->ai_next) {
fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (fd < 0)
continue;
if (!set_socket_timeouts(fd)) {
close(fd);
fd = -1;
continue;
}
if (connect(fd, rp->ai_addr, rp->ai_addrlen) == 0)
break;
close(fd);
fd = -1;
}
struct timeval ct;
ct.tv_sec = g_contimeout_sec;
ct.tv_usec = 0;
setsockopt(client->file_descriptor, SOL_SOCKET, SO_RCVTIMEO, &ct, sizeof(ct));
setsockopt(client->file_descriptor, SOL_SOCKET, SO_SNDTIMEO, &ct, sizeof(ct));
if (fd < 0) {
if (connect(client->file_descriptor, (struct sockaddr*)&client->address, client->address_length) <
0) {
perror("Could not connect to Server!");
freeaddrinfo(res);
return false;
}
// Save the connected address
socklen_t addr_len = rp->ai_addrlen;
if (addr_len > sizeof(client->address))
addr_len = sizeof(client->address);
memcpy(&client->address, rp->ai_addr, addr_len);
client->address_length = addr_len;
freeaddrinfo(res);
// Close old fd if any and set new one
if (client->file_descriptor >= 0)
close(client->file_descriptor);
client->file_descriptor = fd;
tcp_apply_socket_timeout(client->file_descriptor);
return true;
}
@@ -289,10 +205,7 @@ void client_disconnect(Client* client) {
client->ssl = NULL;
io_set_ssl(NULL);
}
if (client->file_descriptor >= 0) {
close(client->file_descriptor);
client->file_descriptor = -1;
}
close(client->file_descriptor);
if (client->ssh_child_pid > 0) {
int status;
waitpid(client->ssh_child_pid, &status, 0);
@@ -303,7 +216,6 @@ void client_disconnect(Client* client) {
void client_delete(Client* client) {
if (client == NULL)
return;
client_disconnect(client);
if (client->ssl_ctx) {
SSL_CTX_free(client->ssl_ctx);
client->ssl_ctx = NULL;