diff options
author | Stephane Coulondre <stephane@coulondre.info> | 2023-06-20 20:16:24 +0300 |
---|---|---|
committer | Stephane Coulondre <stephane@coulondre.info> | 2023-06-20 20:16:24 +0300 |
commit | 13309ef940d728c2d8bd0e10abe0191042dc685c (patch) | |
tree | 88f4969661a0e668bdc1342dd9dc387cca39a3a9 /src | |
parent | 3e440b4e36f2def0aa2efdc2ae3bd1c0db42f254 (diff) |
Solved issue #2910 - Added support for IPv6 with fallback to IPv4 for ssh
Diffstat (limited to 'src')
-rw-r--r-- | src/remmina_ssh.c | 62 |
1 files changed, 59 insertions, 3 deletions
diff --git a/src/remmina_ssh.c b/src/remmina_ssh.c index a5c9101ce..d5c6f1115 100644 --- a/src/remmina_ssh.c +++ b/src/remmina_ssh.c @@ -1672,6 +1672,12 @@ remmina_ssh_init_session(RemminaSSH *ssh) socket_t sshsock; gint optval; #endif + // Handle IPv4 / IPv6 dual stack + char *hostname; + struct addrinfo hints,*aitop,*ai; + char ipstr[INET6_ADDRSTRLEN]; + void *addr4=NULL; + void *addr6=NULL; ssh->callback = g_new0(struct ssh_callbacks_struct, 1); @@ -1840,12 +1846,62 @@ remmina_ssh_init_session(RemminaSSH *ssh) else REMMINA_DEBUG("SSH_OPTIONS_COMPRESSION does not have a valid value. %s", ssh->compression); - if (ssh_connect(ssh->session)) { - // TRANSLATORS: The placeholder %s is an error message - remmina_ssh_set_error(ssh, _("Could not start SSH session. %s")); + // Handle the dual IPv4 / IPv6 stack + // Prioritize IPv6 and fallback to IPv4 + + // Run the DNS resolution + // First retrieve host from the ssh->session structure + ssh_options_get(ssh->session, SSH_OPTIONS_HOST, &hostname); + // Call getaddrinfo + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + if ((getaddrinfo(hostname, NULL, &hints, &aitop)) != 0) { + ssh->error = g_strdup_printf("Could not resolve hostname %s", hostname); + REMMINA_DEBUG(ssh->error); return FALSE; } + // We have one or more addesses now, extract them + ai = aitop; + while (ai != NULL) { + if (ai->ai_family == AF_INET) { // IPv4 + struct sockaddr_in *ipv4 = (struct sockaddr_in *)ai->ai_addr; + addr4 = &(ipv4->sin_addr); + } else { // IPv6 + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)ai->ai_addr; + addr6 = &(ipv6->sin6_addr); + } + ai = ai->ai_next; + } + freeaddrinfo(aitop); + + unsigned short int success6 = 0; + if (addr6 != NULL) { + // Try IPv6 first + inet_ntop(AF_INET6, addr6, ipstr, sizeof ipstr); + ssh_options_set(ssh->session, SSH_OPTIONS_HOST, ipstr); + REMMINA_DEBUG("Setting SSH_OPTIONS_HOST to IPv6 %s", ipstr); + if (ssh_connect(ssh->session)) { + ssh_disconnect(ssh->session); + REMMINA_DEBUG("IPv6 session failed"); + } else { + success6 = 1; + REMMINA_DEBUG("IPv6 session success !"); + } + } + if (success6 == 0) { + // Fallback to IPv4 + inet_ntop(AF_INET, addr4, ipstr, sizeof ipstr); + ssh_options_set(ssh->session, SSH_OPTIONS_HOST, ipstr); + REMMINA_DEBUG("Setting SSH_OPTIONS_HOST to IPv4 %s", ipstr); + if (ssh_connect(ssh->session)) { + // TRANSLATORS: The placeholder %s is an error message + remmina_ssh_set_error(ssh, _("Could not start SSH session. %s")); + return FALSE; + } + } + #ifdef HAVE_NETINET_TCP_H /* Set keepalive on SSH socket, so we can keep firewalls awaken and detect * when we loss the tunnel */ |