diff options
Diffstat (limited to 'libc/netbsd')
-rw-r--r-- | libc/netbsd/gethnamaddr.c | 3 | ||||
-rw-r--r-- | libc/netbsd/net/getaddrinfo.c | 8 | ||||
-rw-r--r-- | libc/netbsd/net/nsdispatch.c | 1 | ||||
-rw-r--r-- | libc/netbsd/resolv/res_cache.c | 453 | ||||
-rw-r--r-- | libc/netbsd/resolv/res_debug.c | 1 | ||||
-rw-r--r-- | libc/netbsd/resolv/res_init.c | 6 | ||||
-rw-r--r-- | libc/netbsd/resolv/res_send.c | 196 | ||||
-rw-r--r-- | libc/netbsd/resolv/res_state.c | 99 |
8 files changed, 688 insertions, 79 deletions
diff --git a/libc/netbsd/gethnamaddr.c b/libc/netbsd/gethnamaddr.c index 3ebe53e..9a9f6e2 100644 --- a/libc/netbsd/gethnamaddr.c +++ b/libc/netbsd/gethnamaddr.c @@ -67,6 +67,7 @@ #include <netdb.h> #include <stdarg.h> #include <stdio.h> +#include <strings.h> #include <syslog.h> #ifndef LOG_AUTH @@ -637,7 +638,7 @@ gethostbyname_internal(const char *name, int af, res_state res) } struct hostent * -gethostbyaddr(const char *addr, /* XXX should have been def'd as u_char! */ +gethostbyaddr(const void *addr, socklen_t len, int af) { const u_char *uaddr = (const u_char *)addr; diff --git a/libc/netbsd/net/getaddrinfo.c b/libc/netbsd/net/getaddrinfo.c index edb4f70..ace8c1a 100644 --- a/libc/netbsd/net/getaddrinfo.c +++ b/libc/netbsd/net/getaddrinfo.c @@ -97,6 +97,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <strings.h> #include <unistd.h> #include <syslog.h> @@ -1910,6 +1911,13 @@ _dns_getaddrinfo(void *rv, void *cb_data, va_list ap) if (pai->ai_flags & AI_ADDRCONFIG) { query_ipv6 = _have_ipv6(); query_ipv4 = _have_ipv4(); + if (query_ipv6 == 0 && query_ipv4 == 0) { + // Both our IPv4 and IPv6 connectivity probes failed, which indicates + // that we have neither an IPv4 or an IPv6 default route (and thus no + // global IPv4 or IPv6 connectivity). We might be in a walled garden. + // Throw up our arms and ask for both A and AAAA. + query_ipv6 = query_ipv4 = 1; + } } if (query_ipv6) { q.qtype = T_AAAA; diff --git a/libc/netbsd/net/nsdispatch.c b/libc/netbsd/net/nsdispatch.c index fa99366..15282be 100644 --- a/libc/netbsd/net/nsdispatch.c +++ b/libc/netbsd/net/nsdispatch.c @@ -84,6 +84,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <strings.h> #include <unistd.h> static nss_method diff --git a/libc/netbsd/resolv/res_cache.c b/libc/netbsd/resolv/res_cache.c index 58d60c9..284ca2f 100644 --- a/libc/netbsd/resolv/res_cache.c +++ b/libc/netbsd/resolv/res_cache.c @@ -27,6 +27,7 @@ */ #include "resolv_cache.h" +#include <resolv.h> #include <stdlib.h> #include <string.h> #include <time.h> @@ -35,6 +36,12 @@ #include <errno.h> #include "arpa_nameser.h" #include <sys/system_properties.h> +#include <net/if.h> +#include <netdb.h> +#include <linux/if.h> + +#include <arpa/inet.h> +#include "resolv_private.h" /* This code implements a small and *simple* DNS resolver cache. * @@ -159,9 +166,6 @@ #include <stdio.h> #include <stdarg.h> -#include <arpa/inet.h> -#include "resolv_private.h" - /** BOUNDED BUFFER FORMATTING **/ @@ -1165,6 +1169,14 @@ typedef struct resolv_cache { Entry* entries; } Cache; +typedef struct resolv_cache_info { + char ifname[IF_NAMESIZE + 1]; + struct in_addr ifaddr; + Cache* cache; + struct resolv_cache_info* next; + char* nameservers[MAXNS +1]; + struct addrinfo* nsaddrinfo[MAXNS + 1]; +} CacheInfo; #define HTABLE_VALID(x) ((x) != NULL && (x) != HTABLE_DELETED) @@ -1393,6 +1405,10 @@ _cache_remove_oldest( Cache* cache ) XLOG("%s: OLDEST NOT IN HTABLE ?", __FUNCTION__); return; } + if (DEBUG) { + XLOG("Cache full - removing oldest"); + XLOG_QUERY(oldest->query, oldest->querylen); + } _cache_remove_p(cache, lookup); } @@ -1462,6 +1478,7 @@ _resolv_cache_lookup( struct resolv_cache* cache, /* remove stale entries here */ if (now >= e->expires) { XLOG( " NOT IN CACHE (STALE ENTRY %p DISCARDED)", *lookup ); + XLOG_QUERY(e->query, e->querylen); _cache_remove_p(cache, lookup); goto Exit; } @@ -1567,11 +1584,47 @@ Exit: /****************************************************************************/ /****************************************************************************/ -static struct resolv_cache* _res_cache; static pthread_once_t _res_cache_once; +// Head of the list of caches. Protected by _res_cache_list_lock. +static struct resolv_cache_info _res_cache_list; + +// name of the current default inteface +static char _res_default_ifname[IF_NAMESIZE + 1]; + +// lock protecting everything in the _resolve_cache_info structs (next ptr, etc) +static pthread_mutex_t _res_cache_list_lock; + + +/* lookup the default interface name */ +static char *_get_default_iface_locked(); +/* insert resolv_cache_info into the list of resolv_cache_infos */ +static void _insert_cache_info_locked(struct resolv_cache_info* cache_info); +/* creates a resolv_cache_info */ +static struct resolv_cache_info* _create_cache_info( void ); +/* gets cache associated with an interface name, or NULL if none exists */ +static struct resolv_cache* _find_named_cache_locked(const char* ifname); +/* gets a resolv_cache_info associated with an interface name, or NULL if not found */ +static struct resolv_cache_info* _find_cache_info_locked(const char* ifname); +/* free dns name server list of a resolv_cache_info structure */ +static void _free_nameservers(struct resolv_cache_info* cache_info); +/* look up the named cache, and creates one if needed */ +static struct resolv_cache* _get_res_cache_for_iface_locked(const char* ifname); +/* empty the named cache */ +static void _flush_cache_for_iface_locked(const char* ifname); +/* empty the nameservers set for the named cache */ +static void _free_nameservers_locked(struct resolv_cache_info* cache_info); +/* lookup the namserver for the name interface */ +static int _get_nameserver_locked(const char* ifname, int n, char* addr, int addrLen); +/* lookup the addr of the nameserver for the named interface */ +static struct addrinfo* _get_nameserver_addr_locked(const char* ifname, int n); +/* lookup the inteface's address */ +static struct in_addr* _get_addr_locked(const char * ifname); + + + static void -_res_cache_init( void ) +_res_cache_init(void) { const char* env = getenv(CONFIG_ENV); @@ -1580,29 +1633,393 @@ _res_cache_init( void ) return; } - _res_cache = _resolv_cache_create(); + memset(&_res_default_ifname, 0, sizeof(_res_default_ifname)); + memset(&_res_cache_list, 0, sizeof(_res_cache_list)); + pthread_mutex_init(&_res_cache_list_lock, NULL); } - struct resolv_cache* -__get_res_cache( void ) +__get_res_cache(void) +{ + struct resolv_cache *cache; + + pthread_once(&_res_cache_once, _res_cache_init); + + pthread_mutex_lock(&_res_cache_list_lock); + + char* ifname = _get_default_iface_locked(); + + // if default interface not set then use the first cache + // associated with an interface as the default one. + if (ifname[0] == '\0') { + struct resolv_cache_info* cache_info = _res_cache_list.next; + while (cache_info) { + if (cache_info->ifname[0] != '\0') { + ifname = cache_info->ifname; + break; + } + + cache_info = cache_info->next; + } + } + cache = _get_res_cache_for_iface_locked(ifname); + + pthread_mutex_unlock(&_res_cache_list_lock); + XLOG("_get_res_cache. default_ifname = %s\n", ifname); + return cache; +} + +static struct resolv_cache* +_get_res_cache_for_iface_locked(const char* ifname) { - pthread_once( &_res_cache_once, _res_cache_init ); - return _res_cache; + if (ifname == NULL) + return NULL; + + struct resolv_cache* cache = _find_named_cache_locked(ifname); + if (!cache) { + struct resolv_cache_info* cache_info = _create_cache_info(); + if (cache_info) { + cache = _resolv_cache_create(); + if (cache) { + int len = sizeof(cache_info->ifname); + cache_info->cache = cache; + strncpy(cache_info->ifname, ifname, len - 1); + cache_info->ifname[len - 1] = '\0'; + + _insert_cache_info_locked(cache_info); + } else { + free(cache_info); + } + } + } + return cache; } void -_resolv_cache_reset( unsigned generation ) +_resolv_cache_reset(unsigned generation) { XLOG("%s: generation=%d", __FUNCTION__, generation); - if (_res_cache == NULL) - return; + pthread_once(&_res_cache_once, _res_cache_init); + pthread_mutex_lock(&_res_cache_list_lock); + + char* ifname = _get_default_iface_locked(); + // if default interface not set then use the first cache + // associated with an interface as the default one. + // Note: Copied the code from __get_res_cache since this + // method will be deleted/obsolete when cache per interface + // implemented all over + if (ifname[0] == '\0') { + struct resolv_cache_info* cache_info = _res_cache_list.next; + while (cache_info) { + if (cache_info->ifname[0] != '\0') { + ifname = cache_info->ifname; + break; + } + + cache_info = cache_info->next; + } + } + struct resolv_cache* cache = _get_res_cache_for_iface_locked(ifname); + + if (cache != NULL) { + pthread_mutex_lock( &cache->lock ); + if (cache->generation != generation) { + _cache_flush_locked(cache); + cache->generation = generation; + } + pthread_mutex_unlock( &cache->lock ); + } + + pthread_mutex_unlock(&_res_cache_list_lock); +} - pthread_mutex_lock( &_res_cache->lock ); - if (_res_cache->generation != generation) { - _cache_flush_locked(_res_cache); - _res_cache->generation = generation; +void +_resolv_flush_cache_for_default_iface(void) +{ + char* ifname; + + pthread_once(&_res_cache_once, _res_cache_init); + pthread_mutex_lock(&_res_cache_list_lock); + + ifname = _get_default_iface_locked(); + _flush_cache_for_iface_locked(ifname); + + pthread_mutex_unlock(&_res_cache_list_lock); +} + +void +_resolv_flush_cache_for_iface(const char* ifname) +{ + pthread_once(&_res_cache_once, _res_cache_init); + pthread_mutex_lock(&_res_cache_list_lock); + + _flush_cache_for_iface_locked(ifname); + + pthread_mutex_unlock(&_res_cache_list_lock); +} + +static void +_flush_cache_for_iface_locked(const char* ifname) +{ + struct resolv_cache* cache = _find_named_cache_locked(ifname); + if (cache) { + pthread_mutex_lock(&cache->lock); + _cache_flush_locked(cache); + pthread_mutex_unlock(&cache->lock); + } +} + +static struct resolv_cache_info* +_create_cache_info(void) +{ + struct resolv_cache_info* cache_info; + + cache_info = calloc(sizeof(*cache_info), 1); + return cache_info; +} + +static void +_insert_cache_info_locked(struct resolv_cache_info* cache_info) +{ + struct resolv_cache_info* last; + + for (last = &_res_cache_list; last->next; last = last->next); + + last->next = cache_info; + +} + +static struct resolv_cache* +_find_named_cache_locked(const char* ifname) { + + struct resolv_cache_info* info = _find_cache_info_locked(ifname); + + if (info != NULL) return info->cache; + + return NULL; +} + +static struct resolv_cache_info* +_find_cache_info_locked(const char* ifname) +{ + if (ifname == NULL) + return NULL; + + struct resolv_cache_info* cache_info = _res_cache_list.next; + + while (cache_info) { + if (strcmp(cache_info->ifname, ifname) == 0) { + break; + } + + cache_info = cache_info->next; + } + return cache_info; +} + +static char* +_get_default_iface_locked(void) +{ + char* iface = _res_default_ifname; + + return iface; +} + +void +_resolv_set_default_iface(const char* ifname) +{ + XLOG("_resolv_set_default_if ifname %s\n",ifname); + + pthread_once(&_res_cache_once, _res_cache_init); + pthread_mutex_lock(&_res_cache_list_lock); + + int size = sizeof(_res_default_ifname); + memset(_res_default_ifname, 0, size); + strncpy(_res_default_ifname, ifname, size - 1); + _res_default_ifname[size - 1] = '\0'; + + pthread_mutex_unlock(&_res_cache_list_lock); +} + +void +_resolv_set_nameservers_for_iface(const char* ifname, char** servers, int numservers) +{ + int i, rt, index; + struct addrinfo hints; + char sbuf[NI_MAXSERV]; + + pthread_once(&_res_cache_once, _res_cache_init); + + pthread_mutex_lock(&_res_cache_list_lock); + // creates the cache if not created + _get_res_cache_for_iface_locked(ifname); + + struct resolv_cache_info* cache_info = _find_cache_info_locked(ifname); + + if (cache_info != NULL) { + // free current before adding new + _free_nameservers_locked(cache_info); + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; /*dummy*/ + hints.ai_flags = AI_NUMERICHOST; + sprintf(sbuf, "%u", NAMESERVER_PORT); + + index = 0; + for (i = 0; i < numservers && i < MAXNS; i++) { + rt = getaddrinfo(servers[i], sbuf, &hints, &cache_info->nsaddrinfo[index]); + if (rt == 0) { + cache_info->nameservers[index] = strdup(servers[i]); + index++; + } else { + cache_info->nsaddrinfo[index] = NULL; + } + } + } + pthread_mutex_unlock(&_res_cache_list_lock); +} + +static void +_free_nameservers_locked(struct resolv_cache_info* cache_info) +{ + int i; + for (i = 0; i <= MAXNS; i++) { + free(cache_info->nameservers[i]); + cache_info->nameservers[i] = NULL; + if (cache_info->nsaddrinfo[i] != NULL) { + freeaddrinfo(cache_info->nsaddrinfo[i]); + cache_info->nsaddrinfo[i] = NULL; + } + } +} + +int +_resolv_cache_get_nameserver(int n, char* addr, int addrLen) +{ + char *ifname; + int result = 0; + + pthread_once(&_res_cache_once, _res_cache_init); + pthread_mutex_lock(&_res_cache_list_lock); + + ifname = _get_default_iface_locked(); + result = _get_nameserver_locked(ifname, n, addr, addrLen); + + pthread_mutex_unlock(&_res_cache_list_lock); + return result; +} + +static int +_get_nameserver_locked(const char* ifname, int n, char* addr, int addrLen) +{ + int len = 0; + char* ns; + struct resolv_cache_info* cache_info; + + if (n < 1 || n > MAXNS || !addr) + return 0; + + cache_info = _find_cache_info_locked(ifname); + if (cache_info) { + ns = cache_info->nameservers[n - 1]; + if (ns) { + len = strlen(ns); + if (len < addrLen) { + strncpy(addr, ns, len); + addr[len] = '\0'; + } else { + len = 0; + } + } + } + + return len; +} + +struct addrinfo* +_cache_get_nameserver_addr(int n) +{ + struct addrinfo *result; + char* ifname; + + pthread_once(&_res_cache_once, _res_cache_init); + pthread_mutex_lock(&_res_cache_list_lock); + + ifname = _get_default_iface_locked(); + + result = _get_nameserver_addr_locked(ifname, n); + pthread_mutex_unlock(&_res_cache_list_lock); + return result; +} + +static struct addrinfo* +_get_nameserver_addr_locked(const char* ifname, int n) +{ + struct addrinfo* ai = NULL; + struct resolv_cache_info* cache_info; + + if (n < 1 || n > MAXNS) + return NULL; + + cache_info = _find_cache_info_locked(ifname); + if (cache_info) { + ai = cache_info->nsaddrinfo[n - 1]; + } + return ai; +} + +void +_resolv_set_addr_of_iface(const char* ifname, struct in_addr* addr) +{ + pthread_once(&_res_cache_once, _res_cache_init); + pthread_mutex_lock(&_res_cache_list_lock); + struct resolv_cache_info* cache_info = _find_cache_info_locked(ifname); + if (cache_info) { + memcpy(&cache_info->ifaddr, addr, sizeof(*addr)); + + if (DEBUG) { + char* addr_s = inet_ntoa(cache_info->ifaddr); + XLOG("address of interface %s is %s\n", ifname, addr_s); + } + } + pthread_mutex_unlock(&_res_cache_list_lock); +} + +struct in_addr* +_resolv_get_addr_of_default_iface(void) +{ + struct in_addr* ai = NULL; + char* ifname; + + pthread_once(&_res_cache_once, _res_cache_init); + pthread_mutex_lock(&_res_cache_list_lock); + ifname = _get_default_iface_locked(); + ai = _get_addr_locked(ifname); + pthread_mutex_unlock(&_res_cache_list_lock); + + return ai; +} + +struct in_addr* +_resolv_get_addr_of_iface(const char* ifname) +{ + struct in_addr* ai = NULL; + + pthread_once(&_res_cache_once, _res_cache_init); + pthread_mutex_lock(&_res_cache_list_lock); + ai =_get_addr_locked(ifname); + pthread_mutex_unlock(&_res_cache_list_lock); + return ai; +} + +static struct in_addr* +_get_addr_locked(const char * ifname) +{ + struct resolv_cache_info* cache_info = _find_cache_info_locked(ifname); + if (cache_info) { + return &cache_info->ifaddr; } - pthread_mutex_unlock( &_res_cache->lock ); + return NULL; } diff --git a/libc/netbsd/resolv/res_debug.c b/libc/netbsd/resolv/res_debug.c index 721e015..46e583b 100644 --- a/libc/netbsd/resolv/res_debug.c +++ b/libc/netbsd/resolv/res_debug.c @@ -123,6 +123,7 @@ __RCSID("$NetBSD: res_debug.c,v 1.7 2004/11/07 02:25:01 christos Exp $"); #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <strings.h> #include <time.h> diff --git a/libc/netbsd/resolv/res_init.c b/libc/netbsd/resolv/res_init.c index 2158f20..ffd4054 100644 --- a/libc/netbsd/resolv/res_init.c +++ b/libc/netbsd/resolv/res_init.c @@ -225,6 +225,9 @@ __res_vinit(res_state statp, int preinit) { char dnsProperty[PROP_VALUE_MAX]; #endif + if ((statp->options & RES_INIT) != 0U) + res_ndestroy(statp); + if (!preinit) { statp->retrans = RES_TIMEOUT; statp->retry = RES_DFLRETRY; @@ -232,9 +235,6 @@ __res_vinit(res_state statp, int preinit) { statp->id = res_randomid(); } - if ((statp->options & RES_INIT) != 0U) - res_ndestroy(statp); - memset(u, 0, sizeof(u)); #ifdef USELOOPBACK u[nserv].sin.sin_addr = inet_makeaddr(IN_LOOPBACKNET, 1); diff --git a/libc/netbsd/resolv/res_send.c b/libc/netbsd/resolv/res_send.c index b118956..dbad6dd 100644 --- a/libc/netbsd/resolv/res_send.c +++ b/libc/netbsd/resolv/res_send.c @@ -99,6 +99,7 @@ __RCSID("$NetBSD: res_send.c,v 1.9 2006/01/24 17:41:25 christos Exp $"); #include <arpa/inet.h> #include <errno.h> +#include <fcntl.h> #include <netdb.h> #ifdef ANDROID_CHANGES #include "resolv_private.h" @@ -109,6 +110,7 @@ __RCSID("$NetBSD: res_send.c,v 1.9 2006/01/24 17:41:25 christos Exp $"); #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <time.h> #include <unistd.h> #include <isc/eventlib.h> @@ -117,6 +119,8 @@ __RCSID("$NetBSD: res_send.c,v 1.9 2006/01/24 17:41:25 christos Exp $"); # include <resolv_cache.h> #endif +#include "logd.h" + #ifndef DE_CONST #define DE_CONST(c,v) v = ((c) ? \ strchr((const void *)(c), *(const char *)(const void *)(c)) : NULL) @@ -130,6 +134,7 @@ __RCSID("$NetBSD: res_send.c,v 1.9 2006/01/24 17:41:25 christos Exp $"); #include "res_private.h" #define EXT(res) ((res)->_u._ext) +#define DBG 0 static const int highestFD = FD_SETSIZE - 1; @@ -152,7 +157,10 @@ static int pselect(int, void *, void *, void *, const sigset_t *); #endif void res_pquery(const res_state, const u_char *, int, FILE *); - +static int connect_with_timeout(int sock, const struct sockaddr *nsap, + socklen_t salen, int sec); +static int retrying_select(const int sock, fd_set *readset, fd_set *writeset, + const struct timespec *finish); /* BIONIC-BEGIN: implement source port randomization */ typedef union { @@ -413,7 +421,7 @@ res_nsend(res_state statp, if (EXT(statp).nssocks[ns] == -1) continue; peerlen = sizeof(peer); - if (getsockname(EXT(statp).nssocks[ns], + if (getpeername(EXT(statp).nssocks[ns], (struct sockaddr *)(void *)&peer, &peerlen) < 0) { needclose++; break; @@ -521,16 +529,23 @@ res_nsend(res_state statp, Dprint(((statp->options & RES_DEBUG) && getnameinfo(nsap, (socklen_t)nsaplen, abuf, sizeof(abuf), - NULL, 0, niflags) == 0), - (stdout, ";; Querying server (# %d) address = %s\n", - ns + 1, abuf)); + NULL, 0, niflags) == 0), + (stdout, ";; Querying server (# %d) address = %s\n", + ns + 1, abuf)); if (v_circuit) { /* Use VC; at most one attempt per server. */ try = statp->retry; + n = send_vc(statp, buf, buflen, ans, anssiz, &terrno, ns); + + if (DBG) { + __libc_android_log_print(ANDROID_LOG_DEBUG, "libc", + "used send_vc %d\n", n); + } + if (n < 0) goto fail; if (n == 0) @@ -538,12 +553,26 @@ res_nsend(res_state statp, resplen = n; } else { /* Use datagrams. */ + if (DBG) { + __libc_android_log_print(ANDROID_LOG_DEBUG, "libc", + "using send_dg\n"); + } + n = send_dg(statp, buf, buflen, ans, anssiz, &terrno, ns, &v_circuit, &gotsomewhere); + if (DBG) { + __libc_android_log_print(ANDROID_LOG_DEBUG, "libc", + "used send_dg %d\n",n); + } + if (n < 0) goto fail; if (n == 0) goto next_ns; + if (DBG) { + __libc_android_log_print(ANDROID_LOG_DEBUG, "libc", + "time=%d, %d\n",time(NULL), time(NULL)%2); + } if (v_circuit) goto same_ns; resplen = n; @@ -668,6 +697,23 @@ get_nsaddr(statp, n) } } +static int get_timeout(const res_state statp, const int ns) +{ + int timeout = (statp->retrans << ns); + if (ns > 0) { + timeout /= statp->nscount; + } + if (timeout <= 0) { + timeout = 1; + } + if (DBG) { + __libc_android_log_print(ANDROID_LOG_DEBUG, "libc", + "using timeout of %d sec\n", timeout); + } + + return timeout; +} + static int send_vc(res_state statp, const u_char *buf, int buflen, u_char *ans, int anssiz, @@ -683,6 +729,10 @@ send_vc(res_state statp, u_char *cp; void *tmp; + if (DBG) { + __libc_android_log_print(ANDROID_LOG_DEBUG, "libc", "using send_vc\n"); + } + nsap = get_nsaddr(statp, (size_t)ns); nsaplen = get_salen(nsap); @@ -735,7 +785,8 @@ send_vc(res_state statp, res_nclose(statp); return (0); } - if (connect(statp->_vcsock, nsap, (socklen_t)nsaplen) < 0) { + if (connect_with_timeout(statp->_vcsock, nsap, (socklen_t)nsaplen, + get_timeout(statp, ns)) < 0) { *terrno = errno; Aerror(statp, stderr, "connect/vc", errno, nsap, nsaplen); @@ -859,6 +910,111 @@ send_vc(res_state statp, return (resplen); } +/* return -1 on error (errno set), 0 on success */ +static int +connect_with_timeout(int sock, const struct sockaddr *nsap, socklen_t salen, int sec) +{ + int res, origflags; + fd_set rset, wset; + struct timespec now, timeout, finish; + + origflags = fcntl(sock, F_GETFL, 0); + fcntl(sock, F_SETFL, origflags | O_NONBLOCK); + + res = connect(sock, nsap, salen); + if (res < 0 && errno != EINPROGRESS) { + res = -1; + goto done; + } + if (res != 0) { + now = evNowTime(); + timeout = evConsTime((long)sec, 0L); + finish = evAddTime(now, timeout); + if (DBG) { + __libc_android_log_print(ANDROID_LOG_DEBUG, "libc", + " %d send_vc\n", sock); + } + + res = retrying_select(sock, &rset, &wset, &finish); + if (res <= 0) { + res = -1; + } + } +done: + fcntl(sock, F_SETFL, origflags); + if (DBG) { + __libc_android_log_print(ANDROID_LOG_DEBUG, "libc", + " %d connect_with_timeout returning %s\n", sock, res); + } + return res; +} + +static int +retrying_select(const int sock, fd_set *readset, fd_set *writeset, const struct timespec *finish) +{ + struct timespec now, timeout; + int n, error; + socklen_t len; + + +retry: + if (DBG) { + __libc_android_log_print(ANDROID_LOG_DEBUG, "libc", " %d retying_select\n", sock); + } + + now = evNowTime(); + if (readset) { + FD_ZERO(readset); + FD_SET(sock, readset); + } + if (writeset) { + FD_ZERO(writeset); + FD_SET(sock, writeset); + } + if (evCmpTime(*finish, now) > 0) + timeout = evSubTime(*finish, now); + else + timeout = evConsTime(0L, 0L); + + n = pselect(sock + 1, readset, writeset, NULL, &timeout, NULL); + if (n == 0) { + if (DBG) { + __libc_android_log_print(ANDROID_LOG_DEBUG, " libc", + " %d retrying_select timeout\n", sock); + } + errno = ETIMEDOUT; + return 0; + } + if (n < 0) { + if (errno == EINTR) + goto retry; + if (DBG) { + __libc_android_log_print(ANDROID_LOG_DEBUG, "libc", + " %d retrying_select got error %d\n",sock, n); + } + return n; + } + if ((readset && FD_ISSET(sock, readset)) || (writeset && FD_ISSET(sock, writeset))) { + len = sizeof(error); + if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &len) < 0 || error) { + errno = error; + if (DBG) { + __libc_android_log_print(ANDROID_LOG_DEBUG, "libc", + " %d retrying_select dot error2 %d\n", sock, errno); + } + + return -1; + } + } + if (DBG) { + __libc_android_log_print(ANDROID_LOG_DEBUG, "libc", + " %d retrying_select returning %d for %d\n",sock, n); + } + + return n; +} + + static int send_dg(res_state statp, const u_char *buf, int buflen, u_char *ans, int anssiz, @@ -944,33 +1100,19 @@ send_dg(res_state statp, /* * Wait for reply. */ - seconds = (statp->retrans << ns); - if (ns > 0) - seconds /= statp->nscount; - if (seconds <= 0) - seconds = 1; + seconds = get_timeout(statp, ns); now = evNowTime(); timeout = evConsTime((long)seconds, 0L); finish = evAddTime(now, timeout); - goto nonow; - wait: - now = evNowTime(); - nonow: - FD_ZERO(&dsmask); - FD_SET(s, &dsmask); - if (evCmpTime(finish, now) > 0) - timeout = evSubTime(finish, now); - else - timeout = evConsTime(0L, 0L); - n = pselect(s + 1, &dsmask, NULL, NULL, &timeout, NULL); +retry: + n = retrying_select(s, &dsmask, NULL, &finish); + if (n == 0) { Dprint(statp->options & RES_DEBUG, (stdout, ";; timeout\n")); *gotsomewhere = 1; return (0); } if (n < 0) { - if (errno == EINTR) - goto wait; Perror(statp, stderr, "select", errno); res_nclose(statp); return (0); @@ -1006,7 +1148,7 @@ send_dg(res_state statp, (statp->pfcode & RES_PRF_REPLY), (stdout, ";; old answer:\n"), ans, (resplen > anssiz) ? anssiz : resplen); - goto wait; + goto retry; } if (!(statp->options & RES_INSECURE1) && !res_ourserver_p(statp, (struct sockaddr *)(void *)&from)) { @@ -1019,7 +1161,7 @@ send_dg(res_state statp, (statp->pfcode & RES_PRF_REPLY), (stdout, ";; not our server:\n"), ans, (resplen > anssiz) ? anssiz : resplen); - goto wait; + goto retry; } #ifdef RES_USE_EDNS0 if (anhp->rcode == FORMERR && (statp->options & RES_USE_EDNS0) != 0U) { @@ -1049,7 +1191,7 @@ send_dg(res_state statp, (statp->pfcode & RES_PRF_REPLY), (stdout, ";; wrong query name:\n"), ans, (resplen > anssiz) ? anssiz : resplen); - goto wait; + goto retry;; } if (anhp->rcode == SERVFAIL || anhp->rcode == NOTIMP || diff --git a/libc/netbsd/resolv/res_state.c b/libc/netbsd/resolv/res_state.c index 3209b6f..e05846a 100644 --- a/libc/netbsd/resolv/res_state.c +++ b/libc/netbsd/resolv/res_state.c @@ -38,21 +38,32 @@ #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_ #include <sys/_system_properties.h> +/* Set to 1 to enable debug traces */ +#define DEBUG 0 + +#if DEBUG +# include <logd.h> +# include <unistd.h> /* for gettid() */ +# define D(...) __libc_android_log_print(ANDROID_LOG_DEBUG,"libc", __VA_ARGS__) +#else +# define D(...) do{}while(0) +#endif + static pthread_key_t _res_key; static pthread_once_t _res_once; typedef struct { - int _h_errno; - struct __res_state _nres[1]; - unsigned _serial; - struct prop_info* _pi; - struct res_static _rstatic[1]; + int _h_errno; + struct __res_state _nres[1]; + unsigned _serial; + struct prop_info* _pi; + struct res_static _rstatic[1]; } _res_thread; static _res_thread* _res_thread_alloc(void) { - _res_thread* rt = malloc(sizeof(*rt)); + _res_thread* rt = calloc(1, sizeof(*rt)); if (rt) { rt->_h_errno = 0; @@ -62,12 +73,7 @@ _res_thread_alloc(void) if (rt->_pi) { rt->_serial = rt->_pi->serial; } - if ( res_ninit( rt->_nres ) < 0 ) { - free(rt); - rt = NULL; - } else { - memset(rt->_rstatic, 0, sizeof rt->_rstatic); - } + memset(rt->_rstatic, 0, sizeof rt->_rstatic); } return rt; } @@ -91,6 +97,8 @@ _res_thread_free( void* _rt ) { _res_thread* rt = _rt; + D("%s: rt=%p for thread=%d", __FUNCTION__, rt, gettid()); + _res_static_done(rt->_rstatic); res_ndestroy(rt->_nres); free(rt); @@ -108,29 +116,60 @@ _res_thread_get(void) _res_thread* rt; pthread_once( &_res_once, _res_init_key ); rt = pthread_getspecific( _res_key ); - if (rt == NULL) { - if ((rt = _res_thread_alloc()) == NULL) { - return NULL; + + if (rt != NULL) { + /* We already have one thread-specific DNS state object. + * Check the serial value for any changes to net.* properties */ + D("%s: Called for tid=%d rt=%p rt->pi=%p rt->serial=%d", + __FUNCTION__, gettid(), rt, rt->_pi, rt->_serial); + if (rt->_pi == NULL) { + /* The property wasn't created when _res_thread_get() was + * called the last time. This should only happen very + * early during the boot sequence. First, let's try to see if it + * is here now. */ + rt->_pi = (struct prop_info*) __system_property_find("net.change"); + if (rt->_pi == NULL) { + /* Still nothing, return current state */ + D("%s: exiting for tid=%d rt=%d since system property not found", + __FUNCTION__, gettid(), rt); + return rt; + } } - rt->_h_errno = 0; - rt->_serial = 0; - pthread_setspecific( _res_key, rt ); - } - /* Check the serial value for any chanes to net.* properties. */ - if (rt->_pi == NULL) { - rt->_pi = (struct prop_info*) __system_property_find("net.change"); + if (rt->_serial == rt->_pi->serial) { + /* Nothing changed, so return the current state */ + D("%s: tid=%d rt=%p nothing changed, returning", + __FUNCTION__, gettid(), rt); + return rt; + } + /* Update the recorded serial number, and go reset the state */ + rt->_serial = rt->_pi->serial; + goto RESET_STATE; } - if (rt->_pi == NULL || rt->_serial == rt->_pi->serial) { - return rt; + + /* It is the first time this function is called in this thread, + * we need to create a new thread-specific DNS resolver state. */ + rt = _res_thread_alloc(); + if (rt == NULL) { + return NULL; } - rt->_serial = rt->_pi->serial; - /* Reload from system properties. */ + pthread_setspecific( _res_key, rt ); + D("%s: tid=%d Created new DNS state rt=%p", + __FUNCTION__, gettid(), rt); + +RESET_STATE: + /* Reset the state, note that res_ninit() can now properly reset + * an existing state without leaking memory. + */ + D("%s: tid=%d, rt=%p, resetting DNS state (options RES_INIT=%d)", + __FUNCTION__, gettid(), rt, (rt->_nres->options & RES_INIT) != 0); if ( res_ninit( rt->_nres ) < 0 ) { - free(rt); - rt = NULL; - pthread_setspecific( _res_key, rt ); + /* This should not happen */ + D("%s: tid=%d rt=%p, woot, res_ninit() returned < 0", + __FUNCTION__, gettid(), rt); + _res_thread_free(rt); + pthread_setspecific( _res_key, NULL ); + return NULL; } - _resolv_cache_reset(rt->_serial); return rt; } |