diff options
-rw-r--r-- | include/resolv.h | 33 | ||||
-rw-r--r-- | libc/inet/resolv.c | 162 |
2 files changed, 140 insertions, 55 deletions
diff --git a/include/resolv.h b/include/resolv.h index 6651dded9..d5168770d 100644 --- a/include/resolv.h +++ b/include/resolv.h @@ -118,10 +118,9 @@ struct __res_state { int retry; /* number of times to retransmit */ #endif u_int32_t options; /* (was: ulong) option flags - see below. */ - struct sockaddr_in - nsaddr_list[MAXNS]; /* address of name server */ + struct sockaddr_in nsaddr_list[MAXNS]; /* address of name server */ #define nsaddr nsaddr_list[0] /* for backward compatibility */ - char *dnsrch[MAXDNSRCH+1]; /* components of domain to search */ + char *dnsrch[MAXDNSRCH + 1]; /* components of domain to search */ #ifdef __UCLIBC_HAS_COMPAT_RES_STATE__ /* googling for "_res.defdname" says it's still sometimes used. * Pity. It's huge, I want to move it to EXTRA_COMPAT... */ @@ -138,18 +137,38 @@ struct __res_state { struct in_addr addr; u_int32_t mask; } sort_list[MAXRESOLVSORT]; +#endif + +#ifdef __UCLIBC_HAS_IPV6__ + /* I assume that the intention is to store all + * DNS servers' addresses here, and duplicate in nsaddr_list[] + * those which have IPv4 address. In the case of IPv4 address + * _u._ext.nsaddrs[x] will point to some nsaddr_list[y], + * otherwise it will point into malloc'ed sockaddr_in6. + * nscount is the number of IPv4 addresses and _u._ext.nscount + * is the number of addresses of all kinds. + * + * If this differs from established usage and you need + * to change this, please describe how is it supposed to work. + */ union { struct { - u_int16_t nscount; - u_int16_t nstimes[MAXNS]; /* ms. */ + struct sockaddr_in6 *nsaddrs[MAXNS]; + u_int8_t nscount; /* (was: u_int16_t) */ +#ifdef __UCLIBC_HAS_COMPAT_RES_STATE__ + /* rather obscure, and differs in BSD and glibc */ + u_int16_t nstimes[MAXNS]; int nssocks[MAXNS]; - /* below: not in xBSD. glibc only? */ u_int16_t nscount6; u_int16_t nsinit; - struct sockaddr_in6 *nsaddrs[MAXNS]; + /* glibc also has: */ + /*u_int16_t nsmap[MAXNS];*/ + /*unsigned long long initstamp;*/ +#endif } _ext; } _u; #endif + #ifdef __UCLIBC_HAS_EXTRA_COMPAT_RES_STATE__ /* Truly obscure stuff. * Googling for "_res.XXX" for these members diff --git a/libc/inet/resolv.c b/libc/inet/resolv.c index defd4d77c..4a1f97541 100644 --- a/libc/inet/resolv.c +++ b/libc/inet/resolv.c @@ -241,6 +241,9 @@ libc_hidden_proto(__libc_getdomainname) #define DPRINTF(X,args...) #endif /* DEBUG */ +#undef ARRAY_SIZE +#define ARRAY_SIZE(v) (sizeof(v) / sizeof((v)[0])) + /* Make sure the incoming char * buffer is aligned enough to handle our random * structures. This define is the same as we use for malloc alignment (which * has same requirements). The offset is the number of bytes we need to adjust @@ -305,6 +308,11 @@ extern unsigned __nameservers attribute_hidden; extern unsigned __searchdomains attribute_hidden; extern sockaddr46_t *__nameserver attribute_hidden; extern char **__searchdomain attribute_hidden; +#ifdef __UCLIBC_HAS_IPV4__ +extern const struct sockaddr_in __local_nameserver attribute_hidden; +#else +extern const struct sockaddr_in6 __local_nameserver attribute_hidden; +#endif /* Arbitrary */ #define MAXLEN_searchdomain 128 @@ -858,6 +866,12 @@ int __form_query(int id, const char *name, int type, unsigned char *packet, #ifdef L_opennameservers +# if __BYTE_ORDER == __LITTLE_ENDIAN +#define NAMESERVER_PORT_N (__bswap_constant_16(NAMESERVER_PORT)) +#else +#define NAMESERVER_PORT_N NAMESERVER_PORT +#endif + __UCLIBC_MUTEX_INIT(__resolv_lock, PTHREAD_MUTEX_INITIALIZER); /* Protected by __resolv_lock */ @@ -867,6 +881,17 @@ unsigned __nameservers; unsigned __searchdomains; sockaddr46_t *__nameserver; char **__searchdomain; +#ifdef __UCLIBC_HAS_IPV4__ +const struct sockaddr_in __local_nameserver = { + .sin_family = AF_INET, + .sin_port = NAMESERVER_PORT_N, +}; +#else +const struct sockaddr_in6 __local_nameserver = { + .sin6_family = AF_INET6, + .sin6_port = NAMESERVER_PORT_N, +}; +#endif /* Helpers. Both stop on EOL, if it's '\n', it is converted to NUL first */ static char *skip_nospace(char *p) @@ -954,30 +979,28 @@ void attribute_hidden __open_nameservers(void) } if (strcmp(keyword, "domain") == 0 || strcmp(keyword, "search") == 0) { char *p1; -//TODO: delete old domains... + + /* free old domains ("last 'domain' or 'search' wins" rule) */ + while (__searchdomains) + free(__searchdomain[--__searchdomains]); + /*free(__searchdomain);*/ + /*__searchdomain = NULL; - not necessary */ 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; + /* NB: strlen(p) <= MAXLEN_searchdomain) because szBuffer[] is smaller */ 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; @@ -989,17 +1012,7 @@ void attribute_hidden __open_nameservers(void) fclose(fp); } if (__nameservers == 0) { - __nameserver = malloc(sizeof(__nameserver[0])); -//TODO: error check? - memset(&__nameserver[0], 0, sizeof(__nameserver[0])); -#ifdef __UCLIBC_HAS_IPV4__ - __nameserver[0].sa4.sin_family = AF_INET; - /*__nameserver[0].sa4.sin_addr = INADDR_ANY; - done by memset */ - __nameserver[0].sa4.sin_port = htons(NAMESERVER_PORT); -#else - __nameserver[0].sa6.sin6_family = AF_INET6; - __nameserver[0].sa6.sin6_port = htons(NAMESERVER_PORT); -#endif + __nameserver = (void*) &__local_nameserver; __nameservers++; } if (__searchdomains == 0) { @@ -1009,10 +1022,16 @@ void attribute_hidden __open_nameservers(void) buf[sizeof(buf) - 1] = '\0'; if (i == 0 && (p = strchr(buf, '.')) != NULL && p[1]) { p = strdup(p + 1); + if (!p) + goto err; __searchdomain = malloc(sizeof(__searchdomain[0])); -//TODO: error check? + if (!__searchdomain) { + free(p); + goto err; + } __searchdomain[0] = p; __searchdomains++; + err: ; } } DPRINTF("nameservers = %d\n", __nameservers); @@ -1029,7 +1048,8 @@ void attribute_hidden __open_nameservers(void) /* Must be called under __resolv_lock. */ void attribute_hidden __close_nameservers(void) { - free(__nameserver); + if (__nameserver != (void*) &__local_nameserver) + free(__nameserver); __nameserver = NULL; __nameservers = 0; while (__searchdomains) @@ -1383,7 +1403,7 @@ libc_hidden_def(gethostbyname) struct hostent *gethostbyname2(const char *name, int family) { #ifndef __UCLIBC_HAS_IPV6__ - return family == AF_INET ? gethostbyname(name) : (struct hostent*)0; + return family == AF_INET ? gethostbyname(name) : (struct hostent*)NULL; #else /* __UCLIBC_HAS_IPV6__ */ static struct hostent h; static char buf[sizeof(struct in6_addr) + @@ -1404,30 +1424,27 @@ struct hostent *gethostbyname2(const char *name, int family) /* Protected by __resolv_lock */ struct __res_state _res; -#undef ARRAY_SIZE -#define ARRAY_SIZE(v) (sizeof(v) / sizeof((v)[0])) - /* Will be called under __resolv_lock. */ static void res_sync_func(void) { struct __res_state *rp = &(_res); + int n; - /* Track .nscount and .nsaddr_list - * (busybox's nslookup uses it). - */ - if (__nameservers > rp->nscount) - __nameservers = rp->nscount; /* TODO: * if (__nameservers < rp->nscount) - try to grow __nameserver[]? */ -#ifdef __UCLIBC_HAS_IPV4__ - { - int n = __nameservers; - while (--n >= 0) { - __nameserver[n].sa.sa_family = AF_INET; - __nameserver[n].sa4 = rp->nsaddr_list[n]; /* struct copy */ - } - } +#ifdef __UCLIBC_HAS_IPV6__ + if (__nameservers > rp->_u._ext.nscount) + __nameservers = rp->_u._ext.nscount; + n = __nameservers; + while (--n >= 0) + __nameserver[n].sa6 = *rp->_u._ext.nsaddrs[n]; /* struct copy */ +#else /* __UCLIBC_HAS_IPV4__ */ + if (__nameservers > rp->nscount) + __nameservers = rp->nscount; + n = __nameservers; + while (--n >= 0) + __nameserver[n].sa4 = rp->nsaddr_list[n]; /* struct copy */ #endif /* Extend and comment what program is known * to use which _res.XXX member(s). @@ -1439,8 +1456,12 @@ static void res_sync_func(void) /* Our res_init never fails (always returns 0) */ int res_init(void) { - int i, n; struct __res_state *rp = &(_res); + int i; + int n; +#ifdef __UCLIBC_HAS_IPV6__ + int m = 0; +#endif __UCLIBC_MUTEX_LOCK(__resolv_lock); __close_nameservers(); @@ -1465,23 +1486,57 @@ int res_init(void) n = ARRAY_SIZE(rp->dnsrch); for (i = 0; i < n; i++) rp->dnsrch[i] = __searchdomain[i]; -#ifdef __UCLIBC_HAS_IPV4__ + + /* copy nameservers' addresses */ i = 0; +#ifdef __UCLIBC_HAS_IPV4__ 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: + if (__nameserver[i].sa.sa_family == AF_INET) { + rp->nsaddr_list[n] = __nameserver[i].sa4; /* struct copy */ +#ifdef __UCLIBC_HAS_IPV6__ + if (m < ARRAY_SIZE(rp->_u._ext.nsaddrs)) { + rp->_u._ext.nsaddrs[m] = (void*) &rp->nsaddr_list[n]; + m++; + } +#endif + n++; + } +#ifdef __UCLIBC_HAS_IPV6__ + if (__nameserver[i].sa.sa_family == AF_INET6 + && m < ARRAY_SIZE(rp->_u._ext.nsaddrs) + ) { + struct sockaddr_in6 *sa6 = malloc(sizeof(sa6)); + if (sa6) { + *sa6 = __nameserver[i].sa6; /* struct copy */ + rp->_u._ext.nsaddrs[m] = sa6; + m++; + } + } +#endif i++; } rp->nscount = n; +#ifdef __UCLIBC_HAS_IPV6__ + rp->_u._ext.nscount = m; #endif + +#else /* if !__UCLIBC_HAS_IPV4__ (only IPV6) */ + while (m < ARRAY_SIZE(rp->_u._ext.nsaddrs) && i < __nameservers) { + struct sockaddr_in6 *sa6 = malloc(sizeof(sa6)); + if (sa6) { + *sa6 = __nameserver[i].sa6; /* struct copy */ + rp->_u._ext.nsaddrs[m] = sa6; + m++; + } + i++; + } + rp->_u._ext.nscount = m; +#endif /* !__UCLIBC_HAS_IPV4__ (only IPV6) */ + __UCLIBC_MUTEX_UNLOCK(__resolv_lock); return 0; } -#undef ARRAY_SIZE libc_hidden_def(res_init) #ifdef __UCLIBC_HAS_BSD_RES_CLOSE__ @@ -1490,6 +1545,18 @@ void res_close(void) __UCLIBC_MUTEX_LOCK(__resolv_lock); __close_nameservers(); __res_sync = NULL; +#ifdef __UCLIBC_HAS_IPV6__ + { + char *p1 = (char*) &(_res.nsaddr_list[0]); + int m = 0; + /* free nsaddrs[m] if they do not point to nsaddr_list[x] */ + while (m < ARRAY_SIZE(_res._u._ext.nsaddrs)) { + char *p2 = (char*)(_res._u._ext.nsaddrs[m]); + if (p2 < p1 || (p2 - p1) > sizeof(_res.nsaddr_list)) + free(p2); + } + } +#endif memset(&_res, 0, sizeof(_res)); __UCLIBC_MUTEX_UNLOCK(__resolv_lock); } @@ -1497,7 +1564,6 @@ void res_close(void) #endif /* L_res_init */ - #ifdef L_res_query int res_query(const char *dname, int class, int type, |