Переглянути джерело

resolver: move large code blocks to arrange related functions closer.
almost no code changes

Denis Vlasenko 15 роки тому
батько
коміт
94cec9a5ee
1 змінених файлів з 1446 додано та 1467 видалено
  1. 1446 1467
      libc/inet/resolv.c

+ 1446 - 1467
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:
+			 * <ipv4 addr> host
+			 * <ipv6 addr> 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 : "<Nil>"), 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:
-			 * <ipv4 addr> host
-			 * <ipv6 addr> 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 : "<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);
 	}
-	*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: */