feat(p5-socket): --address, -4/-6, --sockopts, server bind options
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)");
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user