mirror of
https://git.libssh.org/projects/libssh.git
synced 2026-02-04 12:20:42 +09:00
connect: Support AddressFamily option
* allow parsing of AddressFamily in config and cli * supports options "any", "inet" and "inet6" * introduce SSH_OPTIONS_ADDRESS_FAMILY Signed-off-by: Samir Benmendil <me@rmz.io> Reviewed-by: Jakub Jelen <jjelen@redhat.com> Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
This commit is contained in:
31
src/config.c
31
src/config.c
@@ -91,7 +91,7 @@ static struct ssh_config_keyword_table_s ssh_config_keyword_table[] = {
|
||||
{"passwordauthentication", SOC_PASSWORDAUTHENTICATION, true},
|
||||
{"pubkeyauthentication", SOC_PUBKEYAUTHENTICATION, true},
|
||||
{"addkeystoagent", SOC_UNSUPPORTED, true},
|
||||
{"addressfamily", SOC_UNSUPPORTED, true},
|
||||
{"addressfamily", SOC_ADDRESSFAMILY, true},
|
||||
{"batchmode", SOC_UNSUPPORTED, true},
|
||||
{"canonicaldomains", SOC_UNSUPPORTED, true},
|
||||
{"canonicalizefallbacklocal", SOC_UNSUPPORTED, true},
|
||||
@@ -1564,6 +1564,35 @@ static int ssh_config_parse_line_internal(ssh_session session,
|
||||
ssh_options_set(session, SSH_OPTIONS_RSA_MIN_SIZE, &l);
|
||||
}
|
||||
break;
|
||||
case SOC_ADDRESSFAMILY:
|
||||
p = ssh_config_get_str_tok(&s, NULL);
|
||||
if (p == NULL) {
|
||||
SSH_LOG(SSH_LOG_WARNING,
|
||||
"line %d: no argument after keyword \"addressfamily\"",
|
||||
count);
|
||||
SAFE_FREE(x);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
if (*parsing) {
|
||||
int value = -1;
|
||||
|
||||
if (strcasecmp(p, "any") == 0) {
|
||||
value = SSH_ADDRESS_FAMILY_ANY;
|
||||
} else if (strcasecmp(p, "inet") == 0) {
|
||||
value = SSH_ADDRESS_FAMILY_INET;
|
||||
} else if (strcasecmp(p, "inet6") == 0) {
|
||||
value = SSH_ADDRESS_FAMILY_INET6;
|
||||
} else {
|
||||
SSH_LOG(SSH_LOG_WARNING,
|
||||
"line %d: invalid argument \"%s\"",
|
||||
count,
|
||||
p);
|
||||
SAFE_FREE(x);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
ssh_options_set(session, SSH_OPTIONS_ADDRESS_FAMILY, &value);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ssh_set_error(session, SSH_FATAL, "ERROR - unimplemented opcode: %d",
|
||||
opcode);
|
||||
|
||||
@@ -109,7 +109,8 @@ static int ssh_connect_socket_close(socket_t s)
|
||||
#endif
|
||||
}
|
||||
|
||||
static int getai(const char *host, int port, struct addrinfo **ai)
|
||||
static int
|
||||
getai(const char *host, int port, int ai_family, struct addrinfo **ai)
|
||||
{
|
||||
const char *service = NULL;
|
||||
struct addrinfo hints;
|
||||
@@ -118,7 +119,7 @@ static int getai(const char *host, int port, struct addrinfo **ai)
|
||||
ZERO_STRUCT(hints);
|
||||
|
||||
hints.ai_protocol = IPPROTO_TCP;
|
||||
hints.ai_family = PF_UNSPEC;
|
||||
hints.ai_family = ai_family;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
if (port == 0) {
|
||||
@@ -165,16 +166,39 @@ socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host,
|
||||
{
|
||||
socket_t s = -1, first = -1;
|
||||
int rc;
|
||||
int ai_family;
|
||||
static const char *ai_family_str = NULL;
|
||||
struct addrinfo *ai = NULL;
|
||||
struct addrinfo *itr = NULL;
|
||||
char addrname[NI_MAXHOST], portname[NI_MAXSERV];
|
||||
|
||||
SSH_LOG(SSH_LOG_PACKET, "Resolve target hostname %s port %d", host, port);
|
||||
rc = getai(host, port, &ai);
|
||||
switch (session->opts.address_family) {
|
||||
case SSH_ADDRESS_FAMILY_INET:
|
||||
ai_family = PF_INET;
|
||||
ai_family_str = "inet";
|
||||
break;
|
||||
case SSH_ADDRESS_FAMILY_INET6:
|
||||
ai_family = PF_INET6;
|
||||
ai_family_str = "inet6";
|
||||
break;
|
||||
case SSH_ADDRESS_FAMILY_ANY:
|
||||
default:
|
||||
ai_family = PF_UNSPEC;
|
||||
ai_family_str = "any";
|
||||
}
|
||||
SSH_LOG(SSH_LOG_PACKET,
|
||||
"Resolve target hostname %s port %d (%s)",
|
||||
host,
|
||||
port,
|
||||
ai_family_str);
|
||||
rc = getai(host, port, ai_family, &ai);
|
||||
if (rc != 0) {
|
||||
ssh_set_error(session, SSH_FATAL,
|
||||
"Failed to resolve hostname %s (%s)",
|
||||
host, gai_strerror(rc));
|
||||
ssh_set_error(session,
|
||||
SSH_FATAL,
|
||||
"Failed to resolve hostname %s (%s): %s",
|
||||
host,
|
||||
ai_family_str,
|
||||
gai_strerror(rc));
|
||||
|
||||
return -1;
|
||||
}
|
||||
@@ -194,13 +218,18 @@ socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host,
|
||||
struct addrinfo *bind_ai = NULL;
|
||||
struct addrinfo *bind_itr = NULL;
|
||||
|
||||
SSH_LOG(SSH_LOG_PACKET, "Resolving bind address %s", bind_addr);
|
||||
SSH_LOG(SSH_LOG_PACKET,
|
||||
"Resolving bind address %s (%s)",
|
||||
bind_addr,
|
||||
ai_family_str);
|
||||
|
||||
rc = getai(bind_addr, 0, &bind_ai);
|
||||
rc = getai(bind_addr, 0, ai_family, &bind_ai);
|
||||
if (rc != 0) {
|
||||
ssh_set_error(session, SSH_FATAL,
|
||||
"Failed to resolve bind address %s (%s)",
|
||||
ssh_set_error(session,
|
||||
SSH_FATAL,
|
||||
"Failed to resolve bind address %s (%s): %s",
|
||||
bind_addr,
|
||||
ai_family_str,
|
||||
gai_strerror(rc));
|
||||
ssh_connect_socket_close(s);
|
||||
s = -1;
|
||||
|
||||
@@ -256,6 +256,7 @@ int ssh_options_copy(ssh_session src, ssh_session *dest)
|
||||
new->opts.nodelay = src->opts.nodelay;
|
||||
new->opts.config_processed = src->opts.config_processed;
|
||||
new->opts.control_master = src->opts.control_master;
|
||||
new->opts.address_family = src->opts.address_family;
|
||||
new->common.log_verbosity = src->common.log_verbosity;
|
||||
new->common.callbacks = src->common.callbacks;
|
||||
|
||||
@@ -650,6 +651,13 @@ int ssh_options_set_algo(ssh_session session,
|
||||
* context and can free it after this call.
|
||||
* (ssh_pki_ctx)
|
||||
*
|
||||
* - SSH_OPTIONS_ADDRESS_FAMILY
|
||||
* Specify which address family to use when connecting.
|
||||
*
|
||||
* Possible options:
|
||||
* - SSH_ADDRESS_FAMILY_ANY: use any address family
|
||||
* - SSH_ADDRESS_FAMILY_INET: IPv4 only
|
||||
* - SSH_ADDRESS_FAMILY_INET6: IPv6 only
|
||||
*
|
||||
* @param value The value to set. This is a generic pointer and the
|
||||
* datatype which is used should be set according to the
|
||||
@@ -1393,6 +1401,20 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case SSH_OPTIONS_ADDRESS_FAMILY:
|
||||
if (value == NULL) {
|
||||
ssh_set_error_invalid(session);
|
||||
return -1;
|
||||
} else {
|
||||
int *x = (int *)value;
|
||||
if (*x < SSH_ADDRESS_FAMILY_ANY ||
|
||||
*x > SSH_ADDRESS_FAMILY_INET6) {
|
||||
ssh_set_error_invalid(session);
|
||||
return -1;
|
||||
}
|
||||
session->opts.address_family = *x;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ssh_set_error(session, SSH_REQUEST_DENIED, "Unknown ssh option %d", type);
|
||||
return -1;
|
||||
|
||||
Reference in New Issue
Block a user