feat(p5-socket): --address, -4/-6, --sockopts, server bind options

This commit is contained in:
2026-09-09 13:41:28 +02:00
parent 90ccf297d7
commit 07d1dd84f7
15 changed files with 715 additions and 45 deletions
+31
View File
@@ -148,6 +148,24 @@ static int set_compression_threads_option(int* dest, const char* value) {
return 0;
}
/* Parse and validate --sockopts=OPTIONS into the config. The strict allowlist
* (config_sockopts_parse) rejects an unknown option name or an invalid value
* up front, so a typo never silently disables a socket option. */
static int set_sockopts_option(Config* config, const char* value) {
SockOptEntry* entries = NULL;
int count = 0;
if (config_sockopts_parse(value, &entries, &count) != 0) {
log_message(LOG_LEVEL_ERROR,
"--sockopts must be a comma-separated OPT=VAL list of supported options "
"(TCP_NODELAY, SO_KEEPALIVE, SO_RCVBUF, SO_SNDBUF, SO_REUSEADDR)");
return -1;
}
free(config->sockopts);
config->sockopt_count = count;
config->sockopts = entries;
return 0;
}
/* Parse a string as a non-negative integer into *dest. Returns 0 on success, -1 on error. */
static int set_nonneg_int_option(int* dest, const char* value, const char* option_name) {
if (!parse_nonneg_int(value, dest)) {
@@ -530,6 +548,9 @@ static const OptionEntry OPTION_TABLE[] = {
{"--timeout", NULL, OPT_POS_INT, offsetof(Config, timeout)},
{"--contimeout", NULL, OPT_POS_INT, offsetof(Config, contimeout)},
{"--max-depth", NULL, OPT_NONNEG_INT, offsetof(Config, max_depth)},
{"--address", NULL, OPT_STRING, offsetof(Config, address)},
{"--ipv4", "-4", OPT_FLAG, offsetof(Config, ipv4)},
{"--ipv6", "-6", OPT_FLAG, offsetof(Config, ipv6)},
{"--max-size", NULL, OPT_ULL, offsetof(Config, max_size)},
{"--min-size", NULL, OPT_ULL, offsetof(Config, min_size)},
@@ -1125,6 +1146,16 @@ int parse_args(Config* config, int argc, char* argv[], int* positional_args,
}
if (set_checksum_seed(config, argv[++i]) != 0)
return -1;
} else if (strncmp(argv[i], "--sockopts=", 11) == 0) {
if (set_sockopts_option(config, argv[i] + 11) != 0)
return -1;
} else if (opt_is(argv[i], "--sockopts", NULL)) {
if (i + 1 >= argc) {
log_message(LOG_LEVEL_ERROR, "missing argument for --sockopts");
return -1;
}
if (set_sockopts_option(config, argv[++i]) != 0)
return -1;
} else if (strncmp(argv[i], "--compare-dest=", 15) == 0) {
if (set_basis_dest_option(config, BASIS_DEST_COMPARE, argv[i] + 15, "--compare-dest") != 0)
return -1;
+12 -3
View File
@@ -373,12 +373,21 @@ static Client* connect_transfer_client(const Config* config) {
Client* client = client_create();
if (!client)
return NULL;
/* Socket/connect concerns that never cross the wire: --address (source bind),
* -4/-6 (family pinning), and --sockopts. Passed straight to the TCP layer. */
TcpConnectOptions connect_opts;
connect_opts.bind_address = config->address;
connect_opts.family = tcp_connect_family(config->ipv4, config->ipv6);
connect_opts.sockopts = config->sockopts;
connect_opts.sockopt_count = config->sockopt_count;
bool connected;
if (config->use_tls) {
connected = client_connect_tls(client, config->server_host, config->server_port,
config->tls_cert, config->tls_key, config->tls_ca);
connected = client_connect_tls_ex(client, config->server_host, config->server_port,
config->tls_cert, config->tls_key, config->tls_ca,
&connect_opts);
} else {
connected = client_connect(client, config->server_host, config->server_port);
connected = client_connect_ex(client, config->server_host, config->server_port,
&connect_opts);
}
if (!connected) {
client_disconnect(client);
+5
View File
@@ -34,6 +34,11 @@ bool validate_config(const Config* config) {
log_message(LOG_LEVEL_ERROR, "--incremental is not supported with -s (chunk serialization)");
return false;
}
/* -4 and -6 are mutually exclusive: a socket address family cannot be both. */
if (config->ipv4 && config->ipv6) {
log_message(LOG_LEVEL_ERROR, "-4/--ipv4 and -6/--ipv6 are mutually exclusive");
return false;
}
if (config->skip_compress_set && config->use_chunk_serialization) {
log_message(LOG_LEVEL_ERROR,
"--skip-compress cannot be combined with -s (chunk serialization)");
+5
View File
@@ -166,6 +166,11 @@ void print_usage(void) {
printf(" --timeout <sec> I/O timeout in seconds (default: 30)\n");
printf(" -T <sec> Alias for --timeout\n");
printf(" --contimeout <sec> Connection timeout in seconds (default: 10)\n");
printf(" --address <ip> Bind the outgoing client socket to this source address\n");
printf(" -4, --ipv4 Force IPv4 for destination resolution\n");
printf(" -6, --ipv6 Force IPv6 for destination resolution\n");
printf(" --sockopts=OPTS Comma-separated OPT=VAL socket options applied before connect:\n");
printf(" TCP_NODELAY, SO_KEEPALIVE, SO_RCVBUF, SO_SNDBUF, SO_REUSEADDR\n");
printf(" --backup Backup existing files before overwriting\n");
printf(" --backup-dir <dir> Directory for backups (requires --backup)\n");
printf(" --suffix <str> Backup suffix (default: ~)\n");