diff options
Diffstat (limited to 'libc/inet')
-rw-r--r-- | libc/inet/resolv.c | 999 |
1 files changed, 489 insertions, 510 deletions
diff --git a/libc/inet/resolv.c b/libc/inet/resolv.c index e6dac1ae3..5fe8a8ed7 100644 --- a/libc/inet/resolv.c +++ b/libc/inet/resolv.c @@ -198,6 +198,7 @@ libc_hidden_proto(printf) libc_hidden_proto(sprintf) libc_hidden_proto(snprintf) libc_hidden_proto(fgets) +libc_hidden_proto(getnameinfo) libc_hidden_proto(gethostbyname) libc_hidden_proto(gethostbyname_r) libc_hidden_proto(gethostbyname2_r) @@ -1395,469 +1396,6 @@ int attribute_hidden __dns_lookup(const char *name, int type, #endif -#ifdef L_gethostbyname - -struct hostent *gethostbyname(const char *name) -{ - static struct hostent h; - static char buf[sizeof(struct in_addr) + - sizeof(struct in_addr *) * 2 + - sizeof(char *)*ALIAS_DIM + 384/*namebuffer*/ + 32/* margin */]; - struct hostent *hp; - - gethostbyname_r(name, &h, buf, sizeof(buf), &hp, &h_errno); - return hp; -} -libc_hidden_def(gethostbyname) -#endif - - -#ifdef L_gethostbyname2 - -struct hostent *gethostbyname2(const char *name, int family) -{ -#ifndef __UCLIBC_HAS_IPV6__ - return family == AF_INET ? gethostbyname(name) : (struct hostent*)NULL; -#else - static struct hostent h; - static char buf[sizeof(struct in6_addr) + - sizeof(struct in6_addr *) * 2 + - sizeof(char *)*ALIAS_DIM + 384/*namebuffer*/ + 32/* margin */]; - struct hostent *hp; - - gethostbyname2_r(name, family, &h, buf, sizeof(buf), &hp, &h_errno); - return hp; -#endif -} -#endif - - -#ifdef L_res_init - -/* Protected by __resolv_lock */ -struct __res_state _res; - -/* Will be called under __resolv_lock. */ -static void res_sync_func(void) -{ - struct __res_state *rp = &(_res); - int n; - - /* If we didn't get malloc failure earlier... */ - if (__nameserver != (void*) &__local_nameserver) { - /* TODO: - * if (__nameservers < rp->nscount) - try to grow __nameserver[]? - */ -#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 /* IPv4 only */ - 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). - */ - // __resolv_opts = rp->options; - // ... -} - -/* Our res_init never fails (always returns 0) */ -int res_init(void) -{ - struct __res_state *rp = &(_res); - int i; - int n; -#ifdef __UCLIBC_HAS_IPV6__ - int m = 0; -#endif - - __UCLIBC_MUTEX_LOCK(__resolv_lock); - __close_nameservers(); - __open_nameservers(); - - __res_sync = res_sync_func; - - memset(rp, 0, sizeof(*rp)); - rp->options = RES_INIT; -#ifdef __UCLIBC_HAS_COMPAT_RES_STATE__ - rp->retrans = RES_TIMEOUT; - rp->retry = 4; - rp->id = random(); -#endif - rp->ndots = 1; -#ifdef __UCLIBC_HAS_EXTRA_COMPAT_RES_STATE__ - rp->_vcsock = -1; -#endif - - n = __searchdomains; - if (n > ARRAY_SIZE(rp->dnsrch)) - n = ARRAY_SIZE(rp->dnsrch); - for (i = 0; i < n; i++) - rp->dnsrch[i] = __searchdomain[i]; - - /* 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) { - 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 /* IPv6 only */ - 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_MUTEX_UNLOCK(__resolv_lock); - return 0; -} -libc_hidden_def(res_init) - -#ifdef __UCLIBC_HAS_BSD_RES_CLOSE__ -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); -} -#endif -#endif /* L_res_init */ - - -#ifdef L_res_query - -int res_query(const char *dname, int class, int type, - unsigned char *answer, int anslen) -{ - int i; - unsigned char * packet = NULL; - struct resolv_answer a; - - if (!dname || class != 1 /* CLASS_IN */) { - h_errno = NO_RECOVERY; - return -1; - } - - memset(&a, '\0', sizeof(a)); - i = __dns_lookup(dname, type, &packet, &a); - - if (i < 0) { - h_errno = TRY_AGAIN; - return -1; - } - - free(a.dotted); - - if (a.atype == type) { /* CNAME */ - if (i > anslen) - i = anslen; - memcpy(answer, packet, i); - } - free(packet); - return i; -} -libc_hidden_def(res_query) - -/* - * Formulate a normal query, send, and retrieve answer in supplied buffer. - * Return the size of the response on success, -1 on error. - * If enabled, implement search rules until answer or unrecoverable failure - * is detected. Error code, if any, is left in h_errno. - */ -#define __TRAILING_DOT (1<<0) -#define __GOT_NODATA (1<<1) -#define __GOT_SERVFAIL (1<<2) -#define __TRIED_AS_IS (1<<3) -int res_search(const char *name, int class, int type, u_char *answer, - int anslen) -{ - const char *cp, * const *domain; - HEADER *hp = (HEADER *)(void *)answer; - unsigned dots; - unsigned state; - int ret, saved_herrno; - uint32_t _res_options; - unsigned _res_ndots; - char **_res_dnsrch; - - if (!name || !answer) { - h_errno = NETDB_INTERNAL; - return -1; - } - - again: - __UCLIBC_MUTEX_LOCK(__resolv_lock); - _res_options = _res.options; - _res_ndots = _res.ndots; - _res_dnsrch = _res.dnsrch; - __UCLIBC_MUTEX_UNLOCK(__resolv_lock); - if (!(_res_options & RES_INIT)) { - res_init(); /* our res_init never fails */ - goto again; - } - - state = 0; - errno = 0; - h_errno = HOST_NOT_FOUND; /* default, if we never query */ - dots = 0; - for (cp = name; *cp; cp++) - dots += (*cp == '.'); - - if (cp > name && *--cp == '.') - state |= __TRAILING_DOT; - - /* - * If there are dots in the name already, let's just give it a try - * 'as is'. The threshold can be set with the "ndots" option. - */ - saved_herrno = -1; - if (dots >= _res_ndots) { - ret = res_querydomain(name, NULL, class, type, answer, anslen); - if (ret > 0) - return ret; - saved_herrno = h_errno; - state |= __TRIED_AS_IS; - } - - /* - * We do at least one level of search if - * - there is no dot and RES_DEFNAME is set, or - * - there is at least one dot, there is no trailing dot, - * and RES_DNSRCH is set. - */ - if ((!dots && (_res_options & RES_DEFNAMES)) - || (dots && !(state & __TRAILING_DOT) && (_res_options & RES_DNSRCH)) - ) { - bool done = 0; - - for (domain = (const char * const *)_res_dnsrch; - *domain && !done; - domain++) { - - ret = res_querydomain(name, *domain, class, type, - answer, anslen); - if (ret > 0) - return ret; - - /* - * If no server present, give up. - * If name isn't found in this domain, - * keep trying higher domains in the search list - * (if that's enabled). - * On a NO_DATA error, keep trying, otherwise - * a wildcard entry of another type could keep us - * from finding this entry higher in the domain. - * If we get some other error (negative answer or - * server failure), then stop searching up, - * but try the input name below in case it's - * fully-qualified. - */ - if (errno == ECONNREFUSED) { - h_errno = TRY_AGAIN; - return -1; - } - - switch (h_errno) { - case NO_DATA: - state |= __GOT_NODATA; - /* FALLTHROUGH */ - case HOST_NOT_FOUND: - /* keep trying */ - break; - case TRY_AGAIN: - if (hp->rcode == SERVFAIL) { - /* try next search element, if any */ - state |= __GOT_SERVFAIL; - break; - } - /* FALLTHROUGH */ - default: - /* anything else implies that we're done */ - done = 1; - } - /* - * if we got here for some reason other than DNSRCH, - * we only wanted one iteration of the loop, so stop. - */ - if (!(_res_options & RES_DNSRCH)) - done = 1; - } - } - - /* - * if we have not already tried the name "as is", do that now. - * note that we do this regardless of how many dots were in the - * name or whether it ends with a dot. - */ - if (!(state & __TRIED_AS_IS)) { - ret = res_querydomain(name, NULL, class, type, answer, anslen); - if (ret > 0) - return ret; - } - - /* - * if we got here, we didn't satisfy the search. - * if we did an initial full query, return that query's h_errno - * (note that we wouldn't be here if that query had succeeded). - * else if we ever got a nodata, send that back as the reason. - * else send back meaningless h_errno, that being the one from - * the last DNSRCH we did. - */ - if (saved_herrno != -1) - h_errno = saved_herrno; - else if (state & __GOT_NODATA) - h_errno = NO_DATA; - else if (state & __GOT_SERVFAIL) - h_errno = TRY_AGAIN; - return -1; -} -#undef __TRAILING_DOT -#undef __GOT_NODATA -#undef __GOT_SERVFAIL -#undef __TRIED_AS_IS -/* - * Perform a call on res_query on the concatenation of name and domain, - * removing a trailing dot from name if domain is NULL. - */ -int res_querydomain(const char *name, const char *domain, int class, int type, - u_char * answer, int anslen) -{ - char nbuf[MAXDNAME]; - const char *longname = nbuf; - size_t n, d; -#ifdef DEBUG - uint32_t _res_options; -#endif - - if (!name || !answer) { - h_errno = NETDB_INTERNAL; - return -1; - } - -#ifdef DEBUG - again: - __UCLIBC_MUTEX_LOCK(__resolv_lock); - _res_options = _res.options; - __UCLIBC_MUTEX_UNLOCK(__resolv_lock); - if (!(_res_options & RES_INIT)) { - res_init(); /* our res_init never fails */ - goto again: - } - if (_res_options & RES_DEBUG) - printf(";; res_querydomain(%s, %s, %d, %d)\n", - name, (domain ? domain : "<Nil>"), class, type); -#endif - if (domain == NULL) { - /* - * Check for trailing '.'; - * copy without '.' if present. - */ - n = strlen(name); - if (n + 1 > sizeof(nbuf)) { - h_errno = NO_RECOVERY; - return -1; - } - if (n > 0 && name[--n] == '.') { - strncpy(nbuf, name, n); - nbuf[n] = '\0'; - } else - longname = name; - } else { - n = strlen(name); - d = strlen(domain); - if (n + 1 + d + 1 > sizeof(nbuf)) { - h_errno = NO_RECOVERY; - return -1; - } - snprintf(nbuf, sizeof(nbuf), "%s.%s", name, domain); - } - return res_query(longname, class, type, answer, anslen); -} -libc_hidden_def(res_querydomain) -/* res_mkquery */ -/* res_send */ -/* dn_comp */ -/* dn_expand */ -#endif - - -#ifdef L_gethostbyaddr - -struct hostent *gethostbyaddr(const void *addr, socklen_t len, int type) -{ - static struct hostent h; - static char buf[ -#ifndef __UCLIBC_HAS_IPV6__ - sizeof(struct in_addr) + sizeof(struct in_addr *)*2 + -#else - sizeof(struct in6_addr) + sizeof(struct in6_addr *)*2 + -#endif /* __UCLIBC_HAS_IPV6__ */ - sizeof(char *)*ALIAS_DIM + 384 /*namebuffer*/ + 32 /* margin */]; - struct hostent *hp; - - gethostbyaddr_r(addr, len, type, &h, buf, sizeof(buf), &hp, &h_errno); - return hp; -} -libc_hidden_def(gethostbyaddr) -#endif - - #ifdef L_read_etc_hosts_r FILE * __open_etc_hosts(void) @@ -1890,57 +1428,44 @@ int attribute_hidden __read_etc_hosts_r( /* make sure user char * is aligned */ i = ALIGN_BUFFER_OFFSET(buf); - if (unlikely(i)) { - if (buflen < i) - return ERANGE; - buf += i; - buflen -= i; - } + buf += i; + buflen -= i; - if (buflen < sizeof(char *) * ALIAS_DIM) - return ERANGE; alias = (char **)buf; buf += sizeof(char **) * ALIAS_DIM; buflen -= sizeof(char **) * ALIAS_DIM; + if ((ssize_t)buflen < 0) + return ERANGE; if (action != GETHOSTENT) { #ifdef __UCLIBC_HAS_IPV6__ char *p = buf; size_t len = buflen; -#endif /* __UCLIBC_HAS_IPV6__ */ +#endif *h_errnop = NETDB_INTERNAL; - if (buflen < sizeof(*in)) - return ERANGE; in = (struct in_addr*)buf; buf += sizeof(*in); buflen -= sizeof(*in); - - if (buflen < sizeof(*addr_list) * 2) - return ERANGE; addr_list = (struct in_addr **)buf; buf += sizeof(*addr_list) * 2; - buflen -= sizeof(*addr_list)*2; - -#ifdef __UCLIBC_HAS_IPV6__ - if (len < sizeof(*in6)) + buflen -= sizeof(*addr_list) * 2; + if ((ssize_t)buflen < 0) return ERANGE; +#ifdef __UCLIBC_HAS_IPV6__ in6 = (struct in6_addr*)p; p += sizeof(*in6); len -= sizeof(*in6); - - if (len < sizeof(*addr_list6) * 2) - return ERANGE; addr_list6 = (struct in6_addr**)p; p += sizeof(*addr_list6) * 2; len -= sizeof(*addr_list6) * 2; - + if ((ssize_t)len < 0) + return ERANGE; if (len < buflen) { buflen = len; buf = p; } -#endif /* __UCLIBC_HAS_IPV6__ */ - - if (buflen < 80) +#endif + if ((ssize_t)buflen < 80) return ERANGE; fp = __open_etc_hosts(); @@ -1993,7 +1518,7 @@ int attribute_hidden __read_etc_hosts_r( if (type == AF_INET && inet_pton(AF_INET, alias[0], in) > 0) { DPRINTF("Found INET\n"); addr_list[0] = in; - addr_list[1] = 0; + addr_list[1] = NULL; result_buf->h_name = alias[1]; result_buf->h_addrtype = AF_INET; result_buf->h_length = sizeof(*in); @@ -2001,11 +1526,12 @@ int attribute_hidden __read_etc_hosts_r( result_buf->h_aliases = alias + 2; *result = result_buf; ret = NETDB_SUCCESS; + } #ifdef __UCLIBC_HAS_IPV6__ - } else if (type == AF_INET6 && inet_pton(AF_INET6, alias[0], in6) > 0) { + else if (type == AF_INET6 && inet_pton(AF_INET6, alias[0], in6) > 0) { DPRINTF("Found INET6\n"); addr_list6[0] = in6; - addr_list6[1] = 0; + addr_list6[1] = NULL; result_buf->h_name = alias[1]; result_buf->h_addrtype = AF_INET6; result_buf->h_length = sizeof(*in6); @@ -2013,8 +1539,9 @@ int attribute_hidden __read_etc_hosts_r( result_buf->h_aliases = alias + 2; *result = result_buf; ret = NETDB_SUCCESS; -#endif /* __UCLIBC_HAS_IPV6__ */ - } else { + } +#endif + else { /* continue parsing in the hope the user has multiple * host types listed in the database like so: * <ipv4 addr> host @@ -2088,6 +1615,7 @@ DONE: } libc_hidden_def(gethostent_r) +//TODO: move into separat .o file! struct hostent *gethostent(void) { static struct hostent h; @@ -2101,10 +1629,7 @@ struct hostent *gethostent(void) 80 /*namebuffer*/ + 2 /* margin */]; struct hostent *host; -//BUG: the lock is not recursive! -// __UCLIBC_MUTEX_LOCK(mylock); gethostent_r(&h, buf, sizeof(buf), &host, &h_errno); -// __UCLIBC_MUTEX_UNLOCK(mylock); return host; } #endif @@ -2161,9 +1686,66 @@ int attribute_hidden __get_hosts_byaddr_r(const char * addr, int len, int type, #endif +#ifdef L_gethostbyname + +struct hostent *gethostbyname(const char *name) +{ + static struct hostent h; + static char buf[sizeof(struct in_addr) + + sizeof(struct in_addr *) * 2 + + sizeof(char *)*ALIAS_DIM + 384/*namebuffer*/ + 32/* margin */]; + struct hostent *hp; + + gethostbyname_r(name, &h, buf, sizeof(buf), &hp, &h_errno); + return hp; +} +libc_hidden_def(gethostbyname) +#endif + + +#ifdef L_gethostbyname2 + +struct hostent *gethostbyname2(const char *name, int family) +{ +#ifndef __UCLIBC_HAS_IPV6__ + return family == AF_INET ? gethostbyname(name) : (struct hostent*)NULL; +#else + static struct hostent h; + static char buf[sizeof(struct in6_addr) + + sizeof(struct in6_addr *) * 2 + + sizeof(char *)*ALIAS_DIM + 384/*namebuffer*/ + 32/* margin */]; + struct hostent *hp; + + gethostbyname2_r(name, family, &h, buf, sizeof(buf), &hp, &h_errno); + return hp; +#endif +} +#endif + + +#ifdef L_gethostbyaddr + +struct hostent *gethostbyaddr(const void *addr, socklen_t len, int type) +{ + static struct hostent h; + static char buf[ +#ifndef __UCLIBC_HAS_IPV6__ + sizeof(struct in_addr) + sizeof(struct in_addr *)*2 + +#else + sizeof(struct in6_addr) + sizeof(struct in6_addr *)*2 + +#endif /* __UCLIBC_HAS_IPV6__ */ + sizeof(char *)*ALIAS_DIM + 384 /*namebuffer*/ + 32 /* margin */]; + struct hostent *hp; + + gethostbyaddr_r(addr, len, type, &h, buf, sizeof(buf), &hp, &h_errno); + return hp; +} +libc_hidden_def(gethostbyaddr) +#endif + + #ifdef L_getnameinfo -libc_hidden_proto(getnameinfo) int getnameinfo(const struct sockaddr *sa, socklen_t addrlen, char *host, socklen_t hostlen, char *serv, socklen_t servlen, unsigned int flags) @@ -2929,24 +2511,15 @@ int __dn_expand(const u_char *msg, const u_char *eom, const u_char *src, #ifdef L_ns_name -/* - * printable(ch) - * Thinking in noninternationalized USASCII (per the DNS spec), - * is this character visible and not a space when printed ? - * return: - * boolean. +/* Thinking in noninternationalized USASCII (per the DNS spec), + * is this character visible and not a space when printed ? */ static int printable(int ch) { return (ch > 0x20 && ch < 0x7f); } - -/* - * special(ch) - * Thinking in noninternationalized USASCII (per the DNS spec), - * is this characted special ("in need of quoting") ? - * return: - * boolean. +/* Thinking in noninternationalized USASCII (per the DNS spec), + * is this characted special ("in need of quoting") ? */ static int special(int ch) { @@ -3144,4 +2717,410 @@ int ns_name_unpack(const u_char *msg, const u_char *eom, const u_char *src, } libc_hidden_def(ns_name_unpack) #endif /* L_ns_name */ + + +#ifdef L_res_init + +/* Protected by __resolv_lock */ +struct __res_state _res; + +/* Will be called under __resolv_lock. */ +static void res_sync_func(void) +{ + struct __res_state *rp = &(_res); + int n; + + /* If we didn't get malloc failure earlier... */ + if (__nameserver != (void*) &__local_nameserver) { + /* TODO: + * if (__nameservers < rp->nscount) - try to grow __nameserver[]? + */ +#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 /* IPv4 only */ + 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). + */ + // __resolv_opts = rp->options; + // ... +} + +/* Our res_init never fails (always returns 0) */ +int res_init(void) +{ + struct __res_state *rp = &(_res); + int i; + int n; +#ifdef __UCLIBC_HAS_IPV6__ + int m = 0; +#endif + + __UCLIBC_MUTEX_LOCK(__resolv_lock); + __close_nameservers(); + __open_nameservers(); + + __res_sync = res_sync_func; + + memset(rp, 0, sizeof(*rp)); + rp->options = RES_INIT; +#ifdef __UCLIBC_HAS_COMPAT_RES_STATE__ + rp->retrans = RES_TIMEOUT; + rp->retry = 4; + rp->id = random(); +#endif + rp->ndots = 1; +#ifdef __UCLIBC_HAS_EXTRA_COMPAT_RES_STATE__ + rp->_vcsock = -1; +#endif + + n = __searchdomains; + if (n > ARRAY_SIZE(rp->dnsrch)) + n = ARRAY_SIZE(rp->dnsrch); + for (i = 0; i < n; i++) + rp->dnsrch[i] = __searchdomain[i]; + + /* 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) { + 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 /* IPv6 only */ + 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_MUTEX_UNLOCK(__resolv_lock); + return 0; +} +libc_hidden_def(res_init) + +#ifdef __UCLIBC_HAS_BSD_RES_CLOSE__ +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); +} +#endif +#endif /* L_res_init */ + + +#ifdef L_res_query + +int res_query(const char *dname, int class, int type, + unsigned char *answer, int anslen) +{ + int i; + unsigned char * packet = NULL; + struct resolv_answer a; + + if (!dname || class != 1 /* CLASS_IN */) { + h_errno = NO_RECOVERY; + return -1; + } + + memset(&a, '\0', sizeof(a)); + i = __dns_lookup(dname, type, &packet, &a); + + if (i < 0) { + h_errno = TRY_AGAIN; + return -1; + } + + free(a.dotted); + + if (a.atype == type) { /* CNAME */ + if (i > anslen) + i = anslen; + memcpy(answer, packet, i); + } + free(packet); + return i; +} +libc_hidden_def(res_query) + +/* + * Formulate a normal query, send, and retrieve answer in supplied buffer. + * Return the size of the response on success, -1 on error. + * If enabled, implement search rules until answer or unrecoverable failure + * is detected. Error code, if any, is left in h_errno. + */ +#define __TRAILING_DOT (1<<0) +#define __GOT_NODATA (1<<1) +#define __GOT_SERVFAIL (1<<2) +#define __TRIED_AS_IS (1<<3) +int res_search(const char *name, int class, int type, u_char *answer, + int anslen) +{ + const char *cp, * const *domain; + HEADER *hp = (HEADER *)(void *)answer; + unsigned dots; + unsigned state; + int ret, saved_herrno; + uint32_t _res_options; + unsigned _res_ndots; + char **_res_dnsrch; + + if (!name || !answer) { + h_errno = NETDB_INTERNAL; + return -1; + } + + again: + __UCLIBC_MUTEX_LOCK(__resolv_lock); + _res_options = _res.options; + _res_ndots = _res.ndots; + _res_dnsrch = _res.dnsrch; + __UCLIBC_MUTEX_UNLOCK(__resolv_lock); + if (!(_res_options & RES_INIT)) { + res_init(); /* our res_init never fails */ + goto again; + } + + state = 0; + errno = 0; + h_errno = HOST_NOT_FOUND; /* default, if we never query */ + dots = 0; + for (cp = name; *cp; cp++) + dots += (*cp == '.'); + + if (cp > name && *--cp == '.') + state |= __TRAILING_DOT; + + /* + * If there are dots in the name already, let's just give it a try + * 'as is'. The threshold can be set with the "ndots" option. + */ + saved_herrno = -1; + if (dots >= _res_ndots) { + ret = res_querydomain(name, NULL, class, type, answer, anslen); + if (ret > 0) + return ret; + saved_herrno = h_errno; + state |= __TRIED_AS_IS; + } + + /* + * We do at least one level of search if + * - there is no dot and RES_DEFNAME is set, or + * - there is at least one dot, there is no trailing dot, + * and RES_DNSRCH is set. + */ + if ((!dots && (_res_options & RES_DEFNAMES)) + || (dots && !(state & __TRAILING_DOT) && (_res_options & RES_DNSRCH)) + ) { + bool done = 0; + + for (domain = (const char * const *)_res_dnsrch; + *domain && !done; + domain++) { + + ret = res_querydomain(name, *domain, class, type, + answer, anslen); + if (ret > 0) + return ret; + + /* + * If no server present, give up. + * If name isn't found in this domain, + * keep trying higher domains in the search list + * (if that's enabled). + * On a NO_DATA error, keep trying, otherwise + * a wildcard entry of another type could keep us + * from finding this entry higher in the domain. + * If we get some other error (negative answer or + * server failure), then stop searching up, + * but try the input name below in case it's + * fully-qualified. + */ + if (errno == ECONNREFUSED) { + h_errno = TRY_AGAIN; + return -1; + } + + switch (h_errno) { + case NO_DATA: + state |= __GOT_NODATA; + /* FALLTHROUGH */ + case HOST_NOT_FOUND: + /* keep trying */ + break; + case TRY_AGAIN: + if (hp->rcode == SERVFAIL) { + /* try next search element, if any */ + state |= __GOT_SERVFAIL; + break; + } + /* FALLTHROUGH */ + default: + /* anything else implies that we're done */ + done = 1; + } + /* + * if we got here for some reason other than DNSRCH, + * we only wanted one iteration of the loop, so stop. + */ + if (!(_res_options & RES_DNSRCH)) + done = 1; + } + } + + /* + * if we have not already tried the name "as is", do that now. + * note that we do this regardless of how many dots were in the + * name or whether it ends with a dot. + */ + if (!(state & __TRIED_AS_IS)) { + ret = res_querydomain(name, NULL, class, type, answer, anslen); + if (ret > 0) + return ret; + } + + /* + * if we got here, we didn't satisfy the search. + * if we did an initial full query, return that query's h_errno + * (note that we wouldn't be here if that query had succeeded). + * else if we ever got a nodata, send that back as the reason. + * else send back meaningless h_errno, that being the one from + * the last DNSRCH we did. + */ + if (saved_herrno != -1) + h_errno = saved_herrno; + else if (state & __GOT_NODATA) + h_errno = NO_DATA; + else if (state & __GOT_SERVFAIL) + h_errno = TRY_AGAIN; + return -1; +} +#undef __TRAILING_DOT +#undef __GOT_NODATA +#undef __GOT_SERVFAIL +#undef __TRIED_AS_IS +/* + * Perform a call on res_query on the concatenation of name and domain, + * removing a trailing dot from name if domain is NULL. + */ +int res_querydomain(const char *name, const char *domain, int class, int type, + u_char * answer, int anslen) +{ + char nbuf[MAXDNAME]; + const char *longname = nbuf; + size_t n, d; +#ifdef DEBUG + uint32_t _res_options; +#endif + + if (!name || !answer) { + h_errno = NETDB_INTERNAL; + return -1; + } + +#ifdef DEBUG + again: + __UCLIBC_MUTEX_LOCK(__resolv_lock); + _res_options = _res.options; + __UCLIBC_MUTEX_UNLOCK(__resolv_lock); + if (!(_res_options & RES_INIT)) { + res_init(); /* our res_init never fails */ + goto again: + } + if (_res_options & RES_DEBUG) + printf(";; res_querydomain(%s, %s, %d, %d)\n", + name, (domain ? domain : "<Nil>"), class, type); +#endif + if (domain == NULL) { + /* + * Check for trailing '.'; + * copy without '.' if present. + */ + n = strlen(name); + if (n + 1 > sizeof(nbuf)) { + h_errno = NO_RECOVERY; + return -1; + } + if (n > 0 && name[--n] == '.') { + strncpy(nbuf, name, n); + nbuf[n] = '\0'; + } else + longname = name; + } else { + n = strlen(name); + d = strlen(domain); + if (n + 1 + d + 1 > sizeof(nbuf)) { + h_errno = NO_RECOVERY; + return -1; + } + snprintf(nbuf, sizeof(nbuf), "%s.%s", name, domain); + } + return res_query(longname, class, type, answer, anslen); +} +libc_hidden_def(res_querydomain) +/* res_mkquery */ +/* res_send */ +/* dn_comp */ +/* dn_expand */ +#endif + /* vi: set sw=4 ts=4: */ |