xref: /openbsd-src/lib/libc/asr/gethostnamadr_async.c (revision 5be03f8ff4bfdb72cf93ab0aee9dc90ee972fe34)
1*5be03f8fSeric /*	$OpenBSD: gethostnamadr_async.c,v 1.27 2014/03/25 19:48:11 eric 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>
23262cf3f8Seric #ifdef YP
24262cf3f8Seric #include <rpc/rpc.h>
25262cf3f8Seric #include <rpcsvc/yp.h>
26262cf3f8Seric #include <rpcsvc/ypclnt.h>
27262cf3f8Seric #include "ypinternal.h"
28262cf3f8Seric #endif
29b44da627Seric 
30b32de4eaSeric #include <ctype.h>
3180f48568Seric #include <err.h>
3280f48568Seric #include <errno.h>
3301b887f7Seric #include <resolv.h> /* for res_hnok */
3480f48568Seric #include <stdlib.h>
3580f48568Seric #include <string.h>
3680f48568Seric #include <unistd.h>
3780f48568Seric 
38b44da627Seric #include "asr.h"
39b44da627Seric #include "asr_private.h"
40b44da627Seric 
41b44da627Seric #define MAXALIASES	16
42b44da627Seric #define MAXADDRS	16
43b44da627Seric 
44cd22283fSeric struct hostent_ext {
45cd22283fSeric 	struct hostent	 h;
46cd22283fSeric 	char		*aliases[MAXALIASES + 1];
47cd22283fSeric 	char		*addrs[MAXADDRS + 1];
48cd22283fSeric 	char		*end;
49cd22283fSeric 	char		*pos;
50cd22283fSeric };
51836b804aSeric 
52*5be03f8fSeric static int gethostnamadr_async_run(struct asr_query *, struct asr_result *);
53cd22283fSeric static struct hostent_ext *hostent_alloc(int);
54cd22283fSeric static int hostent_set_cname(struct hostent_ext *, const char *, int);
55cd22283fSeric static int hostent_add_alias(struct hostent_ext *, const char *, int);
56cd22283fSeric static int hostent_add_addr(struct hostent_ext *, const void *, size_t);
57b32de4eaSeric static struct hostent_ext *hostent_from_addr(int, const char *, const char *);
58cd22283fSeric static struct hostent_ext *hostent_file_match(FILE *, int, int, const char *,
59cd22283fSeric     int);
60cd22283fSeric static struct hostent_ext *hostent_from_packet(int, int, char *, size_t);
611b26c956Seric #ifdef YP
62cd22283fSeric static struct hostent_ext *_yp_gethostnamadr(int, const void *);
63cd22283fSeric static struct hostent_ext *hostent_from_yp(int, char *);
641b26c956Seric #endif
65b44da627Seric 
66*5be03f8fSeric struct asr_query *
67*5be03f8fSeric gethostbyname_async(const char *name, void *asr)
68b44da627Seric {
69b44da627Seric 	return gethostbyname2_async(name, AF_INET, asr);
70b44da627Seric }
71b44da627Seric 
72*5be03f8fSeric struct asr_query *
73*5be03f8fSeric gethostbyname2_async(const char *name, int af, void *asr)
74b44da627Seric {
75b44da627Seric 	struct asr_ctx	 *ac;
76*5be03f8fSeric 	struct asr_query *as;
77b44da627Seric 
78b44da627Seric 	/* the original segfaults */
79b44da627Seric 	if (name == NULL) {
80b44da627Seric 		errno = EINVAL;
81b44da627Seric 		return (NULL);
82b44da627Seric 	}
83b44da627Seric 
84b44da627Seric 	ac = asr_use_resolver(asr);
855bd9e5c2Seric 	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 
98b44da627Seric 	asr_ctx_unref(ac);
99b44da627Seric 	return (as);
100b44da627Seric 
101b44da627Seric     abort:
102b44da627Seric 	if (as)
1035bd9e5c2Seric 		asr_async_free(as);
104b44da627Seric 	asr_ctx_unref(ac);
105b44da627Seric 	return (NULL);
106b44da627Seric }
107b44da627Seric 
108*5be03f8fSeric struct asr_query *
109*5be03f8fSeric gethostbyaddr_async(const void *addr, socklen_t len, int af, void *asr)
110b44da627Seric {
111b44da627Seric 	struct asr_ctx	 *ac;
112*5be03f8fSeric 	struct asr_query *as;
113b44da627Seric 
114b44da627Seric 	ac = asr_use_resolver(asr);
115b44da627Seric 	as = gethostbyaddr_async_ctx(addr, len, af, ac);
116b44da627Seric 	asr_ctx_unref(ac);
117b44da627Seric 
118b44da627Seric 	return (as);
119b44da627Seric }
120b44da627Seric 
121*5be03f8fSeric struct asr_query *
122b44da627Seric gethostbyaddr_async_ctx(const void *addr, socklen_t len, int af,
123b44da627Seric     struct asr_ctx *ac)
124b44da627Seric {
125*5be03f8fSeric 	struct asr_query *as;
126b44da627Seric 
1275bd9e5c2Seric 	if ((as = asr_async_new(ac, ASR_GETHOSTBYADDR)) == NULL)
128b44da627Seric 		goto abort; /* errno set */
129b44da627Seric 	as->as_run = gethostnamadr_async_run;
130b44da627Seric 
131b44da627Seric 	as->as.hostnamadr.family = af;
132b44da627Seric 	as->as.hostnamadr.addrlen = len;
133b44da627Seric 	if (len > 0)
134b44da627Seric 		memmove(as->as.hostnamadr.addr, addr, (len > 16) ? 16 : len);
135b44da627Seric 
136b44da627Seric 	return (as);
137b44da627Seric 
138b44da627Seric     abort:
139b44da627Seric 	if (as)
14064032770Seric 		asr_async_free(as);
141b44da627Seric 	return (NULL);
142b44da627Seric }
143b44da627Seric 
144b44da627Seric static int
145*5be03f8fSeric gethostnamadr_async_run(struct asr_query *as, struct asr_result *ar)
146b44da627Seric {
147cd22283fSeric 	struct hostent_ext	*h;
148cd22283fSeric 	int			 r, type, saved_errno;
149b44da627Seric 	FILE			*f;
1506a7a3d64Seric 	char			 name[MAXDNAME], *data, addr[16], *c;
151b44da627Seric 
152b44da627Seric     next:
153b44da627Seric 	switch (as->as_state) {
154b44da627Seric 
155b44da627Seric 	case ASR_STATE_INIT:
156b44da627Seric 
157b44da627Seric 		if (as->as.hostnamadr.family != AF_INET &&
158b44da627Seric 		    as->as.hostnamadr.family != AF_INET6) {
159b44da627Seric 			ar->ar_h_errno = NETDB_INTERNAL;
160b44da627Seric 			ar->ar_errno = EAFNOSUPPORT;
161b44da627Seric 			async_set_state(as, ASR_STATE_HALT);
162b44da627Seric 			break;
163b44da627Seric 		}
164b44da627Seric 
165b44da627Seric 		if ((as->as.hostnamadr.family == AF_INET &&
166b44da627Seric 		     as->as.hostnamadr.addrlen != INADDRSZ) ||
167b44da627Seric 		    (as->as.hostnamadr.family == AF_INET6 &&
168b44da627Seric 		     as->as.hostnamadr.addrlen != IN6ADDRSZ)) {
169b44da627Seric 			ar->ar_h_errno = NETDB_INTERNAL;
170b44da627Seric 			ar->ar_errno = EINVAL;
171b44da627Seric 			async_set_state(as, ASR_STATE_HALT);
172b44da627Seric 			break;
173b44da627Seric 		}
174b44da627Seric 
175b32de4eaSeric 		/* Name might be an IP address string */
176b32de4eaSeric 		if (as->as_type == ASR_GETHOSTBYNAME) {
177b32de4eaSeric 			for (c = as->as.hostnamadr.name; *c; c++)
178dfe5467eSderaadt 				if (!isdigit((unsigned char)*c) &&
179dfe5467eSderaadt 				     *c != '.' && *c != ':')
180b32de4eaSeric 					break;
181b32de4eaSeric 			if (*c == 0 &&
182b32de4eaSeric 			    inet_pton(as->as.hostnamadr.family,
183b32de4eaSeric 			    as->as.hostnamadr.name, addr) == 1) {
184b32de4eaSeric 				h = hostent_from_addr(as->as.hostnamadr.family,
185b32de4eaSeric 				    as->as.hostnamadr.name, addr);
186b32de4eaSeric 				if (h == NULL) {
187b32de4eaSeric 					ar->ar_errno = errno;
188b32de4eaSeric 					ar->ar_h_errno = NETDB_INTERNAL;
189b32de4eaSeric 				}
190b32de4eaSeric 				else {
191b32de4eaSeric 					ar->ar_hostent = &h->h;
192b32de4eaSeric 					ar->ar_h_errno = NETDB_SUCCESS;
193b32de4eaSeric 				}
194b32de4eaSeric 				async_set_state(as, ASR_STATE_HALT);
195b44da627Seric 				break;
196b44da627Seric 			}
197b44da627Seric 		}
198b44da627Seric 		async_set_state(as, ASR_STATE_NEXT_DB);
199b44da627Seric 		break;
200b44da627Seric 
201b44da627Seric 	case ASR_STATE_NEXT_DB:
202b44da627Seric 
203b44da627Seric 		if (asr_iter_db(as) == -1) {
204b44da627Seric 			async_set_state(as, ASR_STATE_NOT_FOUND);
205b44da627Seric 			break;
206b44da627Seric 		}
207b44da627Seric 
208b44da627Seric 		switch (AS_DB(as)) {
209b44da627Seric 
210b44da627Seric 		case ASR_DB_DNS:
211b44da627Seric 
212b44da627Seric 			/* Create a subquery to do the DNS lookup */
213b44da627Seric 
214b44da627Seric 			if (as->as_type == ASR_GETHOSTBYNAME) {
215b44da627Seric 				type = (as->as.hostnamadr.family == AF_INET) ?
216b44da627Seric 				    T_A : T_AAAA;
2171e1dfc0cSeric 				as->as.hostnamadr.subq = res_search_async_ctx(
2181e1dfc0cSeric 				    as->as.hostnamadr.name,
219c5221d45Seric 				    C_IN, type, as->as_ctx);
220b44da627Seric 			} else {
2215bd9e5c2Seric 				asr_addr_as_fqdn(as->as.hostnamadr.addr,
222b44da627Seric 				    as->as.hostnamadr.family,
2236a7a3d64Seric 				    name, sizeof(name));
224b44da627Seric 				as->as.hostnamadr.subq = res_query_async_ctx(
2256a7a3d64Seric 				    name, C_IN, T_PTR, as->as_ctx);
226b44da627Seric 			}
227b44da627Seric 
228b44da627Seric 			if (as->as.hostnamadr.subq == NULL) {
229b44da627Seric 				ar->ar_errno = errno;
230b44da627Seric 				ar->ar_h_errno = NETDB_INTERNAL;
231b44da627Seric 				async_set_state(as, ASR_STATE_HALT);
232b44da627Seric 				break;
233b44da627Seric 			}
234b44da627Seric 
235b44da627Seric 			async_set_state(as, ASR_STATE_SUBQUERY);
236b44da627Seric 			break;
237b44da627Seric 
238b44da627Seric 		case ASR_DB_FILE:
239b44da627Seric 
240b44da627Seric 			/* Try to find a match in the host file */
241b44da627Seric 
242b44da627Seric 			if ((f = fopen(as->as_ctx->ac_hostfile, "r")) == NULL)
243b44da627Seric 				break;
244b44da627Seric 
2456a7a3d64Seric 			if (as->as_type == ASR_GETHOSTBYNAME) {
2466a7a3d64Seric 				data = asr_hostalias(as->as_ctx,
2476a7a3d64Seric 				    as->as.hostnamadr.name, name, sizeof(name));
2486a7a3d64Seric 				if (data == NULL)
2491e1dfc0cSeric 					data = as->as.hostnamadr.name;
2506a7a3d64Seric 			}
251b44da627Seric 			else
252b44da627Seric 				data = as->as.hostnamadr.addr;
253b44da627Seric 
254cd22283fSeric 			h = hostent_file_match(f, as->as_type,
2550348accfSeric 			    as->as.hostnamadr.family, data,
2560348accfSeric 			    as->as.hostnamadr.addrlen);
257cd22283fSeric 			saved_errno = errno;
258b44da627Seric 			fclose(f);
259cd22283fSeric 			errno = saved_errno;
2600348accfSeric 
261cd22283fSeric 			if (h == NULL) {
2620348accfSeric 				if (errno) {
263b44da627Seric 					ar->ar_errno = errno;
264b44da627Seric 					ar->ar_h_errno = NETDB_INTERNAL;
265b44da627Seric 					async_set_state(as, ASR_STATE_HALT);
2660348accfSeric 				}
2670348accfSeric 				/* otherwise not found */
268b44da627Seric 				break;
269b44da627Seric 			}
270cd22283fSeric 			ar->ar_hostent = &h->h;
271b44da627Seric 			ar->ar_h_errno = NETDB_SUCCESS;
272b44da627Seric 			async_set_state(as, ASR_STATE_HALT);
273b44da627Seric 			break;
2741b26c956Seric #ifdef YP
2751b26c956Seric 		case ASR_DB_YP:
2761b26c956Seric 			/* IPv4 only */
2771b26c956Seric 			if (as->as.hostnamadr.family != AF_INET)
2781b26c956Seric 				break;
2796a7a3d64Seric 			if (as->as_type == ASR_GETHOSTBYNAME) {
2806a7a3d64Seric 				data = asr_hostalias(as->as_ctx,
2816a7a3d64Seric 				    as->as.hostnamadr.name, name, sizeof(name));
2826a7a3d64Seric 				if (data == NULL)
2831e1dfc0cSeric 					data = as->as.hostnamadr.name;
2846a7a3d64Seric 			}
2851b26c956Seric 			else
2861b26c956Seric 				data = as->as.hostnamadr.addr;
287cd22283fSeric 			h = _yp_gethostnamadr(as->as_type, data);
288cd22283fSeric 			if (h == NULL) {
2891b26c956Seric 				if (errno) {
2901b26c956Seric 					ar->ar_errno = errno;
2911b26c956Seric 					ar->ar_h_errno = NETDB_INTERNAL;
2921b26c956Seric 					async_set_state(as, ASR_STATE_HALT);
2931b26c956Seric 				}
2941b26c956Seric 				/* otherwise not found */
2951b26c956Seric 				break;
2961b26c956Seric 			}
297cd22283fSeric 			ar->ar_hostent = &h->h;
2981b26c956Seric 			ar->ar_h_errno = NETDB_SUCCESS;
2991b26c956Seric 			async_set_state(as, ASR_STATE_HALT);
3001b26c956Seric 			break;
3011b26c956Seric #endif
302b44da627Seric 		}
303b44da627Seric 		break;
304b44da627Seric 
305b44da627Seric 	case ASR_STATE_SUBQUERY:
306b44da627Seric 
307b44da627Seric 		/* Run the DNS subquery. */
308b44da627Seric 
309*5be03f8fSeric 		if ((r = asr_run(as->as.hostnamadr.subq, ar)) == ASYNC_COND)
310b44da627Seric 			return (ASYNC_COND);
311b44da627Seric 
312b44da627Seric 		/* Done. */
313b44da627Seric 		as->as.hostnamadr.subq = NULL;
314b44da627Seric 
315b44da627Seric 		if (ar->ar_datalen == -1) {
316b44da627Seric 			async_set_state(as, ASR_STATE_NEXT_DB);
317b44da627Seric 			break;
318b44da627Seric 		}
319b44da627Seric 
320b44da627Seric 		/* If we got a packet but no anwser, use the next DB. */
321b44da627Seric 		if (ar->ar_count == 0) {
322b44da627Seric 			free(ar->ar_data);
3237ffb6b35Seric 			as->as.hostnamadr.subq_h_errno = ar->ar_h_errno;
324b44da627Seric 			async_set_state(as, ASR_STATE_NEXT_DB);
325b44da627Seric 			break;
326b44da627Seric 		}
327b44da627Seric 
328b44da627Seric 		/* Read the hostent from the packet. */
3290348accfSeric 
330cd22283fSeric 		h = hostent_from_packet(as->as_type,
3310348accfSeric 		    as->as.hostnamadr.family, ar->ar_data, ar->ar_datalen);
3320348accfSeric 		free(ar->ar_data);
333cd22283fSeric 		if (h == NULL) {
334b44da627Seric 			ar->ar_errno = errno;
335b44da627Seric 			ar->ar_h_errno = NETDB_INTERNAL;
336b44da627Seric 			async_set_state(as, ASR_STATE_HALT);
337b44da627Seric 			break;
338b44da627Seric 		}
339b44da627Seric 
340b44da627Seric 		if (as->as_type == ASR_GETHOSTBYADDR) {
341cd22283fSeric 			if (hostent_add_addr(h, as->as.hostnamadr.addr,
3420348accfSeric 			    as->as.hostnamadr.addrlen) == -1) {
343cd22283fSeric 				free(h);
3440348accfSeric 				ar->ar_errno = errno;
3450348accfSeric 				ar->ar_h_errno = NETDB_INTERNAL;
3460348accfSeric 				async_set_state(as, ASR_STATE_HALT);
3470348accfSeric 				break;
348b44da627Seric 			}
3490348accfSeric 		}
350b44da627Seric 
351b44da627Seric 		/*
352b44da627Seric 		 * No address found in the dns packet. The blocking version
353b44da627Seric 		 * reports this as an error.
354b44da627Seric 		 */
3556d8f29baSeric 		if ((as->as_type == ASR_GETHOSTBYNAME &&
3566d8f29baSeric 		     h->h.h_addr_list[0] == NULL) ||
3576d8f29baSeric 		    (as->as_type == ASR_GETHOSTBYADDR &&
3586d8f29baSeric 		     h->h.h_name == NULL)) {
359cd22283fSeric 			free(h);
360b44da627Seric 			async_set_state(as, ASR_STATE_NEXT_DB);
361b44da627Seric 			break;
362b44da627Seric 		}
363b44da627Seric 
364cd22283fSeric 		ar->ar_hostent = &h->h;
365b44da627Seric 		ar->ar_h_errno = NETDB_SUCCESS;
366b44da627Seric 		async_set_state(as, ASR_STATE_HALT);
367b44da627Seric 		break;
368b44da627Seric 
369b44da627Seric 	case ASR_STATE_NOT_FOUND:
370b44da627Seric 		ar->ar_errno = 0;
3717ffb6b35Seric 		if (as->as.hostnamadr.subq_h_errno)
3727ffb6b35Seric 			ar->ar_h_errno = as->as.hostnamadr.subq_h_errno;
3731e1dfc0cSeric 		else
374b44da627Seric 			ar->ar_h_errno = HOST_NOT_FOUND;
375b44da627Seric 		async_set_state(as, ASR_STATE_HALT);
376b44da627Seric 		break;
377b44da627Seric 
378b44da627Seric 	case ASR_STATE_HALT:
379b44da627Seric 		if (ar->ar_h_errno)
380b44da627Seric 			ar->ar_hostent = NULL;
381b44da627Seric 		else
382b44da627Seric 			ar->ar_errno = 0;
383b44da627Seric 		return (ASYNC_DONE);
384b44da627Seric 
385b44da627Seric 	default:
386b44da627Seric 		ar->ar_errno = EOPNOTSUPP;
387b44da627Seric 		ar->ar_h_errno = NETDB_INTERNAL;
388b44da627Seric 		ar->ar_gai_errno = EAI_SYSTEM;
389b44da627Seric 		async_set_state(as, ASR_STATE_HALT);
390b44da627Seric 		break;
391b44da627Seric 	}
392b44da627Seric 	goto next;
393b44da627Seric }
394b44da627Seric 
395b44da627Seric /*
396b32de4eaSeric  * Create a hostent from a numeric address string.
397b32de4eaSeric  */
398b32de4eaSeric static struct hostent_ext *
399b32de4eaSeric hostent_from_addr(int family, const char *name, const char *addr)
400b32de4eaSeric {
401b32de4eaSeric 	struct	 hostent_ext *h;
402b32de4eaSeric 
403b32de4eaSeric 	if ((h = hostent_alloc(family)) == NULL)
404b32de4eaSeric 		return (NULL);
405b32de4eaSeric 	if (hostent_set_cname(h, name, 0) == -1)
406b32de4eaSeric 		goto fail;
407b32de4eaSeric 	if (hostent_add_addr(h, addr, h->h.h_length) == -1)
408b32de4eaSeric 		goto fail;
409b32de4eaSeric 	return (h);
410b32de4eaSeric fail:
411b32de4eaSeric 	free(h);
412b32de4eaSeric 	return (NULL);
413b32de4eaSeric }
414b32de4eaSeric 
415b32de4eaSeric /*
416b44da627Seric  * Lookup the first matching entry in the hostfile, either by address or by
4170348accfSeric  * name depending on reqtype, and build a hostent from the line.
418b44da627Seric  */
419cd22283fSeric static struct hostent_ext *
42080f48568Seric hostent_file_match(FILE *f, int reqtype, int family, const char *data,
42180f48568Seric     int datalen)
422b44da627Seric {
4230348accfSeric 	char	*tokens[MAXTOKEN], addr[16];
424cd22283fSeric 	struct	 hostent_ext *h;
425b44da627Seric 	int	 n, i;
426b44da627Seric 
427b44da627Seric 	for (;;) {
4280348accfSeric 		n = asr_parse_namedb_line(f, tokens, MAXTOKEN);
4290348accfSeric 		if (n == -1) {
4300348accfSeric 			errno = 0; /* ignore errors reading the file */
4310348accfSeric 			return (NULL);
4320348accfSeric 		}
433b44da627Seric 
4340348accfSeric 		if (reqtype == ASR_GETHOSTBYNAME) {
435b44da627Seric 			for (i = 1; i < n; i++) {
436b44da627Seric 				if (strcasecmp(data, tokens[i]))
437b44da627Seric 					continue;
438b44da627Seric 				if (inet_pton(family, tokens[0], addr) == 1)
4390348accfSeric 					goto found;
440b44da627Seric 			}
4410348accfSeric 		} else {
442cd22283fSeric 			if (inet_pton(family, tokens[0], addr) == 1 &&
443cd22283fSeric 			    memcmp(addr, data, datalen) == 0)
4440348accfSeric 				goto found;
4450348accfSeric 		}
446b44da627Seric 	}
447b44da627Seric 
4480348accfSeric found:
4490348accfSeric 	if ((h = hostent_alloc(family)) == NULL)
4500348accfSeric 		return (NULL);
4510348accfSeric 	if (hostent_set_cname(h, tokens[1], 0) == -1)
4520348accfSeric 		goto fail;
4530348accfSeric 	for (i = 2; i < n; i ++)
4540348accfSeric 		if (hostent_add_alias(h, tokens[i], 0) == -1)
4550348accfSeric 			goto fail;
456cd22283fSeric 	if (hostent_add_addr(h, addr, h->h.h_length) == -1)
4570348accfSeric 		goto fail;
4580348accfSeric 	return (h);
4590348accfSeric fail:
460836b804aSeric 	free(h);
4610348accfSeric 	return (NULL);
462b44da627Seric }
463b44da627Seric 
464b44da627Seric /*
465b44da627Seric  * Fill the hostent from the given DNS packet.
466b44da627Seric  */
467cd22283fSeric static struct hostent_ext *
4680348accfSeric hostent_from_packet(int reqtype, int family, char *pkt, size_t pktlen)
469b44da627Seric {
470cd22283fSeric 	struct hostent_ext	*h;
471f90bf415Seric 	struct asr_unpack	 p;
472f90bf415Seric 	struct asr_dns_header	 hdr;
473f90bf415Seric 	struct asr_dns_query	 q;
474f90bf415Seric 	struct asr_dns_rr	 rr;
4756d8f29baSeric 	char			 dname[MAXDNAME];
4760348accfSeric 
4770348accfSeric 	if ((h = hostent_alloc(family)) == NULL)
4780348accfSeric 		return (NULL);
479b44da627Seric 
4805bd9e5c2Seric 	asr_unpack_init(&p, pkt, pktlen);
4815bd9e5c2Seric 	asr_unpack_header(&p, &hdr);
482b44da627Seric 	for (; hdr.qdcount; hdr.qdcount--)
4835bd9e5c2Seric 		asr_unpack_query(&p, &q);
4846d8f29baSeric 	strlcpy(dname, q.q_dname, sizeof(dname));
4856d8f29baSeric 
486b44da627Seric 	for (; hdr.ancount; hdr.ancount--) {
4875bd9e5c2Seric 		asr_unpack_rr(&p, &rr);
488b44da627Seric 		if (rr.rr_class != C_IN)
489b44da627Seric 			continue;
490b44da627Seric 		switch (rr.rr_type) {
491b44da627Seric 
492b44da627Seric 		case T_CNAME:
4930348accfSeric 			if (reqtype == ASR_GETHOSTBYNAME) {
4940348accfSeric 				if (hostent_add_alias(h, rr.rr_dname, 1) == -1)
4950348accfSeric 					goto fail;
4960348accfSeric 			} else {
4976d8f29baSeric 				if (strcasecmp(rr.rr_dname, dname) == 0)
4986d8f29baSeric 					strlcpy(dname, rr.rr.cname.cname,
4996d8f29baSeric 					    sizeof(dname));
5000348accfSeric 			}
501b44da627Seric 			break;
502b44da627Seric 
503b44da627Seric 		case T_PTR:
5040348accfSeric 			if (reqtype != ASR_GETHOSTBYADDR)
5050348accfSeric 				break;
5066d8f29baSeric 			if (strcasecmp(rr.rr_dname, dname) != 0)
5076d8f29baSeric 				continue;
5080348accfSeric 			if (hostent_set_cname(h, rr.rr.ptr.ptrname, 1) == -1)
509e7643b50Seric 				hostent_add_alias(h, rr.rr.ptr.ptrname, 1);
510b44da627Seric 			break;
511b44da627Seric 
512b44da627Seric 		case T_A:
5130348accfSeric 			if (reqtype != ASR_GETHOSTBYNAME)
514b44da627Seric 				break;
5150348accfSeric 			if (family != AF_INET)
5160348accfSeric 				break;
5170348accfSeric 			if (hostent_set_cname(h, rr.rr_dname, 1) == -1)
5181a003af8Seric 				;
5190348accfSeric 			if (hostent_add_addr(h, &rr.rr.in_a.addr, 4) == -1)
5200348accfSeric 				goto fail;
521b44da627Seric 			break;
522b44da627Seric 
523b44da627Seric 		case T_AAAA:
5240348accfSeric 			if (reqtype != ASR_GETHOSTBYNAME)
525b44da627Seric 				break;
5260348accfSeric 			if (family != AF_INET6)
5270348accfSeric 				break;
5280348accfSeric 			if (hostent_set_cname(h, rr.rr_dname, 1) == -1)
5291a003af8Seric 				;
5300348accfSeric 			if (hostent_add_addr(h, &rr.rr.in_aaaa.addr6, 16) == -1)
5310348accfSeric 				goto fail;
532b44da627Seric 			break;
533b44da627Seric 		}
534b44da627Seric 	}
535b44da627Seric 
5360348accfSeric 	return (h);
5370348accfSeric fail:
538836b804aSeric 	free(h);
5390348accfSeric 	return (NULL);
540b44da627Seric }
541b44da627Seric 
542cd22283fSeric static struct hostent_ext *
543b44da627Seric hostent_alloc(int family)
544b44da627Seric {
545cd22283fSeric 	struct hostent_ext	*h;
546836b804aSeric 	size_t			alloc;
547b44da627Seric 
548cd22283fSeric 	alloc = sizeof(*h) + 1024;
549836b804aSeric 	if ((h = calloc(1, alloc)) == NULL)
550b44da627Seric 		return (NULL);
551b44da627Seric 
552cd22283fSeric 	h->h.h_addrtype = family;
553cd22283fSeric 	h->h.h_length = (family == AF_INET) ? 4 : 16;
554cd22283fSeric 	h->h.h_aliases = h->aliases;
555cd22283fSeric 	h->h.h_addr_list = h->addrs;
556cd22283fSeric 	h->pos = (char *)(h) + sizeof(*h);
557cd22283fSeric 	h->end = h->pos + 1024;
558b44da627Seric 
559b44da627Seric 	return (h);
560b44da627Seric }
561b44da627Seric 
562b44da627Seric static int
563cd22283fSeric hostent_set_cname(struct hostent_ext *h, const char *name, int isdname)
564b44da627Seric {
565b44da627Seric 	char	buf[MAXDNAME];
566cd22283fSeric 	size_t	n;
567b44da627Seric 
568cd22283fSeric 	if (h->h.h_name)
569cd22283fSeric 		return (-1);
570b44da627Seric 
571b44da627Seric 	if (isdname) {
572b44da627Seric 		asr_strdname(name, buf, sizeof buf);
573b44da627Seric 		buf[strlen(buf) - 1] = '\0';
57401b887f7Seric 		if (!res_hnok(buf))
57501b887f7Seric 			return (-1);
576836b804aSeric 		name = buf;
577b44da627Seric 	}
578836b804aSeric 
579cd22283fSeric 	n = strlen(name) + 1;
580cd22283fSeric 	if (h->pos + n >= h->end)
581cd22283fSeric 		return (-1);
582b44da627Seric 
583cd22283fSeric 	h->h.h_name = h->pos;
584cd22283fSeric 	memmove(h->pos, name, n);
585cd22283fSeric 	h->pos += n;
586b44da627Seric 	return (0);
587b44da627Seric }
588b44da627Seric 
589b44da627Seric static int
590cd22283fSeric hostent_add_alias(struct hostent_ext *h, const char *name, int isdname)
591b44da627Seric {
592b44da627Seric 	char	buf[MAXDNAME];
593cd22283fSeric 	size_t	i, n;
594b44da627Seric 
595cd22283fSeric 	for (i = 0; i < MAXALIASES; i++)
596cd22283fSeric 		if (h->aliases[i] == NULL)
597b44da627Seric 			break;
598cd22283fSeric 	if (i == MAXALIASES)
599cd22283fSeric 		return (-1);
600b44da627Seric 
601b44da627Seric 	if (isdname) {
602b44da627Seric 		asr_strdname(name, buf, sizeof buf);
603b44da627Seric 		buf[strlen(buf)-1] = '\0';
60401b887f7Seric 		if (!res_hnok(buf))
60501b887f7Seric 			return (-1);
606836b804aSeric 		name = buf;
607b44da627Seric 	}
608836b804aSeric 
609cd22283fSeric 	n = strlen(name) + 1;
610cd22283fSeric 	if (h->pos + n >= h->end)
611cd22283fSeric 		return (-1);
612b44da627Seric 
613cd22283fSeric 	h->aliases[i] = h->pos;
614cd22283fSeric 	memmove(h->pos, name, n);
615cd22283fSeric 	h->pos += n;
616b44da627Seric 	return (0);
617b44da627Seric }
618b44da627Seric 
619b44da627Seric static int
620cd22283fSeric hostent_add_addr(struct hostent_ext *h, const void *addr, size_t size)
621b44da627Seric {
622b44da627Seric 	int	i;
623b44da627Seric 
624cd22283fSeric 	for (i = 0; i < MAXADDRS; i++)
625cd22283fSeric 		if (h->addrs[i] == NULL)
626b44da627Seric 			break;
627cd22283fSeric 	if (i == MAXADDRS)
628cd22283fSeric 		return (-1);
629b44da627Seric 
630cd22283fSeric 	if (h->pos + size >= h->end)
631cd22283fSeric 		return (-1);
632836b804aSeric 
633cd22283fSeric 	h->addrs[i] = h->pos;
634cd22283fSeric 	memmove(h->pos, addr, size);
635cd22283fSeric 	h->pos += size;
636b44da627Seric 	return (0);
637b44da627Seric }
638b44da627Seric 
6391b26c956Seric #ifdef YP
640cd22283fSeric static struct hostent_ext *
6411b26c956Seric _yp_gethostnamadr(int type, const void *data)
6421b26c956Seric {
6431b26c956Seric 	static char		*domain = NULL;
644cd22283fSeric 	struct hostent_ext	*h = NULL;
6451b26c956Seric 	const char		*name;
6461b26c956Seric 	char			 buf[MAXHOSTNAMELEN];
6471b26c956Seric 	char			*res = NULL;
6481b26c956Seric 	int			 r, len;
6491b26c956Seric 
6501b26c956Seric 	if (!domain && _yp_check(&domain) == 0) {
6511b26c956Seric 		errno = 0; /* ignore yp_bind errors */
6521b26c956Seric 		return (NULL);
6531b26c956Seric 	}
6541b26c956Seric 
6551b26c956Seric 	if (type == ASR_GETHOSTBYNAME) {
6561b26c956Seric 		name = data;
6571b26c956Seric 		len = strlen(name);
6581b26c956Seric 		r = yp_match(domain, "hosts.byname", name, len, &res, &len);
6591b26c956Seric 	}
6601b26c956Seric 	else {
6611b26c956Seric 		if (inet_ntop(AF_INET, data, buf, sizeof buf) == NULL)
6621b26c956Seric 			return (NULL);
6631b26c956Seric 		len = strlen(buf);
6641b26c956Seric 		r = yp_match(domain, "hosts.byaddr", buf, len, &res, &len);
6651b26c956Seric 	}
6661b26c956Seric 	if (r == 0) {
6671b26c956Seric 		h = hostent_from_yp(AF_INET, res);
6681b26c956Seric 	} else {
6691b26c956Seric 		errno = 0; /* ignore error if not found */
6701b26c956Seric 	}
6711b26c956Seric 	if (res)
6721b26c956Seric 		free(res);
6731b26c956Seric 	return (h);
6741b26c956Seric }
6751b26c956Seric 
6761b26c956Seric static int
6771b26c956Seric strsplit(char *line, char **tokens, int ntokens)
6781b26c956Seric {
6791b26c956Seric 	int	ntok;
6801b26c956Seric 	char	*cp, **tp;
6811b26c956Seric 
6821b26c956Seric 	for (cp = line, tp = tokens, ntok = 0;
6831b26c956Seric 	    ntok < ntokens && (*tp = strsep(&cp, " \t")) != NULL; )
6841b26c956Seric 		if (**tp != '\0') {
6851b26c956Seric 			tp++;
6861b26c956Seric 			ntok++;
6871b26c956Seric 		}
6881b26c956Seric 
6891b26c956Seric 	return (ntok);
6901b26c956Seric }
6911b26c956Seric 
692cd22283fSeric static struct hostent_ext *
6931b26c956Seric hostent_from_yp(int family, char *line)
6941b26c956Seric {
695cd22283fSeric 	struct hostent_ext	*h;
6961b26c956Seric 	char			*next, *tokens[10], addr[IN6ADDRSZ];
6971b26c956Seric 	int			 i, ntok;
6981b26c956Seric 
6991b26c956Seric 	if ((h = hostent_alloc(family)) == NULL)
7001b26c956Seric 		return (NULL);
7011b26c956Seric 
7021b26c956Seric 	for (next = line; line; line = next) {
7031b26c956Seric 		if ((next = strchr(line, '\n'))) {
7041b26c956Seric 			*next = '\0';
7051b26c956Seric 			next += 1;
7061b26c956Seric 		}
7071b26c956Seric 		ntok = strsplit(line, tokens, 10);
7081b26c956Seric 		if (ntok < 2)
7091b26c956Seric 			continue;
7101b26c956Seric 		if (inet_pton(family, tokens[0], addr) == 1)
7111b26c956Seric 			hostent_add_addr(h, addr, family == AF_INET ?
7121b26c956Seric 			    INADDRSZ : IN6ADDRSZ);
7131b26c956Seric 		i = 2;
714cd22283fSeric 		if (h->h.h_name == NULL)
7151b26c956Seric 			hostent_set_cname(h, tokens[1], 0);
716cd22283fSeric 		else if (strcmp(h->h.h_name, tokens[1]))
7171b26c956Seric 			i = 1;
7181b26c956Seric 		for (; i < ntok; i++)
7191b26c956Seric 			hostent_add_alias(h, tokens[i], 0);
7201b26c956Seric 	}
7211b26c956Seric 
722cd22283fSeric 	if (h->h.h_name == NULL) {
7231b26c956Seric 		free(h);
7241b26c956Seric 		return (NULL);
7251b26c956Seric 	}
7261b26c956Seric 
7271b26c956Seric 	return (h);
7281b26c956Seric }
7291b26c956Seric #endif
730