From 12def3a84e0358528347107dc88cfe740a54941f Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 12 Nov 2019 20:29:47 +0100 Subject: [PATCH] dirmngr: Use IPv4 or IPv6 interface only if available. * dirmngr/dns-stuff.c (cached_inet_support): New variable. (dns_stuff_housekeeping): New. (check_inet_support): New. * dirmngr/http.c (connect_server): Use only detected interfaces. * dirmngr/dirmngr.c (housekeeping_thread): Flush the new cache. -- This currently works only for Windows but that is where users really ran into problems. The old workaround was to configure disable-ipv4 or disable-ipv6. Signed-off-by: Werner Koch --- dirmngr/dirmngr.c | 1 + dirmngr/dns-stuff.c | 104 ++++++++++++++++++++++++++++++++++++++++++++ dirmngr/dns-stuff.h | 7 +++ dirmngr/http.c | 10 +++-- 4 files changed, 119 insertions(+), 3 deletions(-) diff --git a/dirmngr/dirmngr.c b/dirmngr/dirmngr.c index f7dd029f6..7948e3f24 100644 --- a/dirmngr/dirmngr.c +++ b/dirmngr/dirmngr.c @@ -1962,6 +1962,7 @@ housekeeping_thread (void *arg) memset (&ctrlbuf, 0, sizeof ctrlbuf); dirmngr_init_default_ctrl (&ctrlbuf); + dns_stuff_housekeeping (); ks_hkp_housekeeping (curtime); if (network_activity_seen) { diff --git a/dirmngr/dns-stuff.c b/dirmngr/dns-stuff.c index e48aca730..4e381c00d 100644 --- a/dirmngr/dns-stuff.c +++ b/dirmngr/dns-stuff.c @@ -149,6 +149,15 @@ static char tor_nameserver[40+20]; static char tor_socks_user[30]; static char tor_socks_password[20]; +/* To avoid checking the interface too often we cache the result. */ +static struct +{ + unsigned int valid:1; + unsigned int v4:1; + unsigned int v6:1; +} cached_inet_support; + + #ifdef USE_LIBDNS /* Libdns global data. */ @@ -686,6 +695,21 @@ reload_dns_stuff (int force) #else (void)force; #endif + + /* We also flush the IPv4/v6 support flag cache. */ + cached_inet_support.valid = 0; +} + + +/* Called from time to time from the housekeeping thread. */ +void +dns_stuff_housekeeping (void) +{ + /* With the current housekeeping interval of 10 minutes we flush + * that case so that a new or removed interface will be detected not + * later than 10 minutes after it changed. This way the user does + * not need a reload. */ + cached_inet_support.valid = 0; } @@ -2387,3 +2411,83 @@ get_dns_cname (ctrl_t ctrl, const char *name, char **r_cname) err ? gpg_strerror (err) : *r_cname); return err; } + + +/* Check whether the machine has any usable inet devices up and + * running. We put this into dns because on Windows this is + * implemented using getaddrinfo and thus easiest done here. */ +void +check_inet_support (int *r_v4, int *r_v6) +{ + if (cached_inet_support.valid) + { + *r_v4 = cached_inet_support.v4; + *r_v6 = cached_inet_support.v6; + return; + } + + *r_v4 = *r_v6 = 0; + +#ifdef HAVE_W32_SYSTEM + { + gpg_error_t err; + int ret; + struct addrinfo *aibuf = NULL; + struct addrinfo *ai; + + ret = getaddrinfo ("..localmachine", NULL, NULL, &aibuf); + if (ret) + { + err = map_eai_to_gpg_error (ret); + log_error ("%s: getaddrinfo failed: %s\n",__func__, gpg_strerror (err)); + aibuf = NULL; + } + + for (ai = aibuf; ai; ai = ai->ai_next) + { + if (opt_debug) + { + log_debug ("%s: family: %d\n", __func__, ai->ai_family); + if (ai->ai_family == AF_INET6 || ai->ai_family == AF_INET) + { + char buffer[46]; + DWORD buflen; + buflen = sizeof buffer; + if (WSAAddressToString (ai->ai_addr, (DWORD)ai->ai_addrlen, + NULL, buffer, &buflen)) + log_debug ("%s: WSAAddressToString failed: ec=%u\n", + __func__, (unsigned int)WSAGetLastError ()); + else + log_debug ("%s: addr: %s\n", __func__, buffer); + } + } + if (ai->ai_family == AF_INET6) + { + struct sockaddr_in6 *v6addr = (struct sockaddr_in6 *)ai->ai_addr; + if (!IN6_IS_ADDR_LINKLOCAL (&v6addr->sin6_addr)) + *r_v6 = 1; + } + else if (ai->ai_family == AF_INET) + { + *r_v4 = 1; + } + } + + if (aibuf) + freeaddrinfo (aibuf); + } +#else /*!HAVE_W32_SYSTEM*/ + { + /* For now we assume that we have both protocols. */ + *r_v4 = *r_v6 = 1; + } +#endif /*!HAVE_W32_SYSTEM*/ + + if (opt_verbose) + log_info ("detected interfaces:%s%s\n", + *r_v4? " IPv4":"", *r_v6? " IPv4":""); + + cached_inet_support.valid = 1; + cached_inet_support.v4 = *r_v4; + cached_inet_support.v6 = *r_v6; +} diff --git a/dirmngr/dns-stuff.h b/dirmngr/dns-stuff.h index 06a43122a..415eac59c 100644 --- a/dirmngr/dns-stuff.h +++ b/dirmngr/dns-stuff.h @@ -134,6 +134,9 @@ void set_dns_nameserver (const char *ipaddr); /* SIGHUP action handler for this module. */ void reload_dns_stuff (int force); +/* Housekeeping for this module. */ +void dns_stuff_housekeeping (void); + void free_dns_addrinfo (dns_addrinfo_t ai); /* Function similar to getaddrinfo. */ @@ -170,4 +173,8 @@ gpg_error_t get_dns_srv (ctrl_t ctrl, struct srventry **list, unsigned int *r_count); +/* Store true in the variable if such an IP interface is usable. */ +void check_inet_support (int *r_v4, int *r_v6); + + #endif /*GNUPG_DIRMNGR_DNS_STUFF_H*/ diff --git a/dirmngr/http.c b/dirmngr/http.c index 8e0701fab..c6dc077da 100644 --- a/dirmngr/http.c +++ b/dirmngr/http.c @@ -2908,7 +2908,7 @@ connect_server (ctrl_t ctrl, const char *server, unsigned short port, unsigned int srvcount = 0; int hostfound = 0; int anyhostaddr = 0; - int srv, connected; + int srv, connected, v4_valid, v6_valid; gpg_error_t last_err = 0; struct srventry *serverlist = NULL; @@ -2918,6 +2918,8 @@ connect_server (ctrl_t ctrl, const char *server, unsigned short port, init_sockets (); #endif /*Windows*/ + check_inet_support (&v4_valid, &v6_valid); + /* Onion addresses require special treatment. */ if (is_onion_address (server)) { @@ -2996,9 +2998,11 @@ connect_server (ctrl_t ctrl, const char *server, unsigned short port, for (ai = aibuf; ai && !connected; ai = ai->next) { - if (ai->family == AF_INET && (flags & HTTP_FLAG_IGNORE_IPv4)) + if (ai->family == AF_INET + && ((flags & HTTP_FLAG_IGNORE_IPv4) || !v4_valid)) continue; - if (ai->family == AF_INET6 && (flags & HTTP_FLAG_IGNORE_IPv6)) + if (ai->family == AF_INET6 + && ((flags & HTTP_FLAG_IGNORE_IPv6) || !v6_valid)) continue; if (sock != ASSUAN_INVALID_FD)