From 7d44964dcd37a32ef56fedae49d1631c4418b6ab Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Mon, 9 Dec 2019 22:47:59 -0500 Subject: [PATCH] fix #641 Track the address family of the last failed connection attempt (either immediately or during TLS handshake), then disprefer that address family during reconnection. --- src/core/server-connect-rec.h | 2 ++ src/core/servers-reconnect.c | 1 + src/core/servers.c | 15 ++++++++++++--- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/core/server-connect-rec.h b/src/core/server-connect-rec.h index fa348769..2f68bd04 100644 --- a/src/core/server-connect-rec.h +++ b/src/core/server-connect-rec.h @@ -11,6 +11,7 @@ int proxy_port; char *proxy_string, *proxy_string_after, *proxy_password; unsigned short family; /* 0 = don't care, AF_INET or AF_INET6 */ +unsigned short chosen_family; /* family actually chosen during name resolution */ char *tag; /* try to keep this tag when connected to server */ char *address; int port; @@ -43,5 +44,6 @@ unsigned int unix_socket:1; /* Connect using named unix socket */ unsigned int use_tls:1; /* this connection uses TLS */ unsigned int tls_verify:1; unsigned int no_connect:1; /* don't connect() at all, it's done by plugin */ +unsigned short last_failed_family; /* #641: if we failed to connect to ipv6, try ipv4 and vice versa */ char *channels; char *away_reason; diff --git a/src/core/servers-reconnect.c b/src/core/servers-reconnect.c index 2cbe60b4..0cfac2ec 100644 --- a/src/core/servers-reconnect.c +++ b/src/core/servers-reconnect.c @@ -168,6 +168,7 @@ server_connect_copy_skeleton(SERVER_CONNECT_REC *src, int connect_info) server_connect_ref(dest); dest->type = module_get_uniq_id("SERVER CONNECT", 0); dest->reconnection = src->reconnection; + dest->last_failed_family = src->last_failed_family; dest->proxy = g_strdup(src->proxy); dest->proxy_port = src->proxy_port; dest->proxy_string = g_strdup(src->proxy_string); diff --git a/src/core/servers.c b/src/core/servers.c index 20452f8c..27f788bb 100644 --- a/src/core/servers.c +++ b/src/core/servers.c @@ -156,6 +156,7 @@ static void server_connect_callback_init(SERVER_REC *server, GIOChannel *handle) error = net_geterror(handle); if (error != 0) { server->connection_lost = TRUE; + server->connrec->last_failed_family = server->connrec->chosen_family; server_connect_failed(server, g_strerror(error)); return; } @@ -176,6 +177,7 @@ static void server_connect_callback_init_ssl(SERVER_REC *server, GIOChannel *han error = irssi_ssl_handshake(handle); if (error == -1) { server->connection_lost = TRUE; + server->connrec->last_failed_family = server->connrec->chosen_family; server_connect_failed(server, NULL); return; } @@ -216,6 +218,7 @@ static void server_real_connect(SERVER_REC *server, IPADDR *ip, return; if (ip != NULL) { + server->connrec->chosen_family = ip->family; own_ip = IPADDR_IS_V6(ip) ? server->connrec->own_ip6 : server->connrec->own_ip4; port = server->connrec->proxy != NULL ? server->connrec->proxy_port : server->connrec->port; @@ -241,9 +244,11 @@ static void server_real_connect(SERVER_REC *server, IPADDR *ip, server->no_reconnect = TRUE; server->connection_lost = TRUE; + server->connrec->last_failed_family = ip->family; server_connect_failed(server, errmsg2 ? errmsg2 : errmsg); g_free(errmsg2); } else { + server->connrec->last_failed_family = 0; server->handle = net_sendbuffer_create(handle, 0); if (server->connrec->use_tls) server_connect_callback_init_ssl(server, handle); @@ -289,11 +294,15 @@ static void server_connect_callback_readpipe(SERVER_REC *server) ip = iprec.ip6.family == 0 ? NULL : &iprec.ip6; servername = iprec.host6; } else { - /* pick the one that was found, or if both do it like - /SET resolve_prefer_ipv6 says. */ + /* pick the one that was found. if both were found: + 1. disprefer the last one that failed + 2. prefer ipv4 over ipv6 unless resolve_prefer_ipv6 is set + */ if (iprec.ip4.family == 0 || (iprec.ip6.family != 0 && - settings_get_bool("resolve_prefer_ipv6"))) { + (server->connrec->last_failed_family == AF_INET || + (settings_get_bool("resolve_prefer_ipv6") && + server->connrec->last_failed_family != AF_INET6)))) { ip = &iprec.ip6; servername = iprec.host6; } else {