xref: /openbsd-src/lib/libc/asr/gethostnamadr_async.c (revision cd3730786509462c9f40deda12b017ae12a5e5f0)
1*cd373078Sop /*	$OpenBSD: gethostnamadr_async.c,v 1.50 2024/09/03 18:20:35 op Exp $	*/
2b44da627Seric /*
3b44da627Seric  * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
4b44da627Seric  *
5b44da627Seric  * Permission to use, copy, modify, and distribute this software for any
6b44da627Seric  * purpose with or without fee is hereby granted, provided that the above
7b44da627Seric  * copyright notice and this permission notice appear in all copies.
8b44da627Seric  *
9b44da627Seric  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10b44da627Seric  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11b44da627Seric  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12b44da627Seric  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13b44da627Seric  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14b44da627Seric  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15b44da627Seric  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16b44da627Seric  */
1780f48568Seric 
18b44da627Seric #include <sys/types.h>
19b44da627Seric #include <sys/socket.h>
20b44da627Seric #include <netinet/in.h>
21b44da627Seric #include <arpa/inet.h>
22b44da627Seric #include <arpa/nameser.h>
23d216d6b1Seric #include <netdb.h>
24b44da627Seric 
25d216d6b1Seric #include <asr.h>
26b32de4eaSeric #include <ctype.h>
2780f48568Seric #include <errno.h>
2801b887f7Seric #include <resolv.h> /* for res_hnok */
2980f48568Seric #include <stdlib.h>
3080f48568Seric #include <string.h>
3180f48568Seric #include <unistd.h>
32e7e445a1Sderaadt #include <limits.h>
33e7e445a1Sderaadt 
34b44da627Seric #include "asr_private.h"
35b44da627Seric 
365712b4f1Sbrynet #define MAXALIASES	35
375712b4f1Sbrynet #define MAXADDRS	35
38b44da627Seric 
39cd22283fSeric struct hostent_ext {
40cd22283fSeric 	struct hostent	 h;
41cd22283fSeric 	char		*aliases[MAXALIASES + 1];
42cd22283fSeric 	char		*addrs[MAXADDRS + 1];
43cd22283fSeric 	char		*end;
44cd22283fSeric 	char		*pos;
45cd22283fSeric };
46836b804aSeric 
47b5afe704Sschwarze struct netent_ext {
48b5afe704Sschwarze 	struct netent	 n;
49b5afe704Sschwarze 	char		*aliases[MAXALIASES + 1];
50b5afe704Sschwarze 	char		*end;
51b5afe704Sschwarze 	char		*pos;
52b5afe704Sschwarze };
53b5afe704Sschwarze 
545be03f8fSeric static int gethostnamadr_async_run(struct asr_query *, struct asr_result *);
55cd22283fSeric static struct hostent_ext *hostent_alloc(int);
56cd22283fSeric static int hostent_set_cname(struct hostent_ext *, const char *, int);
57cd22283fSeric static int hostent_add_alias(struct hostent_ext *, const char *, int);
58cd22283fSeric static int hostent_add_addr(struct hostent_ext *, const void *, size_t);
59b32de4eaSeric static struct hostent_ext *hostent_from_addr(int, const char *, const char *);
60cd22283fSeric static struct hostent_ext *hostent_file_match(FILE *, int, int, const char *,
61cd22283fSeric     int);
62cd22283fSeric static struct hostent_ext *hostent_from_packet(int, int, char *, size_t);
63b5afe704Sschwarze static void netent_from_hostent(struct asr_result *ar);
64b44da627Seric 
655be03f8fSeric struct asr_query *
665be03f8fSeric gethostbyname_async(const char *name, void *asr)
67b44da627Seric {
68b44da627Seric 	return gethostbyname2_async(name, AF_INET, asr);
69b44da627Seric }
705826fd8cSguenther DEF_WEAK(gethostbyname_async);
71b44da627Seric 
725be03f8fSeric struct asr_query *
735be03f8fSeric gethostbyname2_async(const char *name, int af, void *asr)
74b44da627Seric {
75b44da627Seric 	struct asr_ctx	 *ac;
765be03f8fSeric 	struct asr_query *as;
77b44da627Seric 
78b44da627Seric 	/* the original segfaults */
79b44da627Seric 	if (name == NULL) {
80b44da627Seric 		errno = EINVAL;
81b44da627Seric 		return (NULL);
82b44da627Seric 	}
83b44da627Seric 
84253ef892Sderaadt 	ac = _asr_use_resolver(asr);
85253ef892Sderaadt 	if ((as = _asr_async_new(ac, ASR_GETHOSTBYNAME)) == NULL)
86b44da627Seric 		goto abort; /* errno set */
87b44da627Seric 	as->as_run = gethostnamadr_async_run;
88b44da627Seric 
89b44da627Seric 	as->as.hostnamadr.family = af;
90b44da627Seric 	if (af == AF_INET)
91b44da627Seric 		as->as.hostnamadr.addrlen = INADDRSZ;
92b44da627Seric 	else if (af == AF_INET6)
93b44da627Seric 		as->as.hostnamadr.addrlen = IN6ADDRSZ;
94b44da627Seric 	as->as.hostnamadr.name = strdup(name);
95b44da627Seric 	if (as->as.hostnamadr.name == NULL)
96b44da627Seric 		goto abort; /* errno set */
97b44da627Seric 
98253ef892Sderaadt 	_asr_ctx_unref(ac);
99b44da627Seric 	return (as);
100b44da627Seric 
101b44da627Seric     abort:
102b44da627Seric 	if (as)
103253ef892Sderaadt 		_asr_async_free(as);
104253ef892Sderaadt 	_asr_ctx_unref(ac);
105b44da627Seric 	return (NULL);
106b44da627Seric }
1075826fd8cSguenther DEF_WEAK(gethostbyname2_async);
108b44da627Seric 
1095be03f8fSeric struct asr_query *
1105be03f8fSeric gethostbyaddr_async(const void *addr, socklen_t len, int af, void *asr)
111b44da627Seric {
112b44da627Seric 	struct asr_ctx	 *ac;
1135be03f8fSeric 	struct asr_query *as;
114b44da627Seric 
115253ef892Sderaadt 	ac = _asr_use_resolver(asr);
116253ef892Sderaadt 	as = _gethostbyaddr_async_ctx(addr, len, af, ac);
117253ef892Sderaadt 	_asr_ctx_unref(ac);
118b44da627Seric 
119b44da627Seric 	return (as);
120b44da627Seric }
1215826fd8cSguenther DEF_WEAK(gethostbyaddr_async);
122b44da627Seric 
1235be03f8fSeric struct asr_query *
124253ef892Sderaadt _gethostbyaddr_async_ctx(const void *addr, socklen_t len, int af,
125b44da627Seric     struct asr_ctx *ac)
126b44da627Seric {
1275be03f8fSeric 	struct asr_query *as;
128b44da627Seric 
129253ef892Sderaadt 	if ((as = _asr_async_new(ac, ASR_GETHOSTBYADDR)) == NULL)
130b44da627Seric 		goto abort; /* errno set */
131b44da627Seric 	as->as_run = gethostnamadr_async_run;
132b44da627Seric 
133b44da627Seric 	as->as.hostnamadr.family = af;
134b44da627Seric 	as->as.hostnamadr.addrlen = len;
135b44da627Seric 	if (len > 0)
136b44da627Seric 		memmove(as->as.hostnamadr.addr, addr, (len > 16) ? 16 : len);
137b44da627Seric 
138b44da627Seric 	return (as);
139b44da627Seric 
140b44da627Seric     abort:
141b44da627Seric 	if (as)
142253ef892Sderaadt 		_asr_async_free(as);
143b44da627Seric 	return (NULL);
144b44da627Seric }
145b44da627Seric 
146b44da627Seric static int
1475be03f8fSeric gethostnamadr_async_run(struct asr_query *as, struct asr_result *ar)
148b44da627Seric {
149cd22283fSeric 	struct hostent_ext	*h;
150cd22283fSeric 	int			 r, type, saved_errno;
151b44da627Seric 	FILE			*f;
1526a7a3d64Seric 	char			 name[MAXDNAME], *data, addr[16], *c;
153b44da627Seric 
154b44da627Seric     next:
155b44da627Seric 	switch (as->as_state) {
156b44da627Seric 
157b44da627Seric 	case ASR_STATE_INIT:
158b44da627Seric 
159b44da627Seric 		if (as->as.hostnamadr.family != AF_INET &&
160b44da627Seric 		    as->as.hostnamadr.family != AF_INET6) {
161b44da627Seric 			ar->ar_h_errno = NETDB_INTERNAL;
162b44da627Seric 			ar->ar_errno = EAFNOSUPPORT;
163b44da627Seric 			async_set_state(as, ASR_STATE_HALT);
164b44da627Seric 			break;
165b44da627Seric 		}
166b44da627Seric 
167b44da627Seric 		if ((as->as.hostnamadr.family == AF_INET &&
168b44da627Seric 		     as->as.hostnamadr.addrlen != INADDRSZ) ||
169b44da627Seric 		    (as->as.hostnamadr.family == AF_INET6 &&
170b44da627Seric 		     as->as.hostnamadr.addrlen != IN6ADDRSZ)) {
171b44da627Seric 			ar->ar_h_errno = NETDB_INTERNAL;
172b44da627Seric 			ar->ar_errno = EINVAL;
173b44da627Seric 			async_set_state(as, ASR_STATE_HALT);
174b44da627Seric 			break;
175b44da627Seric 		}
176b44da627Seric 
177b32de4eaSeric 		if (as->as_type == ASR_GETHOSTBYNAME) {
178ab50be5eSeric 
179ab50be5eSeric 			if (as->as.hostnamadr.name[0] == '\0') {
180ab50be5eSeric 				ar->ar_h_errno = NO_DATA;
181ab50be5eSeric 				async_set_state(as, ASR_STATE_HALT);
182ab50be5eSeric 				break;
183ab50be5eSeric 			}
184ab50be5eSeric 
185ab50be5eSeric 			/* Name might be an IP address string */
186b32de4eaSeric 			for (c = as->as.hostnamadr.name; *c; c++)
187dfe5467eSderaadt 				if (!isdigit((unsigned char)*c) &&
188dfe5467eSderaadt 				     *c != '.' && *c != ':')
189b32de4eaSeric 					break;
190b32de4eaSeric 			if (*c == 0 &&
191b32de4eaSeric 			    inet_pton(as->as.hostnamadr.family,
192b32de4eaSeric 			    as->as.hostnamadr.name, addr) == 1) {
193b32de4eaSeric 				h = hostent_from_addr(as->as.hostnamadr.family,
194b32de4eaSeric 				    as->as.hostnamadr.name, addr);
195b32de4eaSeric 				if (h == NULL) {
196b32de4eaSeric 					ar->ar_errno = errno;
197b32de4eaSeric 					ar->ar_h_errno = NETDB_INTERNAL;
198b32de4eaSeric 				}
199b32de4eaSeric 				else {
200b32de4eaSeric 					ar->ar_hostent = &h->h;
201b32de4eaSeric 					ar->ar_h_errno = NETDB_SUCCESS;
202b32de4eaSeric 				}
203b32de4eaSeric 				async_set_state(as, ASR_STATE_HALT);
204b44da627Seric 				break;
205b8fce260Sflorian 			}
206b8fce260Sflorian 
2071b04c78cSflorian 			if (!hnok_lenient(as->as.hostnamadr.name)) {
20806579650Sflorian 				ar->ar_h_errno = NETDB_INTERNAL;
20906579650Sflorian 				ar->ar_errno = EINVAL;
2101b04c78cSflorian 				async_set_state(as, ASR_STATE_HALT);
211b8fce260Sflorian 				break;
212b44da627Seric 			}
213373da8abSflorian 
214373da8abSflorian 			/*
215373da8abSflorian 			 * If hostname is "localhost" or falls within the
216373da8abSflorian 			 * ".localhost." domain, use local address.
217373da8abSflorian 			 * RFC 6761, 6.3:
218373da8abSflorian 			 * 3. Name resolution APIs and libraries SHOULD
219373da8abSflorian 			 * recognize localhost names as special and SHOULD
220373da8abSflorian 			 * always return the IP loopback address for address
221373da8abSflorian 			 * queries and negative responses for all other query
222373da8abSflorian 			 * types.  Name resolution APIs SHOULD NOT send queries
223373da8abSflorian 			 * for localhost names to their configured caching DNS
224373da8abSflorian 			 * server(s).
225373da8abSflorian 			 */
226373da8abSflorian 
227373da8abSflorian 			if (_asr_is_localhost(as->as.hostnamadr.name)) {
228373da8abSflorian 				inet_pton(as->as.hostnamadr.family,
229373da8abSflorian 				    as->as.hostnamadr.family == AF_INET ?
230373da8abSflorian 				    "127.0.0.1" : "::1", addr);
231373da8abSflorian 				h = hostent_from_addr(as->as.hostnamadr.family,
232373da8abSflorian 				    as->as.hostnamadr.name, addr);
233373da8abSflorian 				if (h == NULL) {
234373da8abSflorian 					ar->ar_errno = errno;
235373da8abSflorian 					ar->ar_h_errno = NETDB_INTERNAL;
236373da8abSflorian 				}
237373da8abSflorian 				else {
238373da8abSflorian 					ar->ar_hostent = &h->h;
239373da8abSflorian 					ar->ar_h_errno = NETDB_SUCCESS;
240373da8abSflorian 				}
241373da8abSflorian 				async_set_state(as, ASR_STATE_HALT);
242373da8abSflorian 				break;
243373da8abSflorian 			}
244b44da627Seric 		}
245b44da627Seric 		async_set_state(as, ASR_STATE_NEXT_DB);
246b44da627Seric 		break;
247b44da627Seric 
248b44da627Seric 	case ASR_STATE_NEXT_DB:
249b44da627Seric 
250253ef892Sderaadt 		if (_asr_iter_db(as) == -1) {
251b44da627Seric 			async_set_state(as, ASR_STATE_NOT_FOUND);
252b44da627Seric 			break;
253b44da627Seric 		}
254b44da627Seric 
255b44da627Seric 		switch (AS_DB(as)) {
256b44da627Seric 
257b44da627Seric 		case ASR_DB_DNS:
258b44da627Seric 
259b44da627Seric 			/* Create a subquery to do the DNS lookup */
260b44da627Seric 
261b44da627Seric 			if (as->as_type == ASR_GETHOSTBYNAME) {
262b44da627Seric 				type = (as->as.hostnamadr.family == AF_INET) ?
263b44da627Seric 				    T_A : T_AAAA;
264f6f51dadSeric 				as->as_subq = _res_search_async_ctx(
2651e1dfc0cSeric 				    as->as.hostnamadr.name,
266c5221d45Seric 				    C_IN, type, as->as_ctx);
267b44da627Seric 			} else {
268253ef892Sderaadt 				_asr_addr_as_fqdn(as->as.hostnamadr.addr,
269b44da627Seric 				    as->as.hostnamadr.family,
2706a7a3d64Seric 				    name, sizeof(name));
271f6f51dadSeric 				as->as_subq = _res_query_async_ctx(
2726a7a3d64Seric 				    name, C_IN, T_PTR, as->as_ctx);
273b44da627Seric 			}
274b44da627Seric 
275f6f51dadSeric 			if (as->as_subq == NULL) {
276b44da627Seric 				ar->ar_errno = errno;
277b44da627Seric 				ar->ar_h_errno = NETDB_INTERNAL;
278b44da627Seric 				async_set_state(as, ASR_STATE_HALT);
279b44da627Seric 				break;
280b44da627Seric 			}
281b44da627Seric 
282b44da627Seric 			async_set_state(as, ASR_STATE_SUBQUERY);
283b44da627Seric 			break;
284b44da627Seric 
285b44da627Seric 		case ASR_DB_FILE:
286b44da627Seric 
287b44da627Seric 			/* Try to find a match in the host file */
288b44da627Seric 
289d2d7f9c9Seric 			if ((f = fopen(_PATH_HOSTS, "re")) == NULL)
290b44da627Seric 				break;
291b44da627Seric 
292c126605fSderaadt 			if (as->as_type == ASR_GETHOSTBYNAME)
2931e1dfc0cSeric 				data = as->as.hostnamadr.name;
294b44da627Seric 			else
295b44da627Seric 				data = as->as.hostnamadr.addr;
296b44da627Seric 
297cd22283fSeric 			h = hostent_file_match(f, as->as_type,
2980348accfSeric 			    as->as.hostnamadr.family, data,
2990348accfSeric 			    as->as.hostnamadr.addrlen);
300cd22283fSeric 			saved_errno = errno;
301b44da627Seric 			fclose(f);
302cd22283fSeric 			errno = saved_errno;
3030348accfSeric 
304cd22283fSeric 			if (h == NULL) {
3050348accfSeric 				if (errno) {
306b44da627Seric 					ar->ar_errno = errno;
307b44da627Seric 					ar->ar_h_errno = NETDB_INTERNAL;
308b44da627Seric 					async_set_state(as, ASR_STATE_HALT);
3090348accfSeric 				}
3100348accfSeric 				/* otherwise not found */
311b44da627Seric 				break;
312b44da627Seric 			}
313cd22283fSeric 			ar->ar_hostent = &h->h;
314b44da627Seric 			ar->ar_h_errno = NETDB_SUCCESS;
315b44da627Seric 			async_set_state(as, ASR_STATE_HALT);
316b44da627Seric 			break;
317b44da627Seric 		}
318b44da627Seric 		break;
319b44da627Seric 
320b44da627Seric 	case ASR_STATE_SUBQUERY:
321b44da627Seric 
322b44da627Seric 		/* Run the DNS subquery. */
323b44da627Seric 
324f6f51dadSeric 		if ((r = asr_run(as->as_subq, ar)) == ASYNC_COND)
325b44da627Seric 			return (ASYNC_COND);
326b44da627Seric 
327b44da627Seric 		/* Done. */
328f6f51dadSeric 		as->as_subq = NULL;
329b44da627Seric 
330e45d70c8Smartijn 		/*
331e45d70c8Smartijn 		 * We either got no packet or a packet without an answer.
332*cd373078Sop 		 * Safeguard the h_errno and use the next DB.
333e45d70c8Smartijn 		 */
334b44da627Seric 		if (ar->ar_count == 0) {
335b44da627Seric 			free(ar->ar_data);
3367ffb6b35Seric 			as->as.hostnamadr.subq_h_errno = ar->ar_h_errno;
337b44da627Seric 			async_set_state(as, ASR_STATE_NEXT_DB);
338b44da627Seric 			break;
339b44da627Seric 		}
340b44da627Seric 
341b44da627Seric 		/* Read the hostent from the packet. */
3420348accfSeric 
343cd22283fSeric 		h = hostent_from_packet(as->as_type,
3440348accfSeric 		    as->as.hostnamadr.family, ar->ar_data, ar->ar_datalen);
3450348accfSeric 		free(ar->ar_data);
346cd22283fSeric 		if (h == NULL) {
347b44da627Seric 			ar->ar_errno = errno;
348b44da627Seric 			ar->ar_h_errno = NETDB_INTERNAL;
349b44da627Seric 			async_set_state(as, ASR_STATE_HALT);
350b44da627Seric 			break;
351b44da627Seric 		}
352b44da627Seric 
353b44da627Seric 		if (as->as_type == ASR_GETHOSTBYADDR) {
354cd22283fSeric 			if (hostent_add_addr(h, as->as.hostnamadr.addr,
3550348accfSeric 			    as->as.hostnamadr.addrlen) == -1) {
356cd22283fSeric 				free(h);
3570348accfSeric 				ar->ar_errno = errno;
3580348accfSeric 				ar->ar_h_errno = NETDB_INTERNAL;
3590348accfSeric 				async_set_state(as, ASR_STATE_HALT);
3600348accfSeric 				break;
361b44da627Seric 			}
3620348accfSeric 		}
363b44da627Seric 
364b44da627Seric 		/*
3650d7b84a8Seric 		 * No valid hostname or address found in the dns packet.
3660d7b84a8Seric 		 * Ignore it.
367b44da627Seric 		 */
3686d8f29baSeric 		if ((as->as_type == ASR_GETHOSTBYNAME &&
3696d8f29baSeric 		     h->h.h_addr_list[0] == NULL) ||
3700d7b84a8Seric 		    h->h.h_name == NULL) {
371cd22283fSeric 			free(h);
372b44da627Seric 			async_set_state(as, ASR_STATE_NEXT_DB);
373b44da627Seric 			break;
374b44da627Seric 		}
375b44da627Seric 
376cd22283fSeric 		ar->ar_hostent = &h->h;
377b44da627Seric 		ar->ar_h_errno = NETDB_SUCCESS;
378b44da627Seric 		async_set_state(as, ASR_STATE_HALT);
379b44da627Seric 		break;
380b44da627Seric 
381b44da627Seric 	case ASR_STATE_NOT_FOUND:
382b44da627Seric 		ar->ar_errno = 0;
3837ffb6b35Seric 		if (as->as.hostnamadr.subq_h_errno)
3847ffb6b35Seric 			ar->ar_h_errno = as->as.hostnamadr.subq_h_errno;
3851e1dfc0cSeric 		else
386b44da627Seric 			ar->ar_h_errno = HOST_NOT_FOUND;
387b44da627Seric 		async_set_state(as, ASR_STATE_HALT);
388b44da627Seric 		break;
389b44da627Seric 
390b44da627Seric 	case ASR_STATE_HALT:
391b5afe704Sschwarze 		if (ar->ar_h_errno == NETDB_SUCCESS &&
392b5afe704Sschwarze 		    as->as_flags & ASYNC_GETNET)
393b5afe704Sschwarze 			netent_from_hostent(ar);
394b5afe704Sschwarze 		if (ar->ar_h_errno) {
395b44da627Seric 			ar->ar_hostent = NULL;
396b5afe704Sschwarze 			ar->ar_netent = NULL;
397b5afe704Sschwarze 		} else
398b44da627Seric 			ar->ar_errno = 0;
399b44da627Seric 		return (ASYNC_DONE);
400b44da627Seric 
401b44da627Seric 	default:
402b44da627Seric 		ar->ar_errno = EOPNOTSUPP;
403b44da627Seric 		ar->ar_h_errno = NETDB_INTERNAL;
404b44da627Seric 		ar->ar_gai_errno = EAI_SYSTEM;
405b44da627Seric 		async_set_state(as, ASR_STATE_HALT);
406b44da627Seric 		break;
407b44da627Seric 	}
408b44da627Seric 	goto next;
409b44da627Seric }
410b44da627Seric 
411b44da627Seric /*
412b32de4eaSeric  * Create a hostent from a numeric address string.
413b32de4eaSeric  */
414b32de4eaSeric static struct hostent_ext *
415b32de4eaSeric hostent_from_addr(int family, const char *name, const char *addr)
416b32de4eaSeric {
417b32de4eaSeric 	struct	 hostent_ext *h;
418b32de4eaSeric 
419b32de4eaSeric 	if ((h = hostent_alloc(family)) == NULL)
420b32de4eaSeric 		return (NULL);
421b32de4eaSeric 	if (hostent_set_cname(h, name, 0) == -1)
422b32de4eaSeric 		goto fail;
423b32de4eaSeric 	if (hostent_add_addr(h, addr, h->h.h_length) == -1)
424b32de4eaSeric 		goto fail;
425b32de4eaSeric 	return (h);
426b32de4eaSeric fail:
427b32de4eaSeric 	free(h);
428b32de4eaSeric 	return (NULL);
429b32de4eaSeric }
430b32de4eaSeric 
431b32de4eaSeric /*
432b44da627Seric  * Lookup the first matching entry in the hostfile, either by address or by
4330348accfSeric  * name depending on reqtype, and build a hostent from the line.
434b44da627Seric  */
435cd22283fSeric static struct hostent_ext *
43680f48568Seric hostent_file_match(FILE *f, int reqtype, int family, const char *data,
43780f48568Seric     int datalen)
438b44da627Seric {
439f108579bSeric 	char	*tokens[MAXTOKEN], addr[16], buf[BUFSIZ + 1];
440cd22283fSeric 	struct	 hostent_ext *h;
441b44da627Seric 	int	 n, i;
442b44da627Seric 
443b44da627Seric 	for (;;) {
444253ef892Sderaadt 		n = _asr_parse_namedb_line(f, tokens, MAXTOKEN, buf, sizeof(buf));
4450348accfSeric 		if (n == -1) {
4460348accfSeric 			errno = 0; /* ignore errors reading the file */
4470348accfSeric 			return (NULL);
4480348accfSeric 		}
449b44da627Seric 
450f94f01beSeric 		/* there must be an address and at least one name */
451f94f01beSeric 		if (n < 2)
452f94f01beSeric 			continue;
453f94f01beSeric 
4540348accfSeric 		if (reqtype == ASR_GETHOSTBYNAME) {
455b44da627Seric 			for (i = 1; i < n; i++) {
456b44da627Seric 				if (strcasecmp(data, tokens[i]))
457b44da627Seric 					continue;
458b44da627Seric 				if (inet_pton(family, tokens[0], addr) == 1)
4590348accfSeric 					goto found;
460b44da627Seric 			}
4610348accfSeric 		} else {
462cd22283fSeric 			if (inet_pton(family, tokens[0], addr) == 1 &&
463cd22283fSeric 			    memcmp(addr, data, datalen) == 0)
4640348accfSeric 				goto found;
4650348accfSeric 		}
466b44da627Seric 	}
467b44da627Seric 
4680348accfSeric found:
4690348accfSeric 	if ((h = hostent_alloc(family)) == NULL)
4700348accfSeric 		return (NULL);
4710348accfSeric 	if (hostent_set_cname(h, tokens[1], 0) == -1)
4720348accfSeric 		goto fail;
4730348accfSeric 	for (i = 2; i < n; i ++)
4740348accfSeric 		if (hostent_add_alias(h, tokens[i], 0) == -1)
4750348accfSeric 			goto fail;
476cd22283fSeric 	if (hostent_add_addr(h, addr, h->h.h_length) == -1)
4770348accfSeric 		goto fail;
4780348accfSeric 	return (h);
4790348accfSeric fail:
480836b804aSeric 	free(h);
4810348accfSeric 	return (NULL);
482b44da627Seric }
483b44da627Seric 
484b44da627Seric /*
485b44da627Seric  * Fill the hostent from the given DNS packet.
486b44da627Seric  */
487cd22283fSeric static struct hostent_ext *
4880348accfSeric hostent_from_packet(int reqtype, int family, char *pkt, size_t pktlen)
489b44da627Seric {
490cd22283fSeric 	struct hostent_ext	*h;
491f90bf415Seric 	struct asr_unpack	 p;
492f90bf415Seric 	struct asr_dns_header	 hdr;
493f90bf415Seric 	struct asr_dns_query	 q;
494f90bf415Seric 	struct asr_dns_rr	 rr;
4956d8f29baSeric 	char			 dname[MAXDNAME];
4960348accfSeric 
4970348accfSeric 	if ((h = hostent_alloc(family)) == NULL)
4980348accfSeric 		return (NULL);
499b44da627Seric 
500253ef892Sderaadt 	_asr_unpack_init(&p, pkt, pktlen);
501253ef892Sderaadt 	_asr_unpack_header(&p, &hdr);
502b44da627Seric 	for (; hdr.qdcount; hdr.qdcount--)
503253ef892Sderaadt 		_asr_unpack_query(&p, &q);
5046d8f29baSeric 	strlcpy(dname, q.q_dname, sizeof(dname));
5056d8f29baSeric 
506b44da627Seric 	for (; hdr.ancount; hdr.ancount--) {
507253ef892Sderaadt 		_asr_unpack_rr(&p, &rr);
508b44da627Seric 		if (rr.rr_class != C_IN)
509b44da627Seric 			continue;
510b44da627Seric 		switch (rr.rr_type) {
511b44da627Seric 
512b44da627Seric 		case T_CNAME:
5130348accfSeric 			if (reqtype == ASR_GETHOSTBYNAME) {
5140348accfSeric 				if (hostent_add_alias(h, rr.rr_dname, 1) == -1)
5150348accfSeric 					goto fail;
5160348accfSeric 			} else {
5176d8f29baSeric 				if (strcasecmp(rr.rr_dname, dname) == 0)
5186d8f29baSeric 					strlcpy(dname, rr.rr.cname.cname,
5196d8f29baSeric 					    sizeof(dname));
5200348accfSeric 			}
521b44da627Seric 			break;
522b44da627Seric 
523b44da627Seric 		case T_PTR:
5240348accfSeric 			if (reqtype != ASR_GETHOSTBYADDR)
5250348accfSeric 				break;
5266d8f29baSeric 			if (strcasecmp(rr.rr_dname, dname) != 0)
5276d8f29baSeric 				continue;
5280348accfSeric 			if (hostent_set_cname(h, rr.rr.ptr.ptrname, 1) == -1)
529e7643b50Seric 				hostent_add_alias(h, rr.rr.ptr.ptrname, 1);
530b44da627Seric 			break;
531b44da627Seric 
532b44da627Seric 		case T_A:
5330348accfSeric 			if (reqtype != ASR_GETHOSTBYNAME)
534b44da627Seric 				break;
5350348accfSeric 			if (family != AF_INET)
5360348accfSeric 				break;
5370348accfSeric 			if (hostent_set_cname(h, rr.rr_dname, 1) == -1)
5381a003af8Seric 				;
5390348accfSeric 			if (hostent_add_addr(h, &rr.rr.in_a.addr, 4) == -1)
5400348accfSeric 				goto fail;
541b44da627Seric 			break;
542b44da627Seric 
543b44da627Seric 		case T_AAAA:
5440348accfSeric 			if (reqtype != ASR_GETHOSTBYNAME)
545b44da627Seric 				break;
5460348accfSeric 			if (family != AF_INET6)
5470348accfSeric 				break;
5480348accfSeric 			if (hostent_set_cname(h, rr.rr_dname, 1) == -1)
5491a003af8Seric 				;
5500348accfSeric 			if (hostent_add_addr(h, &rr.rr.in_aaaa.addr6, 16) == -1)
5510348accfSeric 				goto fail;
552b44da627Seric 			break;
553b44da627Seric 		}
554b44da627Seric 	}
555b44da627Seric 
5560348accfSeric 	return (h);
5570348accfSeric fail:
558836b804aSeric 	free(h);
5590348accfSeric 	return (NULL);
560b44da627Seric }
561b44da627Seric 
562cd22283fSeric static struct hostent_ext *
563b44da627Seric hostent_alloc(int family)
564b44da627Seric {
565cd22283fSeric 	struct hostent_ext	*h;
566836b804aSeric 	size_t			alloc;
567b44da627Seric 
568cd22283fSeric 	alloc = sizeof(*h) + 1024;
569836b804aSeric 	if ((h = calloc(1, alloc)) == NULL)
570b44da627Seric 		return (NULL);
571b44da627Seric 
572cd22283fSeric 	h->h.h_addrtype = family;
573cd22283fSeric 	h->h.h_length = (family == AF_INET) ? 4 : 16;
574cd22283fSeric 	h->h.h_aliases = h->aliases;
575cd22283fSeric 	h->h.h_addr_list = h->addrs;
576cd22283fSeric 	h->pos = (char *)(h) + sizeof(*h);
577cd22283fSeric 	h->end = h->pos + 1024;
578b44da627Seric 
579b44da627Seric 	return (h);
580b44da627Seric }
581b44da627Seric 
582b44da627Seric static int
583cd22283fSeric hostent_set_cname(struct hostent_ext *h, const char *name, int isdname)
584b44da627Seric {
585b44da627Seric 	char	buf[MAXDNAME];
586cd22283fSeric 	size_t	n;
587b44da627Seric 
588cd22283fSeric 	if (h->h.h_name)
589cd22283fSeric 		return (-1);
590b44da627Seric 
591b44da627Seric 	if (isdname) {
592253ef892Sderaadt 		_asr_strdname(name, buf, sizeof buf);
593b44da627Seric 		buf[strlen(buf) - 1] = '\0';
59401b887f7Seric 		if (!res_hnok(buf))
59501b887f7Seric 			return (-1);
596836b804aSeric 		name = buf;
597b44da627Seric 	}
598836b804aSeric 
599cd22283fSeric 	n = strlen(name) + 1;
600cd22283fSeric 	if (h->pos + n >= h->end)
601cd22283fSeric 		return (-1);
602b44da627Seric 
603cd22283fSeric 	h->h.h_name = h->pos;
604cd22283fSeric 	memmove(h->pos, name, n);
605cd22283fSeric 	h->pos += n;
606b44da627Seric 	return (0);
607b44da627Seric }
608b44da627Seric 
609b44da627Seric static int
610cd22283fSeric hostent_add_alias(struct hostent_ext *h, const char *name, int isdname)
611b44da627Seric {
612b44da627Seric 	char	buf[MAXDNAME];
613cd22283fSeric 	size_t	i, n;
614b44da627Seric 
615cd22283fSeric 	for (i = 0; i < MAXALIASES; i++)
616cd22283fSeric 		if (h->aliases[i] == NULL)
617b44da627Seric 			break;
618cd22283fSeric 	if (i == MAXALIASES)
6195712b4f1Sbrynet 		return (0);
620b44da627Seric 
621b44da627Seric 	if (isdname) {
622253ef892Sderaadt 		_asr_strdname(name, buf, sizeof buf);
623b44da627Seric 		buf[strlen(buf)-1] = '\0';
62401b887f7Seric 		if (!res_hnok(buf))
62501b887f7Seric 			return (-1);
626836b804aSeric 		name = buf;
627b44da627Seric 	}
628836b804aSeric 
629cd22283fSeric 	n = strlen(name) + 1;
630cd22283fSeric 	if (h->pos + n >= h->end)
6315712b4f1Sbrynet 		return (0);
632b44da627Seric 
633cd22283fSeric 	h->aliases[i] = h->pos;
634cd22283fSeric 	memmove(h->pos, name, n);
635cd22283fSeric 	h->pos += n;
636b44da627Seric 	return (0);
637b44da627Seric }
638b44da627Seric 
639b44da627Seric static int
640cd22283fSeric hostent_add_addr(struct hostent_ext *h, const void *addr, size_t size)
641b44da627Seric {
642b44da627Seric 	int	i;
643b44da627Seric 
644cd22283fSeric 	for (i = 0; i < MAXADDRS; i++)
645cd22283fSeric 		if (h->addrs[i] == NULL)
646b44da627Seric 			break;
647cd22283fSeric 	if (i == MAXADDRS)
6485712b4f1Sbrynet 		return (0);
649b44da627Seric 
650cd22283fSeric 	if (h->pos + size >= h->end)
6515712b4f1Sbrynet 		return (0);
652836b804aSeric 
653cd22283fSeric 	h->addrs[i] = h->pos;
654cd22283fSeric 	memmove(h->pos, addr, size);
655cd22283fSeric 	h->pos += size;
656b44da627Seric 	return (0);
657b44da627Seric }
658b5afe704Sschwarze 
659b5afe704Sschwarze static void
660b5afe704Sschwarze netent_from_hostent(struct asr_result *ar)
661b5afe704Sschwarze {
662b5afe704Sschwarze 	struct in_addr		 *addr;
663b5afe704Sschwarze 	struct netent_ext	 *n;
664b5afe704Sschwarze 	struct hostent_ext	 *h;
665b5afe704Sschwarze 	char			**na, **ha;
666b5afe704Sschwarze 	size_t			  sz;
667b5afe704Sschwarze 
668b5afe704Sschwarze 	/* Allocate and initialize the output. */
669b5afe704Sschwarze 	if ((n = calloc(1, sizeof(*n) + 1024)) == NULL) {
670b5afe704Sschwarze 		ar->ar_h_errno = NETDB_INTERNAL;
671b5afe704Sschwarze 		ar->ar_errno = errno;
672b5afe704Sschwarze 		goto out;
673b5afe704Sschwarze 	}
674b5afe704Sschwarze 	n->pos = (char *)(n) + sizeof(*n);
675b5afe704Sschwarze 	n->end = n->pos + 1024;
676b5afe704Sschwarze 	n->n.n_name = n->pos;
677b5afe704Sschwarze 	n->n.n_aliases = n->aliases;
678b5afe704Sschwarze 
679b5afe704Sschwarze 	/* Copy the fixed-size data. */
680b5afe704Sschwarze 	h = (struct hostent_ext *)ar->ar_hostent;
681b5afe704Sschwarze 	addr = (struct in_addr *)h->h.h_addr;
682b5afe704Sschwarze 	n->n.n_net = ntohl(addr->s_addr);
683b5afe704Sschwarze 	n->n.n_addrtype = h->h.h_addrtype;
684b5afe704Sschwarze 
685b5afe704Sschwarze 	/* Copy the network name. */
686b5afe704Sschwarze 	sz = strlen(h->h.h_name) + 1;
687b5afe704Sschwarze 	memcpy(n->pos, h->h.h_name, sz);
688b5afe704Sschwarze 	n->pos += sz;
689b5afe704Sschwarze 
690b5afe704Sschwarze 	/*
691b5afe704Sschwarze 	 * Copy the aliases.
692b5afe704Sschwarze 	 * No overflow check is needed because we are merely copying
693b5afe704Sschwarze 	 * a part of the data from a structure of the same size.
694b5afe704Sschwarze 	 */
695b5afe704Sschwarze 	na = n->aliases;
696b5afe704Sschwarze 	for (ha = h->aliases; *ha != NULL; ha++) {
697b5afe704Sschwarze 		sz = strlen(*ha) + 1;
698b5afe704Sschwarze 		memcpy(n->pos, *ha, sz);
699b5afe704Sschwarze 		*na++ = n->pos;
700b5afe704Sschwarze 		n->pos += sz;
701b5afe704Sschwarze 	}
702b5afe704Sschwarze 	*na = NULL;
703b5afe704Sschwarze 
704b5afe704Sschwarze 	/* Handle the return values. */
705b5afe704Sschwarze 	ar->ar_netent = &n->n;
706b5afe704Sschwarze out:
707b5afe704Sschwarze 	free(ar->ar_hostent);
708b5afe704Sschwarze 	ar->ar_hostent = NULL;
709b5afe704Sschwarze }
710