From 6bd2adf45125649541fdcdad99971d55d78486c9 Mon Sep 17 00:00:00 2001 From: Denis Vlasenko Date: Fri, 14 Nov 2008 00:37:48 +0000 Subject: fixing resolver part 3: fix completely bogus locking in __dns_lookup. --- libc/inet/resolv.c | 615 +++++++++++++++++++++++++++-------------------------- 1 file changed, 311 insertions(+), 304 deletions(-) (limited to 'libc/inet') diff --git a/libc/inet/resolv.c b/libc/inet/resolv.c index b7ab27690..d388db13b 100644 --- a/libc/inet/resolv.c +++ b/libc/inet/resolv.c @@ -222,16 +222,16 @@ int __libc_getdomainname(char *name, size_t len); libc_hidden_proto(__libc_getdomainname) -#define MAX_RECURSE 5 +#define MAX_RECURSE 5 #define REPLY_TIMEOUT 10 -#define MAX_RETRIES 3 -#define MAX_SERVERS 3 -#define MAX_SEARCH 4 +#define MAX_RETRIES 3 +#define MAX_SERVERS 3 +#define MAX_SEARCH 4 -#define MAX_ALIASES 5 +#define MAX_ALIASES 5 /* 1:ip + 1:full + MAX_ALIASES:aliases + 1:NULL */ -#define ALIAS_DIM (2 + MAX_ALIASES + 1) +#define ALIAS_DIM (2 + MAX_ALIASES + 1) #undef DEBUG /* #define DEBUG */ @@ -251,14 +251,6 @@ libc_hidden_proto(__libc_getdomainname) #define ALIGN_BUFFER_OFFSET(buf) ((ALIGN_ATTR - ((size_t)buf % ALIGN_ATTR)) % ALIGN_ATTR) -/* Global stuff (stuff needing to be locked to be thread safe)... */ -extern int __nameservers attribute_hidden; -extern char * __nameserver[MAX_SERVERS] attribute_hidden; -extern int __searchdomains attribute_hidden; -extern char * __searchdomain[MAX_SEARCH] attribute_hidden; - - - /* Structs */ struct resolv_header { int id; @@ -294,6 +286,26 @@ enum etc_hosts_action { GET_HOSTS_BYADDR, }; +typedef union sockaddr46_t { + struct sockaddr sa; +#ifdef __UCLIBC_HAS_IPV4__ + struct sockaddr_in sa4; +#endif +#ifdef __UCLIBC_HAS_IPV6__ + struct sockaddr_in6 sa6; +#endif +} sockaddr46_t; + + +/* Protected by __resolv_lock */ +extern int __nameservers attribute_hidden; +extern int __searchdomains attribute_hidden; +extern sockaddr46_t *__nameserver attribute_hidden; +extern char **__searchdomain attribute_hidden; +/* Arbitrary */ +#define MAXLEN_searchdomain 128 + + /* function prototypes */ extern int __get_hosts_byname_r(const char * name, int type, struct hostent * result_buf, @@ -312,8 +324,8 @@ extern int __read_etc_hosts_r(FILE *fp, const char * name, int type, char * buf, size_t buflen, struct hostent ** result, int * h_errnop) attribute_hidden; -extern int __dns_lookup(const char * name, int type, int nscount, - char ** nsip, unsigned char ** outpacket, struct resolv_answer * a) attribute_hidden; +extern int __dns_lookup(const char * name, int type, + unsigned char ** outpacket, struct resolv_answer * a) attribute_hidden; extern int __encode_dotted(const char * dotted, unsigned char * dest, int maxlen) attribute_hidden; extern int __decode_dotted(const unsigned char * const message, int offset, @@ -731,16 +743,10 @@ int __form_query(int id, const char *name, int type, unsigned char *packet, #endif #ifdef L_dnslookup -__UCLIBC_MUTEX_STATIC(mylock, PTHREAD_MUTEX_INITIALIZER); -/* Just for the record, having to lock __dns_lookup() just for these two globals - * is pretty lame. I think these two variables can probably be de-global-ized, - * which should eliminate the need for doing locking here... Needs a closer - * look anyways. */ -static int static_ns = 0; -/* uint16: minimizing rw data size, even if code grows a tiny bit. - * rw data costs more. */ -static uint16_t static_id = 1; +/* Protected by __resolv_lock */ +static int last_ns_num = 0; +static uint16_t last_id = 1; /* On entry: * a.buf(len) = auxiliary buffer for IP addresses after first one @@ -759,15 +765,11 @@ static uint16_t static_id = 1; * This is a malloced string. May be NULL because strdup failed. */ int attribute_hidden __dns_lookup(const char *name, int type, - int nscount, char **nsip, unsigned char **outpacket, struct resolv_answer *a) -//FIXME: nscount/nsip are *always* taken from __nameservers and __nameserver[] -//globals. Locking is busted: __nameserver[] can be freed under us. -//Fix it by eliminating these params and accessing __nameserver(s) -//only under lock inside this routine. { int i, j, len, fd, pos, rc; + int name_len; #ifdef USE_SELECT struct timeval tv; fd_set fds; @@ -780,119 +782,127 @@ int attribute_hidden __dns_lookup(const char *name, int type, bool first_answer = 1; unsigned retries = 0; unsigned char *packet = malloc(PACKETSZ); - char *dns, *lookup = malloc(MAXDNAME); - int variant = -1; /* search domain to append, -1 - none */ - int local_ns = -1, local_id = -1; + char *lookup; + int variant = -1; /* search domain to append, -1: none */ + int local_ns_num = -1; /* Nth server to use */ + int local_id = local_id; /* for compiler */ + int sdomains; bool ends_with_dot; - union { - struct sockaddr sa; -#ifdef __UCLIBC_HAS_IPV4__ - struct sockaddr_in sa4; -#endif -#ifdef __UCLIBC_HAS_IPV6__ - struct sockaddr_in6 sa6; -#endif - } sa; + sockaddr46_t sa; fd = -1; - - if (!packet || !lookup || !nscount || !name[0]) + lookup = NULL; + name_len = strlen(name); + if ((unsigned)name_len >= MAXDNAME - MAXLEN_searchdomain - 2) + goto fail; /* paranoia */ + lookup = malloc(name_len + 1/*for '.'*/ + MAXLEN_searchdomain + 1); + if (!packet || !lookup || !name[0]) goto fail; - + ends_with_dot = (name[name_len - 1] == '.'); DPRINTF("Looking up type %d answer for '%s'\n", type, name); - ends_with_dot = (name[strlen(name) - 1] == '.'); - - /* Mess with globals while under lock */ - __UCLIBC_MUTEX_LOCK(mylock); - local_ns = static_ns % nscount; - local_id = static_id; - __UCLIBC_MUTEX_UNLOCK(mylock); - while (retries < MAX_RETRIES) { - if (fd != -1) + if (fd != -1) { close(fd); + fd = -1; + } - memset(packet, 0, PACKETSZ); + /* no strcpy! paranoia, user might change name[] under us */ + memcpy(lookup, name, name_len); + lookup[name_len] = '\0'; + /* Mess with globals while under lock */ + /* NB: even data *pointed to* by globals may vanish + * outside the locks. We should assume any and all + * globals can completely change between locked + * code regions. OTOH, this is rare, so we don't need + * to handle it "nicely" (do not skip servers, + * search domains, etc), we only need to ensure + * we do not SEGV, use freed+overwritten data + * or do other Really Bad Things. */ + __UCLIBC_MUTEX_LOCK(__resolv_lock); + __open_nameservers(); + sdomains = __searchdomains; + if ((unsigned)variant < sdomains) { + /* lookup is name_len + 1 + MAXLEN_searchdomain + 1 long */ + /* __searchdomain[] is not bigger than MAXLEN_searchdomain */ + lookup[name_len] = '.'; + strcpy(&lookup[name_len + 1], __searchdomain[variant]); + } + if (local_ns_num < 0) { /* first time */ + local_id = last_id; +//TODO: implement /etc/resolv.conf's "options rotate" +// (a.k.a. RES_ROTATE bit in _res.options) +// local_ns_num = 0; +// if (_res.options & RES_ROTATE) + local_ns_num = last_ns_num; + if (local_ns_num >= __nameservers) + local_ns_num = 0; + } + /* __nameservers == 0 case: act as if we + * have one DNS server configured - on 127.0.0.1 */ + { + int my_nameservers = __nameservers; + if (my_nameservers == 0) + my_nameservers++; + if (local_ns_num >= my_nameservers) { + local_ns_num = 0; + retries++; + /* break if retries >= MAX_RETRIES - *after unlock*! */ + } + } + local_id++; + local_id &= 0xffff; + /* write new values back while still under lock */ + last_id = local_id; + last_ns_num = local_ns_num; + if (__nameservers != 0) { + /* struct copy */ + /* can't just take a pointer, __nameserver[] + * is not safe to use outside of locks */ + sa = __nameserver[local_ns_num]; + } else { + memset(&sa, 0, sizeof(sa)); +#ifdef __UCLIBC_HAS_IPV4__ + sa.sa4.sin_family = AF_INET; + /*sa.sa4.sin_addr = INADDR_ANY; - done by memset */ + sa.sa4.sin_port = htons(NAMESERVER_PORT); +#else + sa.sa6.sin_family = AF_INET6; + sa.sa6.sin6_port = htons(NAMESERVER_PORT); +#endif + } + __UCLIBC_MUTEX_UNLOCK(__resolv_lock); + if (retries >= MAX_RETRIES) + break; + + memset(packet, 0, PACKETSZ); memset(&h, 0, sizeof(h)); - ++local_id; - local_id &= 0xffff; + /* encode header */ h.id = local_id; h.qdcount = 1; h.rd = 1; - DPRINTF("encoding header\n", h.rd); - i = __encode_header(&h, packet, PACKETSZ); if (i < 0) goto fail; - strncpy(lookup, name, MAXDNAME); - __UCLIBC_MUTEX_LOCK(__resolv_lock); - /* nsip is really __nameserver[] which is a global that - needs to hold __resolv_lock before access!! */ - dns = nsip[local_ns]; -/* TODO: all future accesses to 'dns' were guarded by __resolv_lock too. - * Why? We already fetched nsip[local_ns] here, - * future changes to nsip[] by other threads cannot affect us. - * We can use 'dns' without locking. If I'm wrong, - * please explain in comments why locking is needed. - * One thing that worries me is - what if __close_nameservers() free()s - * dns under us? __resolv_lock'ing around accesses to dns won't help either, - * as free() might occur between accesses! - */ - if (variant >= 0) { - if (variant < __searchdomains) { - strncat(lookup, ".", MAXDNAME); - strncat(lookup, __searchdomain[variant], MAXDNAME); - } - } - __UCLIBC_MUTEX_UNLOCK(__resolv_lock); - + /* encode question */ DPRINTF("lookup name: %s\n", lookup); q.dotted = lookup; q.qtype = type; q.qclass = C_IN; /* CLASS_IN */ - j = __encode_question(&q, packet+i, PACKETSZ-i); if (j < 0) goto fail; - len = i + j; - DPRINTF("On try %d, sending query to port %d of machine %s\n", - retries+1, NAMESERVER_PORT, dns); - - sa.sa.sa_family = AF_INET; -#ifdef __UCLIBC_HAS_IPV6__ - //__UCLIBC_MUTEX_LOCK(__resolv_lock); - ///* 'dns' is really __nameserver[] which is a global that - // needs to hold __resolv_lock before access!! */ - if (inet_pton(AF_INET6, dns, &sa.sa6.sin6_addr) > 0) - sa.sa.sa_family = AF_INET6; - //__UCLIBC_MUTEX_UNLOCK(__resolv_lock); -#endif - /* Connect to the UDP socket so that asyncronous errors are returned */ -#ifdef __UCLIBC_HAS_IPV6__ - if (sa.sa.sa_family == AF_INET6) { - sa.sa6.sin6_port = htons(NAMESERVER_PORT); - /* sa6.sin6_addr is already here */ - } else -#endif -#ifdef __UCLIBC_HAS_IPV4__ - { - sa.sa4.sin_port = htons(NAMESERVER_PORT); - //__UCLIBC_MUTEX_LOCK(__resolv_lock); - ///* 'dns' is really __nameserver[] which is a global that - // needs to hold __resolv_lock before access!! */ - sa.sa4.sin_addr.s_addr = inet_addr(dns); - //__UCLIBC_MUTEX_UNLOCK(__resolv_lock); - } -#endif + /* send packet */ + DPRINTF("On try %d, sending query to port %d\n", + retries+1, NAMESERVER_PORT); fd = socket(sa.sa.sa_family, SOCK_DGRAM, IPPROTO_UDP); - if (fd < 0) { + if (fd < 0) { /* paranoia */ retries++; continue; } @@ -900,16 +910,13 @@ int attribute_hidden __dns_lookup(const char *name, int type, if (rc < 0) { if (errno == ENETUNREACH) { /* routing error, presume not transient */ - goto tryall; + goto try_next_server; } /* retry */ retries++; continue; } - - DPRINTF("Transmitting packet of length %d, id=%d, qr=%d\n", - len, h.id, h.qr); - + DPRINTF("Xmit packet len:%d id:%d qr:%d\n", len, h.id, h.qr); send(fd, packet, len, 0); #ifdef USE_SELECT @@ -919,34 +926,35 @@ int attribute_hidden __dns_lookup(const char *name, int type, tv.tv_usec = 0; if (select(fd + 1, &fds, NULL, NULL, &tv) <= 0) { DPRINTF("Timeout\n"); - /* timed out, so retry send and receive, - * to next nameserver on queue */ - goto tryall; + /* timed out, so retry send and receive + * to next nameserver */ + goto try_next_server; } #else fds.fd = fd; fds.events = POLLIN; if (poll(&fds, 1, REPLY_TIMEOUT * 1000) <= 0) { DPRINTF("Timeout\n"); - /* timed out, so retry send and receive, - * to next nameserver on queue */ - goto tryall; + /* timed out, so retry send and receive + * to next nameserver */ + goto try_next_server; } #endif - +//TODO: MSG_DONTWAIT? len = recv(fd, packet, PACKETSZ, 0); if (len < HFIXEDSZ) { /* too short! */ - goto again; +//TODO: why next sdomain? it's just a bogus packet from somewhere, +// we can as well wait more... + goto try_next_sdomain; } __decode_header(packet, &h); - DPRINTF("id = %d, qr = %d\n", h.id, h.qr); - if ((h.id != local_id) || (!h.qr)) { /* unsolicited */ - goto again; +//TODO: why next sdomain?... + goto try_next_sdomain; } DPRINTF("Got response (i think)!\n"); @@ -960,12 +968,9 @@ int attribute_hidden __dns_lookup(const char *name, int type, // which is, eh, an error. :) We were incurring long delays because of this. /* if possible, try next search domain */ if (!ends_with_dot) { - int sdomains; - __UCLIBC_MUTEX_LOCK(__resolv_lock); - sdomains = __searchdomains; - __UCLIBC_MUTEX_UNLOCK(__resolv_lock); + DPRINTF("variant:%d sdomains:%d\n", variant, sdomains); if (variant < sdomains - 1) { - /* next search */ + /* next search domain */ variant++; continue; } @@ -975,21 +980,20 @@ int attribute_hidden __dns_lookup(const char *name, int type, goto fail1; } if (h.rcode != 0) /* error */ - goto again; + goto try_next_sdomain; /* code below won't work correctly with h.ancount == 0, so... */ - if (h.ancount < 1) { + if (h.ancount <= 0) { h_errno = NO_DATA; /* is this correct code? */ goto fail1; } pos = HFIXEDSZ; - for (j = 0; j < h.qdcount; j++) { DPRINTF("Skipping question %d at %d\n", j, pos); /* returns -1 only if packet == NULL (can't happen) */ i = __length_question(packet, pos); //if (i < 0) - // goto again; + // goto try_next_sdomain; DPRINTF("Length of question %d is %d\n", j, i); pos += i; } @@ -1001,10 +1005,10 @@ int attribute_hidden __dns_lookup(const char *name, int type, if (i < 0) { DPRINTF("failed decode %d\n", i); /* if the message was truncated and we have - decoded some answers, pretend it's OK */ + * decoded some answers, pretend it's OK */ if (j && h.tc) break; - goto again; + goto try_next_sdomain; } pos += i; @@ -1032,7 +1036,7 @@ int attribute_hidden __dns_lookup(const char *name, int type, free(a->dotted); DPRINTF("Answer address len(%u) differs from original(%u)\n", ma.rdlength, a->rdlength); - goto again; + goto try_next_sdomain; } memcpy(a->buf + (a->add_count * ma.rdlength), ma.rdata, ma.rdlength); ++a->add_count; @@ -1041,52 +1045,29 @@ int attribute_hidden __dns_lookup(const char *name, int type, DPRINTF("Answer name = |%s|\n", a->dotted); DPRINTF("Answer type = |%d|\n", a->atype); - - close(fd); - + if (fd != -1) + close(fd); if (outpacket) *outpacket = packet; else free(packet); free(lookup); + return len; /* success! */ - /* Mess with globals while under lock */ - __UCLIBC_MUTEX_LOCK(mylock); - static_ns = local_ns; - static_id = local_id; - __UCLIBC_MUTEX_UNLOCK(mylock); - - return len; /* success! */ - - tryall: - /* if there are other nameservers, give them a go, - otherwise return with error */ - variant = -1; - local_ns = (local_ns + 1) % nscount; - if (local_ns == 0) - retries++; - - continue; - - again: - /* if there are searchdomains, try them or fallback as passed */ + try_next_sdomain: + /* if there are searchdomains, try them */ if (!ends_with_dot) { - int sdomains; - __UCLIBC_MUTEX_LOCK(__resolv_lock); - sdomains = __searchdomains; - __UCLIBC_MUTEX_UNLOCK(__resolv_lock); if (variant < sdomains - 1) { /* next search */ variant++; continue; } } - /* next server, first search */ - local_ns = (local_ns + 1) % nscount; - if (local_ns == 0) - retries++; + try_next_server: + /* if there are other nameservers, try them */ + local_ns_num++; variant = -1; - } + } /* while (retries < MAX_RETRIES) */ fail: h_errno = NETDB_INTERNAL; @@ -1095,116 +1076,161 @@ int attribute_hidden __dns_lookup(const char *name, int type, close(fd); free(lookup); free(packet); - /* Mess with globals while under lock */ - if (local_ns != -1) { - __UCLIBC_MUTEX_LOCK(mylock); - static_ns = local_ns; - static_id = local_id; - __UCLIBC_MUTEX_UNLOCK(mylock); - } return -1; } #endif #ifdef L_opennameservers -/* We use __resolv_lock to guard access to the - * '__nameservers' and __searchdomains globals */ +//TODO: need not be recursive +__UCLIBC_MUTEX_INIT(__resolv_lock, PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP); + +/* Protected by __resolv_lock */ int __nameservers; -char * __nameserver[MAX_SERVERS]; int __searchdomains; -char * __searchdomain[MAX_SEARCH]; +sockaddr46_t *__nameserver; +char **__searchdomain; -__UCLIBC_MUTEX_INIT(__resolv_lock, PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP); - -/* - * we currently read formats not quite the same as that on normal - * unix systems, we can have a list of nameservers after the keyword. - */ +/* Helpers. Both stop on EOL, if it's '\n', it is converted to NUL first */ +static char *skip_nospace(char *p) +{ + while (*p != '\0' && !isspace(*p)) { + if (*p == '\n') { + *p = '\0'; + break; + } + p++; + } + return p; +} +static char *skip_and_NUL_space(char *p) +{ + /* NB: '\n' is not isspace! */ + while (1) { + char c = *p; + if (c == '\0' || !isspace(c)) + break; + *p = '\0'; + if (c == '\n' || c == '#') + break; + p++; + } + return p; +} +/* Must be called under __resolv_lock. */ void attribute_hidden __open_nameservers(void) { + char szBuffer[MAXLEN_searchdomain]; FILE *fp; int i; -#define RESOLV_ARGS 5 - char szBuffer[128], *p, *argv[RESOLV_ARGS]; - int argc; + sockaddr46_t sa; - __UCLIBC_MUTEX_LOCK(__resolv_lock); if (__nameservers > 0) - goto DONE; + return; - if ((fp = fopen("/etc/resolv.conf", "r")) || - (fp = fopen("/etc/config/resolv.conf", "r"))) - { - while (fgets(szBuffer, sizeof(szBuffer), fp) != NULL) { - for (p = szBuffer; *p && isspace(*p); p++) - /* skip white space */; - if (*p == '\0' || *p == '\n' || *p == '#') /* skip comments etc */ - continue; - argc = 0; - while (*p && argc < RESOLV_ARGS) { - argv[argc++] = p; - while (*p && !isspace(*p) && *p != '\n') - p++; - while (*p && (isspace(*p) || *p == '\n')) /* remove spaces */ - *p++ = '\0'; - } + fp = fopen("/etc/resolv.conf", "r"); + if (!fp) { + fp = fopen("/etc/config/resolv.conf", "r"); + if (!fp) { + DPRINTF("failed to open %s\n", "resolv.conf"); + h_errno = NO_RECOVERY; + return; + } + } - if (strcmp(argv[0], "nameserver") == 0) { - for (i = 1; i < argc && __nameservers < MAX_SERVERS; i++) { -// TODO: what if strdup fails? - __nameserver[__nameservers++] = strdup(argv[i]); - DPRINTF("adding nameserver %s\n", argv[i]); - } + while (fgets(szBuffer, sizeof(szBuffer), fp) != NULL) { + void *ptr; + char *keyword, *p; + + keyword = p = skip_and_NUL_space(szBuffer); + /* skip keyword */ + p = skip_nospace(p); + /* find next word */ + p = skip_and_NUL_space(p); + + if (strcmp(keyword, "nameserver") == 0) { + /* terminate IP addr */ + *skip_nospace(p) = '\0'; + memset(&sa, 0, sizeof(sa)); + if (0) /* nothing */; +#ifdef __UCLIBC_HAS_IPV6__ + else if (inet_pton(AF_INET6, p, &sa.sa6.sin6_addr) > 0) { + sa.sa6.sin6_family = AF_INET6; + sa.sa6.sin6_port = htons(NAMESERVER_PORT); } - - /* domain and search are mutually exclusive, the last one wins */ - if (strcmp(argv[0],"domain") == 0 || strcmp(argv[0],"search") == 0) { - while (__searchdomains > 0) { - free(__searchdomain[--__searchdomains]); - __searchdomain[__searchdomains] = NULL; - } - for (i = 1; i < argc && __searchdomains < MAX_SEARCH; i++) { -// TODO: what if strdup fails? - __searchdomain[__searchdomains++] = strdup(argv[i]); - DPRINTF("adding search %s\n", argv[i]); - } +#endif +#ifdef __UCLIBC_HAS_IPV4__ + else if (inet_pton(AF_INET, p, &sa.sa4.sin_addr) > 0) { + sa.sa4.sin_family = AF_INET; + sa.sa4.sin_port = htons(NAMESERVER_PORT); } +#endif + else + continue; /* garbage on this line */ + ptr = realloc(__nameserver, (__nameservers + 1) * sizeof(__nameserver[0])); + if (!ptr) + continue; + __nameserver = ptr; + __nameserver[__nameservers++] = sa; /* struct copy */ + continue; } - fclose(fp); - DPRINTF("nameservers = %d\n", __nameservers); - goto DONE; + if (strcmp(keyword, "domain") == 0 || strcmp(keyword, "search") == 0) { + char *p1; + next_word: + /* terminate current word */ + p1 = skip_nospace(p); + /* find next word (maybe) */ + p1 = skip_and_NUL_space(p1); + /* paranoia - done by having szBuffer[MAXLEN_searchdomain] */ + /*if (strlen(p) > MAXLEN_searchdomain)*/ + /* goto skip;*/ + /* do we have this domain already? */ + for (i = 0; i < __searchdomains; i++) + if (strcmp(p, __searchdomain[i]) == 0) + goto skip; + /* add it */ + ptr = realloc(__searchdomain, (__searchdomains + 1) * sizeof(__searchdomain[0])); + if (!ptr) + continue; + __searchdomain = ptr; + ptr = strdup(p); + if (!ptr) + continue; + DPRINTF("adding search %s\n", (char*)ptr); + __searchdomain[__searchdomains++] = (char*)ptr; + skip: + p = p1; + if (*p) + goto next_word; + continue; + } + /* if (strcmp(keyword, "sortlist") == 0)... */ + /* if (strcmp(keyword, "options") == 0)... */ } - DPRINTF("failed to open %s\n", "resolv.conf"); - h_errno = NO_RECOVERY; - - /* rv = -1; */ - - DONE: - __UCLIBC_MUTEX_UNLOCK(__resolv_lock); - /* return rv; */ + fclose(fp); + DPRINTF("nameservers = %d\n", __nameservers); } #endif #ifdef L_closenameservers +/* Must be called under __resolv_lock. */ void attribute_hidden __close_nameservers(void) { - __UCLIBC_MUTEX_LOCK(__resolv_lock); - while (__nameservers > 0) { - free(__nameserver[--__nameservers]); - __nameserver[__nameservers] = NULL; - } - while (__searchdomains > 0) { + free(__nameserver); + __nameserver = NULL; + __nameservers = 0; + while (__searchdomains) free(__searchdomain[--__searchdomains]); - __searchdomain[__searchdomains] = NULL; - } - __UCLIBC_MUTEX_UNLOCK(__resolv_lock); + free(__searchdomain); + __searchdomain = NULL; + /*__searchdomains = 0; - already is */ } #endif + #ifdef L_gethostbyname struct hostent *gethostbyname(const char *name) @@ -1222,6 +1248,7 @@ struct hostent *gethostbyname(const char *name) libc_hidden_def(gethostbyname) #endif + #ifdef L_gethostbyname2 struct hostent *gethostbyname2(const char *name, int family) @@ -1240,21 +1267,24 @@ struct hostent *gethostbyname2(const char *name, int family) return hp; #endif /* __UCLIBC_HAS_IPV6__ */ } -#endif +#endif #ifdef L_res_init + /* We use __resolv_lock to guard access to global '_res' */ struct __res_state _res; int res_init(void) { + int i, n; struct __res_state *rp = &(_res); - __UCLIBC_MUTEX_LOCK(__resolv_lock); /* must be a recursive lock! */ + __UCLIBC_MUTEX_LOCK(__resolv_lock); __close_nameservers(); __open_nameservers(); + memset(rp, 0, sizeof(*rp)); rp->options = RES_INIT; #ifdef __UCLIBC_HAS_COMPAT_RES_STATE__ @@ -1276,26 +1306,28 @@ int res_init(void) rp->_vcsock = -1; #endif - if (__searchdomains) { - int i; - for (i = 0; i < __searchdomains; i++) - rp->dnsrch[i] = __searchdomain[i]; - } - - if (__nameservers) { - int i; - struct in_addr a; - for (i = 0; i < __nameservers; i++) { - if (inet_aton(__nameserver[i], &a)) { - rp->nsaddr_list[i].sin_addr = a; - rp->nsaddr_list[i].sin_family = AF_INET; - rp->nsaddr_list[i].sin_port = htons(NAMESERVER_PORT); - } - } - rp->nscount = __nameservers; +#undef ARRAY_SIZE +#define ARRAY_SIZE(v) (sizeof(v) / sizeof((v)[0])) + n = __searchdomains; + if (n > ARRAY_SIZE(rp->dnsrch)) + n = ARRAY_SIZE(rp->dnsrch); + for (i = 0; i < n; i++) + rp->dnsrch[i] = __searchdomain[i]; + i = 0; + n = 0; + while (n < ARRAY_SIZE(rp->nsaddr_list) && i < __nameservers) { + if (__nameserver[i].sa.sa_family != AF_INET) + goto next_i; + rp->nsaddr_list[n] = __nameserver[i].sa4; /* struct copy */ + n++; + next_i: + i++; } + if (n) + rp->nscount = n; + /* else stays 1 */ +#undef ARRAY_SIZE __UCLIBC_MUTEX_UNLOCK(__resolv_lock); - return 0; } libc_hidden_def(res_init) @@ -1303,8 +1335,10 @@ libc_hidden_def(res_init) #ifdef __UCLIBC_HAS_BSD_RES_CLOSE__ void res_close(void) { + __UCLIBC_MUTEX_LOCK(__resolv_lock); __close_nameservers(); memset(&_res, 0, sizeof(_res)); + __UCLIBC_MUTEX_UNLOCK(__resolv_lock); } #endif @@ -1323,8 +1357,6 @@ int res_query(const char *dname, int class, int type, int i; unsigned char * packet = NULL; struct resolv_answer a; - int __nameserversXX; - char ** __nameserverXX; if (!dname || class != 1 /* CLASS_IN */) { h_errno = NO_RECOVERY; @@ -1332,13 +1364,7 @@ int res_query(const char *dname, int class, int type, } memset(&a, '\0', sizeof(a)); - __open_nameservers(); - - __UCLIBC_MUTEX_LOCK(__resolv_lock); - __nameserversXX = __nameservers; - __nameserverXX = __nameserver; - __UCLIBC_MUTEX_UNLOCK(__resolv_lock); - i = __dns_lookup(dname, type, __nameserversXX, __nameserverXX, &packet, &a); + i = __dns_lookup(dname, type, &packet, &a); if (i < 0) { h_errno = TRY_AGAIN; @@ -1381,6 +1407,8 @@ int res_search(const char *name, int class, int type, u_char *answer, __UCLIBC_MUTEX_LOCK(__resolv_lock); _res_options = _res.options; __UCLIBC_MUTEX_UNLOCK(__resolv_lock); +//FIXME: locking is bogus +//FIXME: our res_init never fails if ((!name || !answer) || ((_res_options & RES_INIT) == 0 && res_init() == -1)) { h_errno = NETDB_INTERNAL; return -1; @@ -1528,6 +1556,8 @@ int res_querydomain(const char *name, const char *domain, int class, int type, __UCLIBC_MUTEX_LOCK(__resolv_lock); _res_options = _res.options; __UCLIBC_MUTEX_UNLOCK(__resolv_lock); +//FIXME: locking is bogus +//FIXME: our res_init never fails if ((!name || !answer) || ((_res_options & RES_INIT) == 0 && res_init() == -1)) { h_errno = NETDB_INTERNAL; return -1; @@ -2214,20 +2244,13 @@ int gethostbyname_r(const char * name, } /* talk to DNS servers */ - __open_nameservers(); { - int __nameserversXX; - char ** __nameserverXX; - __UCLIBC_MUTEX_LOCK(__resolv_lock); - __nameserversXX = __nameservers; - __nameserverXX = __nameserver; - __UCLIBC_MUTEX_UNLOCK(__resolv_lock); a.buf = buf; /* take into account that at least one address will be there, * we'll need space of one in_addr + two addr_list[] elems */ a.buflen = buflen - ((sizeof(addr_list[0]) * 2 + sizeof(struct in_addr))); a.add_count = 0; - i = __dns_lookup(name, T_A, __nameserversXX, __nameserverXX, &packet, &a); + i = __dns_lookup(name, T_A, &packet, &a); if (i < 0) { *h_errnop = HOST_NOT_FOUND; DPRINTF("__dns_lookup returned < 0\n"); @@ -2319,8 +2342,6 @@ int gethostbyname2_r(const char *name, int family, struct resolv_answer a; int i; int nest = 0; - int __nameserversXX; - char ** __nameserverXX; int wrong_af = 0; if (family == AF_INET) @@ -2329,7 +2350,6 @@ int gethostbyname2_r(const char *name, int family, if (family != AF_INET6) return EINVAL; - __open_nameservers(); *result = NULL; if (!name) return EINVAL; @@ -2405,12 +2425,7 @@ int gethostbyname2_r(const char *name, int family, memset(&a, '\0', sizeof(a)); for (;;) { - __UCLIBC_MUTEX_LOCK(__resolv_lock); - __nameserversXX = __nameservers; - __nameserverXX = __nameserver; - __UCLIBC_MUTEX_UNLOCK(__resolv_lock); - - i = __dns_lookup(buf, T_AAAA, __nameserversXX, __nameserverXX, &packet, &a); + i = __dns_lookup(buf, T_AAAA, &packet, &a); if (i < 0) { *h_errnop = HOST_NOT_FOUND; @@ -2478,8 +2493,6 @@ int gethostbyaddr_r(const void *addr, socklen_t len, int type, struct resolv_answer a; int i; int nest = 0; - int __nameserversXX; - char ** __nameserverXX; *result = NULL; if (!addr) @@ -2514,8 +2527,6 @@ int gethostbyaddr_r(const void *addr, socklen_t len, int type, return i; } - __open_nameservers(); - #ifdef __UCLIBC_HAS_IPV6__ qp = buf; plen = buflen; @@ -2592,11 +2603,7 @@ int gethostbyaddr_r(const void *addr, socklen_t len, int type, alias[1] = 0; for (;;) { - __UCLIBC_MUTEX_LOCK(__resolv_lock); - __nameserversXX = __nameservers; - __nameserverXX = __nameserver; - __UCLIBC_MUTEX_UNLOCK(__resolv_lock); - i = __dns_lookup(buf, T_PTR, __nameserversXX, __nameserverXX, &packet, &a); + i = __dns_lookup(buf, T_PTR, &packet, &a); if (i < 0) { *h_errnop = HOST_NOT_FOUND; -- cgit v1.2.3