From 94cec9a5ee5301bf5d65b9ed7c93b8b74510a187 Mon Sep 17 00:00:00 2001 From: Denis Vlasenko Date: Mon, 17 Nov 2008 03:01:01 +0000 Subject: resolver: move large code blocks to arrange related functions closer. almost no code changes --- libc/inet/resolv.c | 2913 ++++++++++++++++++++++++++-------------------------- 1 file changed, 1446 insertions(+), 1467 deletions(-) (limited to 'libc/inet') 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,445 +1396,330 @@ 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; +#ifdef L_read_etc_hosts_r -/* Will be called under __resolv_lock. */ -static void res_sync_func(void) +FILE * __open_etc_hosts(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 + FILE * fp; + if ((fp = fopen("/etc/hosts", "r")) == NULL) { + fp = fopen("/etc/config/hosts", "r"); } - /* Extend and comment what program is known - * to use which _res.XXX member(s). - */ - // __resolv_opts = rp->options; - // ... + return fp; } -/* Our res_init never fails (always returns 0) */ -int res_init(void) +int attribute_hidden __read_etc_hosts_r( + FILE * fp, + const char * name, + int type, + enum etc_hosts_action action, + struct hostent * result_buf, + char * buf, size_t buflen, + struct hostent ** result, + int * h_errnop) { - struct __res_state *rp = &(_res); - int i; - int n; + struct in_addr *in = NULL; + struct in_addr **addr_list = NULL; #ifdef __UCLIBC_HAS_IPV6__ - int m = 0; -#endif - - __UCLIBC_MUTEX_LOCK(__resolv_lock); - __close_nameservers(); - __open_nameservers(); - - __res_sync = res_sync_func; + struct in6_addr *in6 = NULL; + struct in6_addr **addr_list6 = NULL; +#endif /* __UCLIBC_HAS_IPV6__ */ + char *cp, **alias; + int aliases, i, ret = HOST_NOT_FOUND; - 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 + /* make sure user char * is aligned */ + i = ALIGN_BUFFER_OFFSET(buf); + buf += i; + buflen -= i; - n = __searchdomains; - if (n > ARRAY_SIZE(rp->dnsrch)) - n = ARRAY_SIZE(rp->dnsrch); - for (i = 0; i < n; i++) - rp->dnsrch[i] = __searchdomain[i]; + alias = (char **)buf; + buf += sizeof(char **) * ALIAS_DIM; + buflen -= sizeof(char **) * ALIAS_DIM; + if ((ssize_t)buflen < 0) + return ERANGE; - /* 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 */ + if (action != GETHOSTENT) { #ifdef __UCLIBC_HAS_IPV6__ - if (m < ARRAY_SIZE(rp->_u._ext.nsaddrs)) { - rp->_u._ext.nsaddrs[m] = (void*) &rp->nsaddr_list[n]; - m++; - } + char *p = buf; + size_t len = buflen; #endif - n++; - } + *h_errnop = NETDB_INTERNAL; + in = (struct in_addr*)buf; + buf += sizeof(*in); + buflen -= sizeof(*in); + addr_list = (struct in_addr **)buf; + buf += sizeof(*addr_list) * 2; + buflen -= sizeof(*addr_list) * 2; + if ((ssize_t)buflen < 0) + return ERANGE; #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++; - } + in6 = (struct in6_addr*)p; + p += sizeof(*in6); + len -= sizeof(*in6); + 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 - i++; - } - rp->nscount = n; -#ifdef __UCLIBC_HAS_IPV6__ - rp->_u._ext.nscount = m; -#endif + if ((ssize_t)buflen < 80) + return ERANGE; -#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++; + fp = __open_etc_hosts(); + if (fp == NULL) { + *result = NULL; + return errno; } - i++; } - rp->_u._ext.nscount = m; -#endif - __UCLIBC_MUTEX_UNLOCK(__resolv_lock); - return 0; -} -libc_hidden_def(res_init) + *h_errnop = HOST_NOT_FOUND; + while (fgets(buf, buflen, fp)) { + cp = strchr(buf, '#'); + if (cp) + *cp = '\0'; + DPRINTF("Looking at: %s\n", buf); + aliases = 0; -#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); + cp = buf; + while (*cp) { + while (*cp && isspace(*cp)) + *cp++ = '\0'; + if (!*cp) + break; + if (aliases < (2+MAX_ALIASES)) + alias[aliases++] = cp; + while (*cp && !isspace(*cp)) + cp++; } - } -#endif - memset(&_res, 0, sizeof(_res)); - __UCLIBC_MUTEX_UNLOCK(__resolv_lock); -} -#endif -#endif /* L_res_init */ + alias[aliases] = NULL; + if (aliases < 2) + continue; /* syntax error really */ -#ifdef L_res_query + if (action == GETHOSTENT) { + /* Return whatever the next entry happens to be. */ + break; + } + if (action == GET_HOSTS_BYADDR) { + if (strcmp(name, alias[0]) != 0) + continue; + } else { + /* GET_HOSTS_BYNAME */ + for (i = 1; i < aliases; i++) + if (strcasecmp(name, alias[i]) == 0) + break; + if (i >= aliases) + continue; + } -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; + if (type == AF_INET && inet_pton(AF_INET, alias[0], in) > 0) { + DPRINTF("Found INET\n"); + addr_list[0] = in; + addr_list[1] = NULL; + result_buf->h_name = alias[1]; + result_buf->h_addrtype = AF_INET; + result_buf->h_length = sizeof(*in); + result_buf->h_addr_list = (char**) addr_list; + 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) { + DPRINTF("Found INET6\n"); + addr_list6[0] = in6; + addr_list6[1] = NULL; + result_buf->h_name = alias[1]; + result_buf->h_addrtype = AF_INET6; + result_buf->h_length = sizeof(*in6); + result_buf->h_addr_list = (char**) addr_list6; + result_buf->h_aliases = alias + 2; + *result = result_buf; + ret = NETDB_SUCCESS; + } +#endif + else { + /* continue parsing in the hope the user has multiple + * host types listed in the database like so: + * host + * host + * If looking for an IPv6 addr, don't bail when we got the IPv4 + */ + DPRINTF("Error: Found host but diff network type\n"); + /* NB: gethostbyname2_r depends on this feature + * to avoid looking for IPv6 addr of "localhost" etc */ + ret = TRY_AGAIN; + continue; + } + break; } + if (action != GETHOSTENT) + fclose(fp); + return ret; +} +#endif - memset(&a, '\0', sizeof(a)); - i = __dns_lookup(dname, type, &packet, &a); - if (i < 0) { - h_errno = TRY_AGAIN; - return -1; - } +#ifdef L_gethostent - free(a.dotted); +__UCLIBC_MUTEX_STATIC(mylock, PTHREAD_MUTEX_INITIALIZER); - if (a.atype == type) { /* CNAME */ - if (i > anslen) - i = anslen; - memcpy(answer, packet, i); +static smallint __stay_open; +static FILE * __gethostent_fp; + +void endhostent(void) +{ + __UCLIBC_MUTEX_LOCK(mylock); + __stay_open = 0; + if (__gethostent_fp) { + fclose(__gethostent_fp); + __gethostent_fp = NULL; } - free(packet); - return i; + __UCLIBC_MUTEX_UNLOCK(mylock); } -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) +void sethostent(int stay_open) { - 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; + __UCLIBC_MUTEX_LOCK(mylock); + __stay_open = (stay_open != 0); + __UCLIBC_MUTEX_UNLOCK(mylock); +} - if (!name || !answer) { - h_errno = NETDB_INTERNAL; - return -1; +int gethostent_r(struct hostent *result_buf, char *buf, size_t buflen, + struct hostent **result, int *h_errnop) +{ + int ret; + + __UCLIBC_MUTEX_LOCK(mylock); + if (__gethostent_fp == NULL) { + __gethostent_fp = __open_etc_hosts(); + if (__gethostent_fp == NULL) { + *result = NULL; + ret = TRY_AGAIN; + goto DONE; + } } - 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; + ret = __read_etc_hosts_r(__gethostent_fp, NULL, AF_INET, GETHOSTENT, + result_buf, buf, buflen, result, h_errnop); + if (__stay_open == 0) { + fclose(__gethostent_fp); + __gethostent_fp = NULL; } +DONE: + __UCLIBC_MUTEX_UNLOCK(mylock); + return ret; +} +libc_hidden_def(gethostent_r) - state = 0; - errno = 0; - h_errno = HOST_NOT_FOUND; /* default, if we never query */ - dots = 0; - for (cp = name; *cp; cp++) - dots += (*cp == '.'); +//TODO: move into separat .o file! +struct hostent *gethostent(void) +{ + 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 + + 80 /*namebuffer*/ + 2 /* margin */]; + struct hostent *host; - if (cp > name && *--cp == '.') - state |= __TRAILING_DOT; + gethostent_r(&h, buf, sizeof(buf), &host, &h_errno); + return host; +} +#endif - /* - * 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; + +#ifdef L_get_hosts_byname_r + +int attribute_hidden __get_hosts_byname_r(const char * name, int type, + struct hostent * result_buf, + char * buf, size_t buflen, + struct hostent ** result, + int * h_errnop) +{ + return __read_etc_hosts_r(NULL, name, type, GET_HOSTS_BYNAME, + result_buf, buf, buflen, result, h_errnop); +} +#endif + + +#ifdef L_get_hosts_byaddr_r + +int attribute_hidden __get_hosts_byaddr_r(const char * addr, int len, int type, + struct hostent * result_buf, + char * buf, size_t buflen, + struct hostent ** result, + int * h_errnop) +{ +#ifndef __UCLIBC_HAS_IPV6__ + char ipaddr[INET_ADDRSTRLEN]; +#else + char ipaddr[INET6_ADDRSTRLEN]; +#endif /* __UCLIBC_HAS_IPV6__ */ + + switch (type) { + case AF_INET: + if (len != sizeof(struct in_addr)) + return 0; + break; +#ifdef __UCLIBC_HAS_IPV6__ + case AF_INET6: + if (len != sizeof(struct in6_addr)) + return 0; + break; +#endif /* __UCLIBC_HAS_IPV6__ */ + default: + return 0; } - /* - * 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; + inet_ntop(type, addr, ipaddr, sizeof(ipaddr)); - for (domain = (const char * const *)_res_dnsrch; - *domain && !done; - domain++) { + return __read_etc_hosts_r(NULL, ipaddr, type, GET_HOSTS_BYADDR, + result_buf, buf, buflen, result, h_errnop); +} +#endif - 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; - } +#ifdef L_gethostbyname - 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; - } +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; - /* - * 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; + gethostbyname_r(name, &h, buf, sizeof(buf), &hp, &h_errno); + return hp; } -#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; +libc_hidden_def(gethostbyname) #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 : ""), class, type); +#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 - 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 @@ -1858,1290 +1744,1383 @@ libc_hidden_def(gethostbyaddr) #endif -#ifdef L_read_etc_hosts_r - -FILE * __open_etc_hosts(void) -{ - FILE * fp; - if ((fp = fopen("/etc/hosts", "r")) == NULL) { - fp = fopen("/etc/config/hosts", "r"); - } - return fp; -} +#ifdef L_getnameinfo -int attribute_hidden __read_etc_hosts_r( - FILE * fp, - const char * name, - int type, - enum etc_hosts_action action, - struct hostent * result_buf, - char * buf, size_t buflen, - struct hostent ** result, - int * h_errnop) +int getnameinfo(const struct sockaddr *sa, socklen_t addrlen, char *host, + socklen_t hostlen, char *serv, socklen_t servlen, + unsigned int flags) { - struct in_addr *in = NULL; - struct in_addr **addr_list = NULL; -#ifdef __UCLIBC_HAS_IPV6__ - struct in6_addr *in6 = NULL; - struct in6_addr **addr_list6 = NULL; -#endif /* __UCLIBC_HAS_IPV6__ */ - char *cp, **alias; - int aliases, i, ret = HOST_NOT_FOUND; + int serrno = errno; + unsigned ok; + struct hostent *h = NULL; + char domain[256]; - /* make sure user char * is aligned */ - i = ALIGN_BUFFER_OFFSET(buf); - if (unlikely(i)) { - if (buflen < i) - return ERANGE; - buf += i; - buflen -= i; - } + if (flags & ~(NI_NUMERICHOST|NI_NUMERICSERV|NI_NOFQDN|NI_NAMEREQD|NI_DGRAM)) + return EAI_BADFLAGS; - if (buflen < sizeof(char *) * ALIAS_DIM) - return ERANGE; - alias = (char **)buf; - buf += sizeof(char **) * ALIAS_DIM; - buflen -= sizeof(char **) * ALIAS_DIM; + if (sa == NULL || addrlen < sizeof(sa_family_t)) + goto BAD_FAM; - if (action != GETHOSTENT) { + ok = sa->sa_family; + if (ok == AF_LOCAL) /* valid */; +#ifdef __UCLIBC_HAS_IPV4__ + else if (ok == AF_INET) { + if (addrlen < sizeof(struct sockaddr_in)) + goto BAD_FAM; + } +#endif #ifdef __UCLIBC_HAS_IPV6__ - char *p = buf; - size_t len = buflen; + else if (ok == AF_INET6) { + if (addrlen < sizeof(struct sockaddr_in6)) + goto BAD_FAM; + } #endif /* __UCLIBC_HAS_IPV6__ */ - *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; + else +BAD_FAM: + return EAI_FAMILY; + ok = 0; + if (host != NULL && hostlen > 0) + switch (sa->sa_family) { + case AF_INET: #ifdef __UCLIBC_HAS_IPV6__ - if (len < sizeof(*in6)) - return ERANGE; - 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 (len < buflen) { - buflen = len; - buf = p; - } -#endif /* __UCLIBC_HAS_IPV6__ */ - - if (buflen < 80) - return ERANGE; - - fp = __open_etc_hosts(); - if (fp == NULL) { - *result = NULL; - return errno; - } - } + case AF_INET6: +#endif + if (!(flags & NI_NUMERICHOST)) { + if (0) /* nothing */; +#ifdef __UCLIBC_HAS_IPV6__ + else if (sa->sa_family == AF_INET6) + h = gethostbyaddr((const void *) + &(((const struct sockaddr_in6 *) sa)->sin6_addr), + sizeof(struct in6_addr), AF_INET6); +#endif +#ifdef __UCLIBC_HAS_IPV4__ + else + h = gethostbyaddr((const void *) + &(((const struct sockaddr_in *)sa)->sin_addr), + sizeof(struct in_addr), AF_INET); +#endif - *h_errnop = HOST_NOT_FOUND; - while (fgets(buf, buflen, fp)) { - cp = strchr(buf, '#'); - if (cp) - *cp = '\0'; - DPRINTF("Looking at: %s\n", buf); - aliases = 0; + if (h) { + char *c; +#undef min +#define min(x,y) (((x) > (y)) ? (y) : (x)) + if ((flags & NI_NOFQDN) + && (__libc_getdomainname(domain, sizeof(domain)) == 0) + && (c = strstr(h->h_name, domain)) != NULL + && (c != h->h_name) && (*(--c) == '.') + ) { + strncpy(host, h->h_name, + min(hostlen, (size_t) (c - h->h_name))); + host[min(hostlen - 1, (size_t) (c - h->h_name))] = '\0'; + } else { + strncpy(host, h->h_name, hostlen); + } + ok = 1; +#undef min + } + } - cp = buf; - while (*cp) { - while (*cp && isspace(*cp)) - *cp++ = '\0'; - if (!*cp) - break; - if (aliases < (2+MAX_ALIASES)) - alias[aliases++] = cp; - while (*cp && !isspace(*cp)) - cp++; - } - alias[aliases] = NULL; + if (!ok) { + const char *c = NULL; - if (aliases < 2) - continue; /* syntax error really */ + if (flags & NI_NAMEREQD) { + errno = serrno; + return EAI_NONAME; + } + if (0) /* nothing */; +#ifdef __UCLIBC_HAS_IPV6__ + else if (sa->sa_family == AF_INET6) { + const struct sockaddr_in6 *sin6p; - if (action == GETHOSTENT) { - /* Return whatever the next entry happens to be. */ + sin6p = (const struct sockaddr_in6 *) sa; + c = inet_ntop(AF_INET6, + (const void *) &sin6p->sin6_addr, + host, hostlen); +#if 0 + /* Does scope id need to be supported? */ + uint32_t scopeid; + scopeid = sin6p->sin6_scope_id; + if (scopeid != 0) { + /* Buffer is >= IFNAMSIZ+1. */ + char scopebuf[IFNAMSIZ + 1]; + char *scopeptr; + int ni_numericscope = 0; + size_t real_hostlen = strnlen(host, hostlen); + size_t scopelen = 0; + + scopebuf[0] = SCOPE_DELIMITER; + scopebuf[1] = '\0'; + scopeptr = &scopebuf[1]; + + if (IN6_IS_ADDR_LINKLOCAL(&sin6p->sin6_addr) + || IN6_IS_ADDR_MC_LINKLOCAL(&sin6p->sin6_addr)) { + if (if_indextoname(scopeid, scopeptr) == NULL) + ++ni_numericscope; + else + scopelen = strlen(scopebuf); + } else { + ++ni_numericscope; + } + + if (ni_numericscope) + scopelen = 1 + snprintf(scopeptr, + (scopebuf + + sizeof scopebuf + - scopeptr), + "%u", scopeid); + + if (real_hostlen + scopelen + 1 > hostlen) + return EAI_SYSTEM; + memcpy(host + real_hostlen, scopebuf, scopelen + 1); + } +#endif + } +#endif /* __UCLIBC_HAS_IPV6__ */ +#if defined __UCLIBC_HAS_IPV4__ + else { + c = inet_ntop(AF_INET, (const void *) + &(((const struct sockaddr_in *) sa)->sin_addr), + host, hostlen); + } +#endif + if (c == NULL) { + errno = serrno; + return EAI_SYSTEM; + } + ok = 1; + } break; - } - if (action == GET_HOSTS_BYADDR) { - if (strcmp(name, alias[0]) != 0) - continue; - } else { - /* GET_HOSTS_BYNAME */ - for (i = 1; i < aliases; i++) - if (strcasecmp(name, alias[i]) == 0) + + case AF_LOCAL: + if (!(flags & NI_NUMERICHOST)) { + struct utsname utsname; + + if (!uname(&utsname)) { + strncpy(host, utsname.nodename, hostlen); break; - if (i >= aliases) - continue; - } + }; + }; - if (type == AF_INET && inet_pton(AF_INET, alias[0], in) > 0) { - DPRINTF("Found INET\n"); - addr_list[0] = in; - addr_list[1] = 0; - result_buf->h_name = alias[1]; - result_buf->h_addrtype = AF_INET; - result_buf->h_length = sizeof(*in); - result_buf->h_addr_list = (char**) addr_list; - 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) { - DPRINTF("Found INET6\n"); - addr_list6[0] = in6; - addr_list6[1] = 0; - result_buf->h_name = alias[1]; - result_buf->h_addrtype = AF_INET6; - result_buf->h_length = sizeof(*in6); - result_buf->h_addr_list = (char**) addr_list6; - result_buf->h_aliases = alias + 2; - *result = result_buf; - ret = NETDB_SUCCESS; -#endif /* __UCLIBC_HAS_IPV6__ */ - } else { - /* continue parsing in the hope the user has multiple - * host types listed in the database like so: - * host - * host - * If looking for an IPv6 addr, don't bail when we got the IPv4 - */ - DPRINTF("Error: Found host but diff network type\n"); - /* NB: gethostbyname2_r depends on this feature - * to avoid looking for IPv6 addr of "localhost" etc */ - ret = TRY_AGAIN; - continue; + if (flags & NI_NAMEREQD) { + errno = serrno; + return EAI_NONAME; + } + + strncpy(host, "localhost", hostlen); + break; +/* Already checked above + default: + return EAI_FAMILY; +*/ + } + + if (serv && (servlen > 0)) { + if (sa->sa_family == AF_LOCAL) { + strncpy(serv, ((const struct sockaddr_un *) sa)->sun_path, servlen); + } else { /* AF_INET || AF_INET6 */ + if (!(flags & NI_NUMERICSERV)) { + struct servent *s; + s = getservbyport(((const struct sockaddr_in *) sa)->sin_port, + ((flags & NI_DGRAM) ? "udp" : "tcp")); + if (s) { + strncpy(serv, s->s_name, servlen); + goto DONE; + } + } + snprintf(serv, servlen, "%d", + ntohs(((const struct sockaddr_in *) sa)->sin_port)); } - break; } - if (action != GETHOSTENT) - fclose(fp); - return ret; +DONE: + if (host && (hostlen > 0)) + host[hostlen-1] = 0; + if (serv && (servlen > 0)) + serv[servlen-1] = 0; + errno = serrno; + return 0; } +libc_hidden_def(getnameinfo) #endif -#ifdef L_gethostent +#ifdef L_gethostbyname_r -__UCLIBC_MUTEX_STATIC(mylock, PTHREAD_MUTEX_INITIALIZER); +/* Bug 671 says: + * "uClibc resolver's gethostbyname does not return the requested name + * as an alias, but instead returns the canonical name. glibc's + * gethostbyname has a similar bug where it returns the requested name + * with the search domain name appended (to make a FQDN) as an alias, + * but not the original name itself. Both contradict POSIX, which says + * that the name argument passed to gethostbyname must be in the alias list" + * This is fixed now, and we differ from glibc: + * + * $ ./gethostbyname_uclibc wer.google.com + * h_name:'c13-ss-2-lb.cnet.com' + * h_length:4 + * h_addrtype:2 AF_INET + * alias:'wer.google.com' <=== + * addr: 0x4174efd8 '216.239.116.65' + * + * $ ./gethostbyname_glibc wer.google.com + * h_name:'c13-ss-2-lb.cnet.com' + * h_length:4 + * h_addrtype:2 AF_INET + * alias:'wer.google.com.com' <=== + * addr:'216.239.116.65' + * + * When examples were run, /etc/resolv.conf contained "search com" line. + */ +int gethostbyname_r(const char * name, + struct hostent * result_buf, + char * buf, + size_t buflen, + struct hostent ** result, + int * h_errnop) +{ + struct in_addr **addr_list; + char **alias; + char *alias0; + unsigned char *packet; + struct resolv_answer a; + int i; + int wrong_af = 0; -static smallint __stay_open; -static FILE * __gethostent_fp; + *result = NULL; + if (!name) + return EINVAL; -void endhostent(void) -{ - __UCLIBC_MUTEX_LOCK(mylock); - __stay_open = 0; - if (__gethostent_fp) { - fclose(__gethostent_fp); - __gethostent_fp = NULL; + /* do /etc/hosts first */ + { + int old_errno = errno; /* save the old errno and reset errno */ + __set_errno(0); /* to check for missing /etc/hosts. */ + i = __get_hosts_byname_r(name, AF_INET, result_buf, + buf, buflen, result, h_errnop); + if (i == NETDB_SUCCESS) { + __set_errno(old_errno); + return i; + } + switch (*h_errnop) { + case HOST_NOT_FOUND: + wrong_af = (i == TRY_AGAIN); + case NO_ADDRESS: + break; + case NETDB_INTERNAL: + if (errno == ENOENT) { + break; + } + /* else fall through */ + default: + return i; + } + __set_errno(old_errno); } - __UCLIBC_MUTEX_UNLOCK(mylock); -} -void sethostent(int stay_open) -{ - __UCLIBC_MUTEX_LOCK(mylock); - __stay_open = (stay_open != 0); - __UCLIBC_MUTEX_UNLOCK(mylock); -} + DPRINTF("Nothing found in /etc/hosts\n"); + + *h_errnop = NETDB_INTERNAL; + + /* prepare future h_aliases[0] */ + i = strlen(name) + 1; + if ((ssize_t)buflen <= i) + return ERANGE; + strcpy(buf, name); + alias0 = buf; + buf += i; + buflen -= i; + + /* make sure pointer is aligned */ + i = ALIGN_BUFFER_OFFSET(buf); + buf += i; + buflen -= i; + + alias = (char **)buf; + buf += sizeof(alias[0]) * 2; + buflen -= sizeof(alias[0]) * 2; + + addr_list = (struct in_addr **)buf; + + /* do not use *buf or buflen before this verification! */ + /* buflen may be < 0, must do signed compare */ + if ((ssize_t)buflen < 256) + return ERANGE; -int gethostent_r(struct hostent *result_buf, char *buf, size_t buflen, - struct hostent **result, int *h_errnop) -{ - int ret; + /* we store only one "alias" - the name itself */ +#ifdef __UCLIBC_MJN3_ONLY__ +#warning TODO -- generate the full list +#endif + alias[0] = alias0; + alias[1] = NULL; - __UCLIBC_MUTEX_LOCK(mylock); - if (__gethostent_fp == NULL) { - __gethostent_fp = __open_etc_hosts(); - if (__gethostent_fp == NULL) { - *result = NULL; - ret = TRY_AGAIN; - goto DONE; + /* maybe it is already an address? */ + { + struct in_addr *in = (struct in_addr *)(buf + sizeof(addr_list[0]) * 2); + if (inet_aton(name, in)) { + addr_list[0] = in; + addr_list[1] = NULL; + result_buf->h_name = alias0; + result_buf->h_aliases = alias; + result_buf->h_addrtype = AF_INET; + result_buf->h_length = sizeof(struct in_addr); + result_buf->h_addr_list = (char **) addr_list; + *result = result_buf; + *h_errnop = NETDB_SUCCESS; + return NETDB_SUCCESS; } } - ret = __read_etc_hosts_r(__gethostent_fp, NULL, AF_INET, GETHOSTENT, - result_buf, buf, buflen, result, h_errnop); - if (__stay_open == 0) { - fclose(__gethostent_fp); - __gethostent_fp = NULL; + /* what if /etc/hosts has it but it's not IPv4? + * F.e. "::1 localhost6". We don't do DNS query for such hosts - + * "ping localhost6" should be fast even if DNS server is down! */ + if (wrong_af) { + *h_errnop = HOST_NOT_FOUND; + return TRY_AGAIN; } -DONE: - __UCLIBC_MUTEX_UNLOCK(mylock); - return ret; -} -libc_hidden_def(gethostent_r) - -struct hostent *gethostent(void) -{ - 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 + - 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 + /* talk to DNS servers */ + { + 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, &packet, &a); + if (i < 0) { + *h_errnop = HOST_NOT_FOUND; + DPRINTF("__dns_lookup returned < 0\n"); + return TRY_AGAIN; + } + } + if (a.atype == T_A) { /* ADDRESS */ + /* we need space for addr_list[] and one IPv4 address */ + /* + 1 accounting for 1st addr (it's in a.rdata), + * another + 1 for NULL in last addr_list[]: */ + int need_bytes = sizeof(addr_list[0]) * (a.add_count + 1 + 1) + /* for 1st addr (it's in a.rdata): */ + + sizeof(struct in_addr); + /* how many bytes will 2nd and following addresses take? */ + int ips_len = a.add_count * a.rdlength; -#ifdef L_get_hosts_byname_r + buflen -= (need_bytes + ips_len); + if ((ssize_t)buflen < 0) { + DPRINTF("buffer too small for all addresses\n"); + /* *h_errnop = NETDB_INTERNAL; - already is */ + i = ERANGE; + goto free_and_ret; + } -int attribute_hidden __get_hosts_byname_r(const char * name, int type, - struct hostent * result_buf, - char * buf, size_t buflen, - struct hostent ** result, - int * h_errnop) -{ - return __read_etc_hosts_r(NULL, name, type, GET_HOSTS_BYNAME, - result_buf, buf, buflen, result, h_errnop); -} -#endif + /* if there are additional addresses in buf, + * move them forward so that they are not destroyed */ + DPRINTF("a.add_count:%d a.rdlength:%d a.rdata:%p\n", a.add_count, a.rdlength, a.rdata); + memmove(buf + need_bytes, buf, ips_len); + /* 1st address is in a.rdata, insert it */ + buf += need_bytes - sizeof(struct in_addr); + memcpy(buf, a.rdata, sizeof(struct in_addr)); -#ifdef L_get_hosts_byaddr_r + /* fill addr_list[] */ + for (i = 0; i <= a.add_count; i++) { + addr_list[i] = (struct in_addr*)buf; + buf += sizeof(struct in_addr); + } + addr_list[i] = NULL; -int attribute_hidden __get_hosts_byaddr_r(const char * addr, int len, int type, - struct hostent * result_buf, - char * buf, size_t buflen, - struct hostent ** result, - int * h_errnop) -{ -#ifndef __UCLIBC_HAS_IPV6__ - char ipaddr[INET_ADDRSTRLEN]; -#else - char ipaddr[INET6_ADDRSTRLEN]; -#endif /* __UCLIBC_HAS_IPV6__ */ + /* if we have enough space, we can report "better" name + * (it may contain search domains attached by __dns_lookup, + * or CNAME of the host if it is different from the name + * we used to find it) */ + if (a.dotted && buflen > strlen(a.dotted)) { + strcpy(buf, a.dotted); + alias0 = buf; + } - switch (type) { - case AF_INET: - if (len != sizeof(struct in_addr)) - return 0; - break; -#ifdef __UCLIBC_HAS_IPV6__ - case AF_INET6: - if (len != sizeof(struct in6_addr)) - return 0; - break; -#endif /* __UCLIBC_HAS_IPV6__ */ - default: - return 0; + result_buf->h_name = alias0; + result_buf->h_aliases = alias; + result_buf->h_addrtype = AF_INET; + result_buf->h_length = sizeof(struct in_addr); + result_buf->h_addr_list = (char **) addr_list; + *result = result_buf; + *h_errnop = NETDB_SUCCESS; + i = NETDB_SUCCESS; + goto free_and_ret; } - inet_ntop(type, addr, ipaddr, sizeof(ipaddr)); + *h_errnop = HOST_NOT_FOUND; + i = TRY_AGAIN; - return __read_etc_hosts_r(NULL, ipaddr, type, GET_HOSTS_BYADDR, - result_buf, buf, buflen, result, h_errnop); + free_and_ret: + free(a.dotted); + free(packet); + return i; } +libc_hidden_def(gethostbyname_r) #endif -#ifdef L_getnameinfo +#ifdef L_gethostbyname2_r -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) +int gethostbyname2_r(const char *name, + int family, + struct hostent * result_buf, + char * buf, + size_t buflen, + struct hostent ** result, + int * h_errnop) { - int serrno = errno; - unsigned ok; - struct hostent *h = NULL; - char domain[256]; - - if (flags & ~(NI_NUMERICHOST|NI_NUMERICSERV|NI_NOFQDN|NI_NAMEREQD|NI_DGRAM)) - return EAI_BADFLAGS; - - if (sa == NULL || addrlen < sizeof(sa_family_t)) - goto BAD_FAM; - - ok = sa->sa_family; - if (ok == AF_LOCAL) /* valid */; -#ifdef __UCLIBC_HAS_IPV4__ - else if (ok == AF_INET) { - if (addrlen < sizeof(struct sockaddr_in)) - goto BAD_FAM; - } -#endif -#ifdef __UCLIBC_HAS_IPV6__ - else if (ok == AF_INET6) { - if (addrlen < sizeof(struct sockaddr_in6)) - goto BAD_FAM; - } -#endif /* __UCLIBC_HAS_IPV6__ */ - else -BAD_FAM: - return EAI_FAMILY; - - ok = 0; - if (host != NULL && hostlen > 0) - switch (sa->sa_family) { - case AF_INET: -#ifdef __UCLIBC_HAS_IPV6__ - case AF_INET6: -#endif - if (!(flags & NI_NUMERICHOST)) { - if (0) /* nothing */; -#ifdef __UCLIBC_HAS_IPV6__ - else if (sa->sa_family == AF_INET6) - h = gethostbyaddr((const void *) - &(((const struct sockaddr_in6 *) sa)->sin6_addr), - sizeof(struct in6_addr), AF_INET6); -#endif -#ifdef __UCLIBC_HAS_IPV4__ - else - h = gethostbyaddr((const void *) - &(((const struct sockaddr_in *)sa)->sin_addr), - sizeof(struct in_addr), AF_INET); -#endif - - if (h) { - char *c; -#undef min -#define min(x,y) (((x) > (y)) ? (y) : (x)) - if ((flags & NI_NOFQDN) - && (__libc_getdomainname(domain, sizeof(domain)) == 0) - && (c = strstr(h->h_name, domain)) != NULL - && (c != h->h_name) && (*(--c) == '.') - ) { - strncpy(host, h->h_name, - min(hostlen, (size_t) (c - h->h_name))); - host[min(hostlen - 1, (size_t) (c - h->h_name))] = '\0'; - } else { - strncpy(host, h->h_name, hostlen); - } - ok = 1; -#undef min - } - } +#ifndef __UCLIBC_HAS_IPV6__ + return family == (AF_INET) + ? gethostbyname_r(name, result_buf, buf, buflen, result, h_errnop) + : HOST_NOT_FOUND; +#else + struct in6_addr *in; + struct in6_addr **addr_list; + unsigned char *packet; + struct resolv_answer a; + int i; + int nest = 0; + int wrong_af = 0; - if (!ok) { - const char *c = NULL; + if (family == AF_INET) + return gethostbyname_r(name, result_buf, buf, buflen, result, h_errnop); - if (flags & NI_NAMEREQD) { - errno = serrno; - return EAI_NONAME; + if (family != AF_INET6) + return EINVAL; + + *result = NULL; + if (!name) + return EINVAL; + + /* do /etc/hosts first */ + { + int old_errno = errno; /* Save the old errno and reset errno */ + __set_errno(0); /* to check for missing /etc/hosts. */ + + i = __get_hosts_byname_r(name, family, result_buf, + buf, buflen, result, h_errnop); + if (i == NETDB_SUCCESS) { + __set_errno(old_errno); + return i; + } + switch (*h_errnop) { + case HOST_NOT_FOUND: + wrong_af = (i == TRY_AGAIN); + case NO_ADDRESS: + break; + case NETDB_INTERNAL: + if (errno == ENOENT) { + break; } - if (0) /* nothing */; -#ifdef __UCLIBC_HAS_IPV6__ - else if (sa->sa_family == AF_INET6) { - const struct sockaddr_in6 *sin6p; + /* else fall through */ + default: + return i; + } + __set_errno(old_errno); + } + DPRINTF("Nothing found in /etc/hosts\n"); - sin6p = (const struct sockaddr_in6 *) sa; - c = inet_ntop(AF_INET6, - (const void *) &sin6p->sin6_addr, - host, hostlen); -#if 0 - /* Does scope id need to be supported? */ - uint32_t scopeid; - scopeid = sin6p->sin6_scope_id; - if (scopeid != 0) { - /* Buffer is >= IFNAMSIZ+1. */ - char scopebuf[IFNAMSIZ + 1]; - char *scopeptr; - int ni_numericscope = 0; - size_t real_hostlen = strnlen(host, hostlen); - size_t scopelen = 0; + *h_errnop = NETDB_INTERNAL; - scopebuf[0] = SCOPE_DELIMITER; - scopebuf[1] = '\0'; - scopeptr = &scopebuf[1]; + /* make sure pointer is aligned */ + i = ALIGN_BUFFER_OFFSET(buf); + buf += i; + buflen -= i; - if (IN6_IS_ADDR_LINKLOCAL(&sin6p->sin6_addr) - || IN6_IS_ADDR_MC_LINKLOCAL(&sin6p->sin6_addr)) { - if (if_indextoname(scopeid, scopeptr) == NULL) - ++ni_numericscope; - else - scopelen = strlen(scopebuf); - } else { - ++ni_numericscope; - } + in = (struct in6_addr*)buf; + buf += sizeof(*in); + buflen -= sizeof(*in); + addr_list = (struct in6_addr**)buf; + buf += sizeof(*addr_list) * 2; + buflen -= sizeof(*addr_list) * 2; + if ((ssize_t)buflen < 256) + return ERANGE; + addr_list[0] = in; + addr_list[1] = NULL; + strncpy(buf, name, buflen); - if (ni_numericscope) - scopelen = 1 + snprintf(scopeptr, - (scopebuf - + sizeof scopebuf - - scopeptr), - "%u", scopeid); + /* maybe it is already an address? */ + if (inet_pton(AF_INET6, name, in)) { + result_buf->h_name = buf; + result_buf->h_addrtype = AF_INET6; + result_buf->h_length = sizeof(*in); + result_buf->h_addr_list = (char **) addr_list; + *result = result_buf; + *h_errnop = NETDB_SUCCESS; + return NETDB_SUCCESS; + } - if (real_hostlen + scopelen + 1 > hostlen) - return EAI_SYSTEM; - memcpy(host + real_hostlen, scopebuf, scopelen + 1); - } -#endif - } -#endif /* __UCLIBC_HAS_IPV6__ */ -#if defined __UCLIBC_HAS_IPV4__ - else { - c = inet_ntop(AF_INET, (const void *) - &(((const struct sockaddr_in *) sa)->sin_addr), - host, hostlen); - } -#endif - if (c == NULL) { - errno = serrno; - return EAI_SYSTEM; - } - ok = 1; - } - break; + /* what if /etc/hosts has it but it's not IPv6? + * F.e. "127.0.0.1 localhost". We don't do DNS query for such hosts - + * "ping localhost" should be fast even if DNS server is down! */ + if (wrong_af) { + *h_errnop = HOST_NOT_FOUND; + return TRY_AGAIN; + } - case AF_LOCAL: - if (!(flags & NI_NUMERICHOST)) { - struct utsname utsname; + /* talk to DNS servers */ +// TODO: why it's so different from gethostbyname_r (IPv4 case)? + memset(&a, '\0', sizeof(a)); + for (;;) { + i = __dns_lookup(buf, T_AAAA, &packet, &a); - if (!uname(&utsname)) { - strncpy(host, utsname.nodename, hostlen); - break; - }; - }; + if (i < 0) { + *h_errnop = HOST_NOT_FOUND; + return TRY_AGAIN; + } - if (flags & NI_NAMEREQD) { - errno = serrno; - return EAI_NONAME; - } + strncpy(buf, a.dotted, buflen); + free(a.dotted); - strncpy(host, "localhost", hostlen); - break; -/* Already checked above - default: - return EAI_FAMILY; -*/ - } + if (a.atype == T_CNAME) { /* CNAME */ + DPRINTF("Got a CNAME in gethostbyname()\n"); + i = __decode_dotted(packet, a.rdoffset, buf, buflen); + free(packet); - if (serv && (servlen > 0)) { - if (sa->sa_family == AF_LOCAL) { - strncpy(serv, ((const struct sockaddr_un *) sa)->sun_path, servlen); - } else { /* AF_INET || AF_INET6 */ - if (!(flags & NI_NUMERICSERV)) { - struct servent *s; - s = getservbyport(((const struct sockaddr_in *) sa)->sin_port, - ((flags & NI_DGRAM) ? "udp" : "tcp")); - if (s) { - strncpy(serv, s->s_name, servlen); - goto DONE; - } + if (i < 0) { + *h_errnop = NO_RECOVERY; + return -1; } - snprintf(serv, servlen, "%d", - ntohs(((const struct sockaddr_in *) sa)->sin_port)); + if (++nest > MAX_RECURSE) { + *h_errnop = NO_RECOVERY; + return -1; + } + continue; + } + if (a.atype == T_AAAA) { /* ADDRESS */ + memcpy(in, a.rdata, sizeof(*in)); + result_buf->h_name = buf; + result_buf->h_addrtype = AF_INET6; + result_buf->h_length = sizeof(*in); + result_buf->h_addr_list = (char **) addr_list; + free(packet); + break; } + free(packet); + *h_errnop = HOST_NOT_FOUND; + return TRY_AGAIN; } -DONE: - if (host && (hostlen > 0)) - host[hostlen-1] = 0; - if (serv && (servlen > 0)) - serv[servlen-1] = 0; - errno = serrno; - return 0; + + *result = result_buf; + *h_errnop = NETDB_SUCCESS; + return NETDB_SUCCESS; +#endif /* __UCLIBC_HAS_IPV6__ */ } -libc_hidden_def(getnameinfo) +libc_hidden_def(gethostbyname2_r) #endif -#ifdef L_gethostbyname_r +#ifdef L_gethostbyaddr_r + +int gethostbyaddr_r(const void *addr, socklen_t len, int type, + struct hostent * result_buf, + char * buf, size_t buflen, + struct hostent ** result, + int * h_errnop) -/* Bug 671 says: - * "uClibc resolver's gethostbyname does not return the requested name - * as an alias, but instead returns the canonical name. glibc's - * gethostbyname has a similar bug where it returns the requested name - * with the search domain name appended (to make a FQDN) as an alias, - * but not the original name itself. Both contradict POSIX, which says - * that the name argument passed to gethostbyname must be in the alias list" - * This is fixed now, and we differ from glibc: - * - * $ ./gethostbyname_uclibc wer.google.com - * h_name:'c13-ss-2-lb.cnet.com' - * h_length:4 - * h_addrtype:2 AF_INET - * alias:'wer.google.com' <=== - * addr: 0x4174efd8 '216.239.116.65' - * - * $ ./gethostbyname_glibc wer.google.com - * h_name:'c13-ss-2-lb.cnet.com' - * h_length:4 - * h_addrtype:2 AF_INET - * alias:'wer.google.com.com' <=== - * addr:'216.239.116.65' - * - * When examples were run, /etc/resolv.conf contained "search com" line. - */ -int gethostbyname_r(const char * name, - struct hostent * result_buf, - char * buf, - size_t buflen, - struct hostent ** result, - int * h_errnop) { + struct in_addr *in; struct in_addr **addr_list; +#ifdef __UCLIBC_HAS_IPV6__ + char *qp; + size_t plen; + struct in6_addr *in6; + struct in6_addr **addr_list6; +#endif /* __UCLIBC_HAS_IPV6__ */ char **alias; - char *alias0; unsigned char *packet; struct resolv_answer a; int i; - int wrong_af = 0; + int nest = 0; *result = NULL; - if (!name) + if (!addr) return EINVAL; + memset(&a, '\0', sizeof(a)); + + switch (type) { + case AF_INET: + if (len != sizeof(struct in_addr)) + return EINVAL; + break; +#ifdef __UCLIBC_HAS_IPV6__ + case AF_INET6: + if (len != sizeof(struct in6_addr)) + return EINVAL; + break; +#endif /* __UCLIBC_HAS_IPV6__ */ + default: + return EINVAL; + } + /* do /etc/hosts first */ - { - int old_errno = errno; /* save the old errno and reset errno */ - __set_errno(0); /* to check for missing /etc/hosts. */ - i = __get_hosts_byname_r(name, AF_INET, result_buf, + i = __get_hosts_byaddr_r(addr, len, type, result_buf, buf, buflen, result, h_errnop); - if (i == NETDB_SUCCESS) { - __set_errno(old_errno); + if (i == 0) + return i; + switch (*h_errnop) { + case HOST_NOT_FOUND: + case NO_ADDRESS: + break; + default: return i; - } - switch (*h_errnop) { - case HOST_NOT_FOUND: - wrong_af = (i == TRY_AGAIN); - case NO_ADDRESS: - break; - case NETDB_INTERNAL: - if (errno == ENOENT) { - break; - } - /* else fall through */ - default: - return i; - } - __set_errno(old_errno); } - DPRINTF("Nothing found in /etc/hosts\n"); - *h_errnop = NETDB_INTERNAL; - /* prepare future h_aliases[0] */ - i = strlen(name) + 1; - if ((ssize_t)buflen <= i) - return ERANGE; - strcpy(buf, name); - alias0 = buf; - buf += i; - buflen -= i; - /* make sure pointer is aligned */ i = ALIGN_BUFFER_OFFSET(buf); buf += i; buflen -= i; +#ifdef __UCLIBC_HAS_IPV6__ + qp = buf; + plen = buflen; +#endif + in = (struct in_addr*)buf; + buf += sizeof(*in); + buflen -= sizeof(*in); + addr_list = (struct in_addr**)buf; + buf += sizeof(*addr_list) * 2; + buflen -= sizeof(*addr_list) * 2; alias = (char **)buf; - buf += sizeof(alias[0]) * 2; - buflen -= sizeof(alias[0]) * 2; - - addr_list = (struct in_addr **)buf; - - /* do not use *buf or buflen before this verification! */ - /* buflen may be < 0, must do signed compare */ - if ((ssize_t)buflen < 256) + buf += sizeof(*alias) * ALIAS_DIM; + buflen -= sizeof(*alias) * ALIAS_DIM; + if ((ssize_t)buflen < 0) return ERANGE; - - /* we store only one "alias" - the name itself */ -#ifdef __UCLIBC_MJN3_ONLY__ -#warning TODO -- generate the full list +#ifdef __UCLIBC_HAS_IPV6__ + in6 = (struct in6_addr*)qp; + qp += sizeof(*in6); + plen -= sizeof(*in6); + addr_list6 = (struct in6_addr**)qp; + qp += sizeof(*addr_list6) * 2; + plen -= sizeof(*addr_list6) * 2; + if ((ssize_t)plen < 0) + return ERANGE; + if (plen < buflen) { + buflen = plen; + buf = qp; + } #endif - alias[0] = alias0; - alias[1] = NULL; + if (buflen < 256) + return ERANGE; - /* maybe it is already an address? */ - { - struct in_addr *in = (struct in_addr *)(buf + sizeof(addr_list[0]) * 2); - if (inet_aton(name, in)) { - addr_list[0] = in; - addr_list[1] = NULL; - result_buf->h_name = alias0; - result_buf->h_aliases = alias; - result_buf->h_addrtype = AF_INET; - result_buf->h_length = sizeof(struct in_addr); - result_buf->h_addr_list = (char **) addr_list; - *result = result_buf; - *h_errnop = NETDB_SUCCESS; - return NETDB_SUCCESS; + if (type == AF_INET) { + unsigned char *tmp_addr = (unsigned char *)addr; + + memcpy(&in->s_addr, addr, len); + addr_list[0] = in; + sprintf(buf, "%u.%u.%u.%u.in-addr.arpa", + tmp_addr[3], tmp_addr[2], tmp_addr[1], tmp_addr[0]); + } +#ifdef __UCLIBC_HAS_IPV6__ + else { + memcpy(in6->s6_addr, addr, len); + addr_list6[0] = in6; + qp = buf; + for (i = len - 1; i >= 0; i--) { + qp += sprintf(qp, "%x.%x.", + in6->s6_addr[i] & 0xf, + (in6->s6_addr[i] >> 4) & 0xf); } + strcpy(qp, "ip6.arpa"); } +#endif + addr_list[1] = NULL; - /* what if /etc/hosts has it but it's not IPv4? - * F.e. "::1 localhost6". We don't do DNS query for such hosts - - * "ping localhost6" should be fast even if DNS server is down! */ - if (wrong_af) { - *h_errnop = HOST_NOT_FOUND; - return TRY_AGAIN; - } + alias[0] = buf; + alias[1] = NULL; + + for (;;) { + i = __dns_lookup(buf, T_PTR, &packet, &a); - /* talk to DNS servers */ - { - 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, &packet, &a); if (i < 0) { *h_errnop = HOST_NOT_FOUND; - DPRINTF("__dns_lookup returned < 0\n"); return TRY_AGAIN; } - } - - if (a.atype == T_A) { /* ADDRESS */ - /* we need space for addr_list[] and one IPv4 address */ - /* + 1 accounting for 1st addr (it's in a.rdata), - * another + 1 for NULL in last addr_list[]: */ - int need_bytes = sizeof(addr_list[0]) * (a.add_count + 1 + 1) - /* for 1st addr (it's in a.rdata): */ - + sizeof(struct in_addr); - /* how many bytes will 2nd and following addresses take? */ - int ips_len = a.add_count * a.rdlength; - - buflen -= (need_bytes + ips_len); - if ((ssize_t)buflen < 0) { - DPRINTF("buffer too small for all addresses\n"); - /* *h_errnop = NETDB_INTERNAL; - already is */ - i = ERANGE; - goto free_and_ret; - } - /* if there are additional addresses in buf, - * move them forward so that they are not destroyed */ - DPRINTF("a.add_count:%d a.rdlength:%d a.rdata:%p\n", a.add_count, a.rdlength, a.rdata); - memmove(buf + need_bytes, buf, ips_len); + strncpy(buf, a.dotted, buflen); + free(a.dotted); - /* 1st address is in a.rdata, insert it */ - buf += need_bytes - sizeof(struct in_addr); - memcpy(buf, a.rdata, sizeof(struct in_addr)); + if (a.atype == T_CNAME) { /* CNAME */ + DPRINTF("Got a CNAME in gethostbyaddr()\n"); + i = __decode_dotted(packet, a.rdoffset, buf, buflen); + free(packet); - /* fill addr_list[] */ - for (i = 0; i <= a.add_count; i++) { - addr_list[i] = (struct in_addr*)buf; - buf += sizeof(struct in_addr); + if (i < 0) { + *h_errnop = NO_RECOVERY; + return -1; + } + if (++nest > MAX_RECURSE) { + *h_errnop = NO_RECOVERY; + return -1; + } + continue; } - addr_list[i] = NULL; - /* if we have enough space, we can report "better" name - * (it may contain search domains attached by __dns_lookup, - * or CNAME of the host if it is different from the name - * we used to find it) */ - if (a.dotted && buflen > strlen(a.dotted)) { - strcpy(buf, a.dotted); - alias0 = buf; + if (a.atype == T_PTR) { /* ADDRESS */ + i = __decode_dotted(packet, a.rdoffset, buf, buflen); + free(packet); + + result_buf->h_name = buf; + result_buf->h_addrtype = type; + if (type == AF_INET) + result_buf->h_length = sizeof(*in); +#ifdef __UCLIBC_HAS_IPV6__ + else + result_buf->h_length = sizeof(*in6); +#endif + result_buf->h_addr_list = (char **) addr_list; + result_buf->h_aliases = alias; + break; } - result_buf->h_name = alias0; - result_buf->h_aliases = alias; - result_buf->h_addrtype = AF_INET; - result_buf->h_length = sizeof(struct in_addr); - result_buf->h_addr_list = (char **) addr_list; - *result = result_buf; - *h_errnop = NETDB_SUCCESS; - i = NETDB_SUCCESS; - goto free_and_ret; + free(packet); + *h_errnop = NO_ADDRESS; + return TRY_AGAIN; } - *h_errnop = HOST_NOT_FOUND; - i = TRY_AGAIN; + *result = result_buf; + *h_errnop = NETDB_SUCCESS; + return NETDB_SUCCESS; +} +libc_hidden_def(gethostbyaddr_r) +#endif + - free_and_ret: - free(a.dotted); - free(packet); - return i; +#ifdef L_res_comp + +/* + * Expand compressed domain name 'comp_dn' to full domain name. + * 'msg' is a pointer to the begining of the message, + * 'eomorig' points to the first location after the message, + * 'exp_dn' is a pointer to a buffer of size 'length' for the result. + * Return size of compressed name or -1 if there was an error. + */ +int __dn_expand(const u_char *msg, const u_char *eom, const u_char *src, + char *dst, int dstsiz) +{ + int n = ns_name_uncompress(msg, eom, src, dst, (size_t)dstsiz); + + if (n > 0 && dst[0] == '.') + dst[0] = '\0'; + return n; } -libc_hidden_def(gethostbyname_r) -#endif +#endif /* L_res_comp */ -#ifdef L_gethostbyname2_r +#ifdef L_ns_name -int gethostbyname2_r(const char *name, - int family, - struct hostent * result_buf, - char * buf, - size_t buflen, - struct hostent ** result, - int * h_errnop) +/* Thinking in noninternationalized USASCII (per the DNS spec), + * is this character visible and not a space when printed ? + */ +static int printable(int ch) { -#ifndef __UCLIBC_HAS_IPV6__ - return family == (AF_INET) - ? gethostbyname_r(name, result_buf, buf, buflen, result, h_errnop) - : HOST_NOT_FOUND; -#else - struct in6_addr *in; - struct in6_addr **addr_list; - unsigned char *packet; - struct resolv_answer a; - int i; - int nest = 0; - int wrong_af = 0; + return (ch > 0x20 && ch < 0x7f); +} +/* Thinking in noninternationalized USASCII (per the DNS spec), + * is this characted special ("in need of quoting") ? + */ +static int special(int ch) +{ + switch (ch) { + case 0x22: /* '"' */ + case 0x2E: /* '.' */ + case 0x3B: /* ';' */ + case 0x5C: /* '\\' */ + /* Special modifiers in zone files. */ + case 0x40: /* '@' */ + case 0x24: /* '$' */ + return 1; + default: + return 0; + } +} - if (family == AF_INET) - return gethostbyname_r(name, result_buf, buf, buflen, result, h_errnop); +/* + * ns_name_uncompress(msg, eom, src, dst, dstsiz) + * Expand compressed domain name to presentation format. + * return: + * Number of bytes read out of `src', or -1 (with errno set). + * note: + * Root domain returns as "." not "". + */ +int ns_name_uncompress(const u_char *msg, const u_char *eom, + const u_char *src, char *dst, size_t dstsiz) +{ + u_char tmp[NS_MAXCDNAME]; + int n; - if (family != AF_INET6) - return EINVAL; + n = ns_name_unpack(msg, eom, src, tmp, sizeof tmp); + if (n == -1) + return -1; + if (ns_name_ntop(tmp, dst, dstsiz) == -1) + return -1; + return n; +} +libc_hidden_def(ns_name_uncompress) - *result = NULL; - if (!name) - return EINVAL; +/* + * ns_name_ntop(src, dst, dstsiz) + * Convert an encoded domain name to printable ascii as per RFC1035. + * return: + * Number of bytes written to buffer, or -1 (with errno set) + * notes: + * The root is returned as "." + * All other domains are returned in non absolute form + */ +int ns_name_ntop(const u_char *src, char *dst, size_t dstsiz) +{ + static const char digits[] = "0123456789"; - /* do /etc/hosts first */ - { - int old_errno = errno; /* Save the old errno and reset errno */ - __set_errno(0); /* to check for missing /etc/hosts. */ + const u_char *cp; + char *dn, *eom; + u_char c; + u_int n; - i = __get_hosts_byname_r(name, family, result_buf, - buf, buflen, result, h_errnop); - if (i == NETDB_SUCCESS) { - __set_errno(old_errno); - return i; + cp = src; + dn = dst; + eom = dst + dstsiz; + + while ((n = *cp++) != 0) { + if ((n & NS_CMPRSFLGS) != 0) { + /* Some kind of compression pointer. */ + __set_errno(EMSGSIZE); + return -1; } - switch (*h_errnop) { - case HOST_NOT_FOUND: - wrong_af = (i == TRY_AGAIN); - case NO_ADDRESS: - break; - case NETDB_INTERNAL: - if (errno == ENOENT) { - break; + if (dn != dst) { + if (dn >= eom) { + __set_errno(EMSGSIZE); + return -1; + } + *dn++ = '.'; + } + if (dn + n >= eom) { + __set_errno(EMSGSIZE); + return -1; + } + for ((void)NULL; n > 0; n--) { + c = *cp++; + if (special(c)) { + if (dn + 1 >= eom) { + __set_errno(EMSGSIZE); + return -1; } - /* else fall through */ - default: - return i; + *dn++ = '\\'; + *dn++ = (char)c; + } else if (!printable(c)) { + if (dn + 3 >= eom) { + __set_errno(EMSGSIZE); + return -1; + } + *dn++ = '\\'; + *dn++ = digits[c / 100]; + *dn++ = digits[(c % 100) / 10]; + *dn++ = digits[c % 10]; + } else { + if (dn >= eom) { + __set_errno(EMSGSIZE); + return -1; + } + *dn++ = (char)c; + } } - __set_errno(old_errno); } - DPRINTF("Nothing found in /etc/hosts\n"); - - *h_errnop = NETDB_INTERNAL; - - /* make sure pointer is aligned */ - i = ALIGN_BUFFER_OFFSET(buf); - buf += i; - buflen -= i; - - in = (struct in6_addr*)buf; - buf += sizeof(*in); - buflen -= sizeof(*in); - addr_list = (struct in6_addr**)buf; - buf += sizeof(*addr_list) * 2; - buflen -= sizeof(*addr_list) * 2; - if ((ssize_t)buflen < 256) - return ERANGE; - addr_list[0] = in; - addr_list[1] = NULL; - strncpy(buf, name, buflen); - - /* maybe it is already an address? */ - if (inet_pton(AF_INET6, name, in)) { - result_buf->h_name = buf; - result_buf->h_addrtype = AF_INET6; - result_buf->h_length = sizeof(*in); - result_buf->h_addr_list = (char **) addr_list; - *result = result_buf; - *h_errnop = NETDB_SUCCESS; - return NETDB_SUCCESS; + if (dn == dst) { + if (dn >= eom) { + __set_errno(EMSGSIZE); + return -1; + } + *dn++ = '.'; } - - /* what if /etc/hosts has it but it's not IPv6? - * F.e. "127.0.0.1 localhost". We don't do DNS query for such hosts - - * "ping localhost" should be fast even if DNS server is down! */ - if (wrong_af) { - *h_errnop = HOST_NOT_FOUND; - return TRY_AGAIN; + if (dn >= eom) { + __set_errno(EMSGSIZE); + return -1; } + *dn++ = '\0'; + return (dn - dst); +} +libc_hidden_def(ns_name_ntop) - /* talk to DNS servers */ -// TODO: why it's so different from gethostbyname_r (IPv4 case)? - memset(&a, '\0', sizeof(a)); - for (;;) { - i = __dns_lookup(buf, T_AAAA, &packet, &a); - - if (i < 0) { - *h_errnop = HOST_NOT_FOUND; - return TRY_AGAIN; - } +/* + * ns_name_unpack(msg, eom, src, dst, dstsiz) + * Unpack a domain name from a message, source may be compressed. + * return: + * -1 if it fails, or consumed octets if it succeeds. + */ +int ns_name_unpack(const u_char *msg, const u_char *eom, const u_char *src, + u_char *dst, size_t dstsiz) +{ + const u_char *srcp, *dstlim; + u_char *dstp; + int n, len, checked; - strncpy(buf, a.dotted, buflen); - free(a.dotted); + len = -1; + checked = 0; + dstp = dst; + srcp = src; + dstlim = dst + dstsiz; + if (srcp < msg || srcp >= eom) { + __set_errno(EMSGSIZE); + return -1; + } + /* Fetch next label in domain name. */ + while ((n = *srcp++) != 0) { + /* Check for indirection. */ + switch (n & NS_CMPRSFLGS) { + case 0: + /* Limit checks. */ + if (dstp + n + 1 >= dstlim || srcp + n >= eom) { + __set_errno(EMSGSIZE); + return -1; + } + checked += n + 1; + *dstp++ = n; + memcpy(dstp, srcp, n); + dstp += n; + srcp += n; + break; - if (a.atype == T_CNAME) { /* CNAME */ - DPRINTF("Got a CNAME in gethostbyname()\n"); - i = __decode_dotted(packet, a.rdoffset, buf, buflen); - free(packet); + case NS_CMPRSFLGS: + if (srcp >= eom) { + __set_errno(EMSGSIZE); + return -1; + } + if (len < 0) + len = srcp - src + 1; + srcp = msg + (((n & 0x3f) << 8) | (*srcp & 0xff)); + if (srcp < msg || srcp >= eom) { /* Out of range. */ + __set_errno(EMSGSIZE); + return -1; + } + checked += 2; + /* + * Check for loops in the compressed name; + * if we've looked at the whole message, + * there must be a loop. + */ + if (checked >= eom - msg) { + __set_errno(EMSGSIZE); + return -1; + } + break; - if (i < 0) { - *h_errnop = NO_RECOVERY; - return -1; - } - if (++nest > MAX_RECURSE) { - *h_errnop = NO_RECOVERY; - return -1; - } - continue; - } - if (a.atype == T_AAAA) { /* ADDRESS */ - memcpy(in, a.rdata, sizeof(*in)); - result_buf->h_name = buf; - result_buf->h_addrtype = AF_INET6; - result_buf->h_length = sizeof(*in); - result_buf->h_addr_list = (char **) addr_list; - free(packet); - break; + default: + __set_errno(EMSGSIZE); + return -1; /* flag error */ } - free(packet); - *h_errnop = HOST_NOT_FOUND; - return TRY_AGAIN; } - - *result = result_buf; - *h_errnop = NETDB_SUCCESS; - return NETDB_SUCCESS; -#endif /* __UCLIBC_HAS_IPV6__ */ + *dstp = '\0'; + if (len < 0) + len = srcp - src; + return len; } -libc_hidden_def(gethostbyname2_r) -#endif +libc_hidden_def(ns_name_unpack) +#endif /* L_ns_name */ -#ifdef L_gethostbyaddr_r +#ifdef L_res_init -int gethostbyaddr_r(const void *addr, socklen_t len, int type, - struct hostent * result_buf, - char * buf, size_t buflen, - struct hostent ** result, - int * h_errnop) +/* Protected by __resolv_lock */ +struct __res_state _res; +/* Will be called under __resolv_lock. */ +static void res_sync_func(void) { - struct in_addr *in; - struct in_addr **addr_list; -#ifdef __UCLIBC_HAS_IPV6__ - char *qp; - size_t plen; - struct in6_addr *in6; - struct in6_addr **addr_list6; -#endif /* __UCLIBC_HAS_IPV6__ */ - char **alias; - unsigned char *packet; - struct resolv_answer a; - int i; - int nest = 0; - - *result = NULL; - if (!addr) - return EINVAL; - - memset(&a, '\0', sizeof(a)); + struct __res_state *rp = &(_res); + int n; - switch (type) { - case AF_INET: - if (len != sizeof(struct in_addr)) - return EINVAL; - break; + /* 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__ - case AF_INET6: - if (len != sizeof(struct in6_addr)) - return EINVAL; - break; -#endif /* __UCLIBC_HAS_IPV6__ */ - default: - return EINVAL; + 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; + // ... +} - /* do /etc/hosts first */ - i = __get_hosts_byaddr_r(addr, len, type, result_buf, - buf, buflen, result, h_errnop); - if (i == 0) - return i; - switch (*h_errnop) { - case HOST_NOT_FOUND: - case NO_ADDRESS: - break; - default: - return i; - } +/* 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 - *h_errnop = NETDB_INTERNAL; + __UCLIBC_MUTEX_LOCK(__resolv_lock); + __close_nameservers(); + __open_nameservers(); - /* make sure pointer is aligned */ - i = ALIGN_BUFFER_OFFSET(buf); - buf += i; - buflen -= i; + __res_sync = res_sync_func; -#ifdef __UCLIBC_HAS_IPV6__ - qp = buf; - plen = buflen; + 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 - in = (struct in_addr*)buf; - buf += sizeof(*in); - buflen -= sizeof(*in); - addr_list = (struct in_addr**)buf; - buf += sizeof(*addr_list) * 2; - buflen -= sizeof(*addr_list) * 2; - alias = (char **)buf; - buf += sizeof(*alias) * ALIAS_DIM; - buflen -= sizeof(*alias) * ALIAS_DIM; - if ((ssize_t)buflen < 0) - return ERANGE; -#ifdef __UCLIBC_HAS_IPV6__ - in6 = (struct in6_addr*)qp; - qp += sizeof(*in6); - plen -= sizeof(*in6); - addr_list6 = (struct in6_addr**)qp; - qp += sizeof(*addr_list6) * 2; - plen -= sizeof(*addr_list6) * 2; - if ((ssize_t)plen < 0) - return ERANGE; - if (plen < buflen) { - buflen = plen; - buf = qp; - } + rp->ndots = 1; +#ifdef __UCLIBC_HAS_EXTRA_COMPAT_RES_STATE__ + rp->_vcsock = -1; #endif - if (buflen < 256) - return ERANGE; - if (type == AF_INET) { - unsigned char *tmp_addr = (unsigned char *)addr; + n = __searchdomains; + if (n > ARRAY_SIZE(rp->dnsrch)) + n = ARRAY_SIZE(rp->dnsrch); + for (i = 0; i < n; i++) + rp->dnsrch[i] = __searchdomain[i]; - memcpy(&in->s_addr, addr, len); - addr_list[0] = in; - sprintf(buf, "%u.%u.%u.%u.in-addr.arpa", - tmp_addr[3], tmp_addr[2], tmp_addr[1], tmp_addr[0]); - } + /* 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__ - else { - memcpy(in6->s6_addr, addr, len); - addr_list6[0] = in6; - qp = buf; - for (i = len - 1; i >= 0; i--) { - qp += sprintf(qp, "%x.%x.", - in6->s6_addr[i] & 0xf, - (in6->s6_addr[i] >> 4) & 0xf); + 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++; + } } - strcpy(qp, "ip6.arpa"); +#endif + i++; } + rp->nscount = n; +#ifdef __UCLIBC_HAS_IPV6__ + rp->_u._ext.nscount = m; #endif - addr_list[1] = NULL; - - alias[0] = buf; - alias[1] = NULL; - - for (;;) { - i = __dns_lookup(buf, T_PTR, &packet, &a); - - if (i < 0) { - *h_errnop = HOST_NOT_FOUND; - return TRY_AGAIN; - } - - strncpy(buf, a.dotted, buflen); - free(a.dotted); - - if (a.atype == T_CNAME) { /* CNAME */ - DPRINTF("Got a CNAME in gethostbyaddr()\n"); - i = __decode_dotted(packet, a.rdoffset, buf, buflen); - free(packet); - if (i < 0) { - *h_errnop = NO_RECOVERY; - return -1; - } - if (++nest > MAX_RECURSE) { - *h_errnop = NO_RECOVERY; - return -1; - } - continue; +#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 - if (a.atype == T_PTR) { /* ADDRESS */ - i = __decode_dotted(packet, a.rdoffset, buf, buflen); - free(packet); + __UCLIBC_MUTEX_UNLOCK(__resolv_lock); + return 0; +} +libc_hidden_def(res_init) - result_buf->h_name = buf; - result_buf->h_addrtype = type; - if (type == AF_INET) - result_buf->h_length = sizeof(*in); +#ifdef __UCLIBC_HAS_BSD_RES_CLOSE__ +void res_close(void) +{ + __UCLIBC_MUTEX_LOCK(__resolv_lock); + __close_nameservers(); + __res_sync = NULL; #ifdef __UCLIBC_HAS_IPV6__ - else - result_buf->h_length = sizeof(*in6); -#endif - result_buf->h_addr_list = (char **) addr_list; - result_buf->h_aliases = alias; - break; + { + 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); } - - free(packet); - *h_errnop = NO_ADDRESS; - return TRY_AGAIN; } - - *result = result_buf; - *h_errnop = NETDB_SUCCESS; - return NETDB_SUCCESS; +#endif + memset(&_res, 0, sizeof(_res)); + __UCLIBC_MUTEX_UNLOCK(__resolv_lock); } -libc_hidden_def(gethostbyaddr_r) #endif +#endif /* L_res_init */ -#ifdef L_res_comp +#ifdef L_res_query -/* - * Expand compressed domain name 'comp_dn' to full domain name. - * 'msg' is a pointer to the begining of the message, - * 'eomorig' points to the first location after the message, - * 'exp_dn' is a pointer to a buffer of size 'length' for the result. - * Return size of compressed name or -1 if there was an error. - */ -int __dn_expand(const u_char *msg, const u_char *eom, const u_char *src, - char *dst, int dstsiz) +int res_query(const char *dname, int class, int type, + unsigned char *answer, int anslen) { - int n = ns_name_uncompress(msg, eom, src, dst, (size_t)dstsiz); + int i; + unsigned char * packet = NULL; + struct resolv_answer a; - if (n > 0 && dst[0] == '.') - dst[0] = '\0'; - return n; -} -#endif /* L_res_comp */ + if (!dname || class != 1 /* CLASS_IN */) { + h_errno = NO_RECOVERY; + return -1; + } + memset(&a, '\0', sizeof(a)); + i = __dns_lookup(dname, type, &packet, &a); -#ifdef L_ns_name + if (i < 0) { + h_errno = TRY_AGAIN; + return -1; + } -/* - * printable(ch) - * Thinking in noninternationalized USASCII (per the DNS spec), - * is this character visible and not a space when printed ? - * return: - * boolean. - */ -static int printable(int ch) -{ - return (ch > 0x20 && ch < 0x7f); -} + free(a.dotted); -/* - * special(ch) - * Thinking in noninternationalized USASCII (per the DNS spec), - * is this characted special ("in need of quoting") ? - * return: - * boolean. - */ -static int special(int ch) -{ - switch (ch) { - case 0x22: /* '"' */ - case 0x2E: /* '.' */ - case 0x3B: /* ';' */ - case 0x5C: /* '\\' */ - /* Special modifiers in zone files. */ - case 0x40: /* '@' */ - case 0x24: /* '$' */ - return 1; - default: - return 0; + if (a.atype == type) { /* CNAME */ + if (i > anslen) + i = anslen; + memcpy(answer, packet, i); } + free(packet); + return i; } +libc_hidden_def(res_query) /* - * ns_name_uncompress(msg, eom, src, dst, dstsiz) - * Expand compressed domain name to presentation format. - * return: - * Number of bytes read out of `src', or -1 (with errno set). - * note: - * Root domain returns as "." not "". + * 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. */ -int ns_name_uncompress(const u_char *msg, const u_char *eom, - const u_char *src, char *dst, size_t dstsiz) +#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) { - u_char tmp[NS_MAXCDNAME]; - int n; + 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; - n = ns_name_unpack(msg, eom, src, tmp, sizeof tmp); - if (n == -1) - return -1; - if (ns_name_ntop(tmp, dst, dstsiz) == -1) + if (!name || !answer) { + h_errno = NETDB_INTERNAL; return -1; - return n; -} -libc_hidden_def(ns_name_uncompress) + } -/* - * ns_name_ntop(src, dst, dstsiz) - * Convert an encoded domain name to printable ascii as per RFC1035. - * return: - * Number of bytes written to buffer, or -1 (with errno set) - * notes: - * The root is returned as "." - * All other domains are returned in non absolute form - */ -int ns_name_ntop(const u_char *src, char *dst, size_t dstsiz) -{ - static const char digits[] = "0123456789"; + 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; - const u_char *cp; - char *dn, *eom; - u_char c; - u_int n; + for (domain = (const char * const *)_res_dnsrch; + *domain && !done; + domain++) { - cp = src; - dn = dst; - eom = dst + dstsiz; + ret = res_querydomain(name, *domain, class, type, + answer, anslen); + if (ret > 0) + return ret; - while ((n = *cp++) != 0) { - if ((n & NS_CMPRSFLGS) != 0) { - /* Some kind of compression pointer. */ - __set_errno(EMSGSIZE); - return -1; - } - if (dn != dst) { - if (dn >= eom) { - __set_errno(EMSGSIZE); + /* + * 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; } - *dn++ = '.'; - } - if (dn + n >= eom) { - __set_errno(EMSGSIZE); - return -1; - } - for ((void)NULL; n > 0; n--) { - c = *cp++; - if (special(c)) { - if (dn + 1 >= eom) { - __set_errno(EMSGSIZE); - return -1; - } - *dn++ = '\\'; - *dn++ = (char)c; - } else if (!printable(c)) { - if (dn + 3 >= eom) { - __set_errno(EMSGSIZE); - return -1; - } - *dn++ = '\\'; - *dn++ = digits[c / 100]; - *dn++ = digits[(c % 100) / 10]; - *dn++ = digits[c % 10]; - } else { - if (dn >= eom) { - __set_errno(EMSGSIZE); - return -1; - } - *dn++ = (char)c; + + 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 (dn == dst) { - if (dn >= eom) { - __set_errno(EMSGSIZE); - return -1; - } - *dn++ = '.'; - } - if (dn >= eom) { - __set_errno(EMSGSIZE); - return -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; } - *dn++ = '\0'; - return (dn - dst); -} -libc_hidden_def(ns_name_ntop) + /* + * 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 /* - * ns_name_unpack(msg, eom, src, dst, dstsiz) - * Unpack a domain name from a message, source may be compressed. - * return: - * -1 if it fails, or consumed octets if it succeeds. + * Perform a call on res_query on the concatenation of name and domain, + * removing a trailing dot from name if domain is NULL. */ -int ns_name_unpack(const u_char *msg, const u_char *eom, const u_char *src, - u_char *dst, size_t dstsiz) +int res_querydomain(const char *name, const char *domain, int class, int type, + u_char * answer, int anslen) { - const u_char *srcp, *dstlim; - u_char *dstp; - int n, len, checked; + char nbuf[MAXDNAME]; + const char *longname = nbuf; + size_t n, d; +#ifdef DEBUG + uint32_t _res_options; +#endif - len = -1; - checked = 0; - dstp = dst; - srcp = src; - dstlim = dst + dstsiz; - if (srcp < msg || srcp >= eom) { - __set_errno(EMSGSIZE); + if (!name || !answer) { + h_errno = NETDB_INTERNAL; return -1; } - /* Fetch next label in domain name. */ - while ((n = *srcp++) != 0) { - /* Check for indirection. */ - switch (n & NS_CMPRSFLGS) { - case 0: - /* Limit checks. */ - if (dstp + n + 1 >= dstlim || srcp + n >= eom) { - __set_errno(EMSGSIZE); - return -1; - } - checked += n + 1; - *dstp++ = n; - memcpy(dstp, srcp, n); - dstp += n; - srcp += n; - break; - - case NS_CMPRSFLGS: - if (srcp >= eom) { - __set_errno(EMSGSIZE); - return -1; - } - if (len < 0) - len = srcp - src + 1; - srcp = msg + (((n & 0x3f) << 8) | (*srcp & 0xff)); - if (srcp < msg || srcp >= eom) { /* Out of range. */ - __set_errno(EMSGSIZE); - return -1; - } - checked += 2; - /* - * Check for loops in the compressed name; - * if we've looked at the whole message, - * there must be a loop. - */ - if (checked >= eom - msg) { - __set_errno(EMSGSIZE); - return -1; - } - break; - default: - __set_errno(EMSGSIZE); - return -1; /* flag error */ +#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 : ""), 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); } - *dstp = '\0'; - if (len < 0) - len = srcp - src; - return len; + return res_query(longname, class, type, answer, anslen); } -libc_hidden_def(ns_name_unpack) -#endif /* L_ns_name */ +libc_hidden_def(res_querydomain) +/* res_mkquery */ +/* res_send */ +/* dn_comp */ +/* dn_expand */ +#endif + /* vi: set sw=4 ts=4: */ -- cgit v1.2.3