xref: /openbsd-src/lib/libc/asr/gethostnamadr_async.c (revision 0b7734b3d77bb9b21afec6f4621cae6c805dbd45)
1 /*	$OpenBSD: gethostnamadr_async.c,v 1.42 2015/12/16 16:32:30 deraadt Exp $	*/
2 /*
3  * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/types.h>
19 #include <sys/socket.h>
20 #include <netinet/in.h>
21 #include <arpa/inet.h>
22 #include <arpa/nameser.h>
23 #include <netdb.h>
24 
25 #include <asr.h>
26 #include <ctype.h>
27 #include <errno.h>
28 #include <resolv.h> /* for res_hnok */
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <limits.h>
33 
34 #include "asr_private.h"
35 
36 #define MAXALIASES	35
37 #define MAXADDRS	35
38 
39 struct hostent_ext {
40 	struct hostent	 h;
41 	char		*aliases[MAXALIASES + 1];
42 	char		*addrs[MAXADDRS + 1];
43 	char		*end;
44 	char		*pos;
45 };
46 
47 static int gethostnamadr_async_run(struct asr_query *, struct asr_result *);
48 static struct hostent_ext *hostent_alloc(int);
49 static int hostent_set_cname(struct hostent_ext *, const char *, int);
50 static int hostent_add_alias(struct hostent_ext *, const char *, int);
51 static int hostent_add_addr(struct hostent_ext *, const void *, size_t);
52 static struct hostent_ext *hostent_from_addr(int, const char *, const char *);
53 static struct hostent_ext *hostent_file_match(FILE *, int, int, const char *,
54     int);
55 static struct hostent_ext *hostent_from_packet(int, int, char *, size_t);
56 
57 struct asr_query *
58 gethostbyname_async(const char *name, void *asr)
59 {
60 	return gethostbyname2_async(name, AF_INET, asr);
61 }
62 DEF_WEAK(gethostbyname_async);
63 
64 struct asr_query *
65 gethostbyname2_async(const char *name, int af, void *asr)
66 {
67 	struct asr_ctx	 *ac;
68 	struct asr_query *as;
69 
70 	/* the original segfaults */
71 	if (name == NULL) {
72 		errno = EINVAL;
73 		return (NULL);
74 	}
75 
76 	ac = _asr_use_resolver(asr);
77 	if ((as = _asr_async_new(ac, ASR_GETHOSTBYNAME)) == NULL)
78 		goto abort; /* errno set */
79 	as->as_run = gethostnamadr_async_run;
80 
81 	as->as.hostnamadr.family = af;
82 	if (af == AF_INET)
83 		as->as.hostnamadr.addrlen = INADDRSZ;
84 	else if (af == AF_INET6)
85 		as->as.hostnamadr.addrlen = IN6ADDRSZ;
86 	as->as.hostnamadr.name = strdup(name);
87 	if (as->as.hostnamadr.name == NULL)
88 		goto abort; /* errno set */
89 
90 	_asr_ctx_unref(ac);
91 	return (as);
92 
93     abort:
94 	if (as)
95 		_asr_async_free(as);
96 	_asr_ctx_unref(ac);
97 	return (NULL);
98 }
99 DEF_WEAK(gethostbyname2_async);
100 
101 struct asr_query *
102 gethostbyaddr_async(const void *addr, socklen_t len, int af, void *asr)
103 {
104 	struct asr_ctx	 *ac;
105 	struct asr_query *as;
106 
107 	ac = _asr_use_resolver(asr);
108 	as = _gethostbyaddr_async_ctx(addr, len, af, ac);
109 	_asr_ctx_unref(ac);
110 
111 	return (as);
112 }
113 DEF_WEAK(gethostbyaddr_async);
114 
115 struct asr_query *
116 _gethostbyaddr_async_ctx(const void *addr, socklen_t len, int af,
117     struct asr_ctx *ac)
118 {
119 	struct asr_query *as;
120 
121 	if ((as = _asr_async_new(ac, ASR_GETHOSTBYADDR)) == NULL)
122 		goto abort; /* errno set */
123 	as->as_run = gethostnamadr_async_run;
124 
125 	as->as.hostnamadr.family = af;
126 	as->as.hostnamadr.addrlen = len;
127 	if (len > 0)
128 		memmove(as->as.hostnamadr.addr, addr, (len > 16) ? 16 : len);
129 
130 	return (as);
131 
132     abort:
133 	if (as)
134 		_asr_async_free(as);
135 	return (NULL);
136 }
137 
138 static int
139 gethostnamadr_async_run(struct asr_query *as, struct asr_result *ar)
140 {
141 	struct hostent_ext	*h;
142 	int			 r, type, saved_errno;
143 	FILE			*f;
144 	char			 name[MAXDNAME], *data, addr[16], *c;
145 
146     next:
147 	switch (as->as_state) {
148 
149 	case ASR_STATE_INIT:
150 
151 		if (as->as.hostnamadr.family != AF_INET &&
152 		    as->as.hostnamadr.family != AF_INET6) {
153 			ar->ar_h_errno = NETDB_INTERNAL;
154 			ar->ar_errno = EAFNOSUPPORT;
155 			async_set_state(as, ASR_STATE_HALT);
156 			break;
157 		}
158 
159 		if ((as->as.hostnamadr.family == AF_INET &&
160 		     as->as.hostnamadr.addrlen != INADDRSZ) ||
161 		    (as->as.hostnamadr.family == AF_INET6 &&
162 		     as->as.hostnamadr.addrlen != IN6ADDRSZ)) {
163 			ar->ar_h_errno = NETDB_INTERNAL;
164 			ar->ar_errno = EINVAL;
165 			async_set_state(as, ASR_STATE_HALT);
166 			break;
167 		}
168 
169 		if (as->as_type == ASR_GETHOSTBYNAME) {
170 
171 			if (as->as.hostnamadr.name[0] == '\0') {
172 				ar->ar_h_errno = NO_DATA;
173 				async_set_state(as, ASR_STATE_HALT);
174 				break;
175 			}
176 
177 			/* Name might be an IP address string */
178 			for (c = as->as.hostnamadr.name; *c; c++)
179 				if (!isdigit((unsigned char)*c) &&
180 				     *c != '.' && *c != ':')
181 					break;
182 			if (*c == 0 &&
183 			    inet_pton(as->as.hostnamadr.family,
184 			    as->as.hostnamadr.name, addr) == 1) {
185 				h = hostent_from_addr(as->as.hostnamadr.family,
186 				    as->as.hostnamadr.name, addr);
187 				if (h == NULL) {
188 					ar->ar_errno = errno;
189 					ar->ar_h_errno = NETDB_INTERNAL;
190 				}
191 				else {
192 					ar->ar_hostent = &h->h;
193 					ar->ar_h_errno = NETDB_SUCCESS;
194 				}
195 				async_set_state(as, ASR_STATE_HALT);
196 				break;
197 			}
198 		}
199 		async_set_state(as, ASR_STATE_NEXT_DB);
200 		break;
201 
202 	case ASR_STATE_NEXT_DB:
203 
204 		if (_asr_iter_db(as) == -1) {
205 			async_set_state(as, ASR_STATE_NOT_FOUND);
206 			break;
207 		}
208 
209 		switch (AS_DB(as)) {
210 
211 		case ASR_DB_DNS:
212 
213 			/* Create a subquery to do the DNS lookup */
214 
215 			if (as->as_type == ASR_GETHOSTBYNAME) {
216 				type = (as->as.hostnamadr.family == AF_INET) ?
217 				    T_A : T_AAAA;
218 				as->as.hostnamadr.subq = _res_search_async_ctx(
219 				    as->as.hostnamadr.name,
220 				    C_IN, type, as->as_ctx);
221 			} else {
222 				_asr_addr_as_fqdn(as->as.hostnamadr.addr,
223 				    as->as.hostnamadr.family,
224 				    name, sizeof(name));
225 				as->as.hostnamadr.subq = _res_query_async_ctx(
226 				    name, C_IN, T_PTR, as->as_ctx);
227 			}
228 
229 			if (as->as.hostnamadr.subq == NULL) {
230 				ar->ar_errno = errno;
231 				ar->ar_h_errno = NETDB_INTERNAL;
232 				async_set_state(as, ASR_STATE_HALT);
233 				break;
234 			}
235 
236 			async_set_state(as, ASR_STATE_SUBQUERY);
237 			break;
238 
239 		case ASR_DB_FILE:
240 
241 			/* Try to find a match in the host file */
242 
243 			if ((f = fopen(_PATH_HOSTS, "re")) == NULL)
244 				break;
245 
246 			if (as->as_type == ASR_GETHOSTBYNAME)
247 				data = as->as.hostnamadr.name;
248 			else
249 				data = as->as.hostnamadr.addr;
250 
251 			h = hostent_file_match(f, as->as_type,
252 			    as->as.hostnamadr.family, data,
253 			    as->as.hostnamadr.addrlen);
254 			saved_errno = errno;
255 			fclose(f);
256 			errno = saved_errno;
257 
258 			if (h == NULL) {
259 				if (errno) {
260 					ar->ar_errno = errno;
261 					ar->ar_h_errno = NETDB_INTERNAL;
262 					async_set_state(as, ASR_STATE_HALT);
263 				}
264 				/* otherwise not found */
265 				break;
266 			}
267 			ar->ar_hostent = &h->h;
268 			ar->ar_h_errno = NETDB_SUCCESS;
269 			async_set_state(as, ASR_STATE_HALT);
270 			break;
271 		}
272 		break;
273 
274 	case ASR_STATE_SUBQUERY:
275 
276 		/* Run the DNS subquery. */
277 
278 		if ((r = asr_run(as->as.hostnamadr.subq, ar)) == ASYNC_COND)
279 			return (ASYNC_COND);
280 
281 		/* Done. */
282 		as->as.hostnamadr.subq = NULL;
283 
284 		if (ar->ar_datalen == -1) {
285 			async_set_state(as, ASR_STATE_NEXT_DB);
286 			break;
287 		}
288 
289 		/* If we got a packet but no anwser, use the next DB. */
290 		if (ar->ar_count == 0) {
291 			free(ar->ar_data);
292 			as->as.hostnamadr.subq_h_errno = ar->ar_h_errno;
293 			async_set_state(as, ASR_STATE_NEXT_DB);
294 			break;
295 		}
296 
297 		/* Read the hostent from the packet. */
298 
299 		h = hostent_from_packet(as->as_type,
300 		    as->as.hostnamadr.family, ar->ar_data, ar->ar_datalen);
301 		free(ar->ar_data);
302 		if (h == NULL) {
303 			ar->ar_errno = errno;
304 			ar->ar_h_errno = NETDB_INTERNAL;
305 			async_set_state(as, ASR_STATE_HALT);
306 			break;
307 		}
308 
309 		if (as->as_type == ASR_GETHOSTBYADDR) {
310 			if (hostent_add_addr(h, as->as.hostnamadr.addr,
311 			    as->as.hostnamadr.addrlen) == -1) {
312 				free(h);
313 				ar->ar_errno = errno;
314 				ar->ar_h_errno = NETDB_INTERNAL;
315 				async_set_state(as, ASR_STATE_HALT);
316 				break;
317 			}
318 		}
319 
320 		/*
321 		 * No valid hostname or address found in the dns packet.
322 		 * Ignore it.
323 		 */
324 		if ((as->as_type == ASR_GETHOSTBYNAME &&
325 		     h->h.h_addr_list[0] == NULL) ||
326 		    h->h.h_name == NULL) {
327 			free(h);
328 			async_set_state(as, ASR_STATE_NEXT_DB);
329 			break;
330 		}
331 
332 		ar->ar_hostent = &h->h;
333 		ar->ar_h_errno = NETDB_SUCCESS;
334 		async_set_state(as, ASR_STATE_HALT);
335 		break;
336 
337 	case ASR_STATE_NOT_FOUND:
338 		ar->ar_errno = 0;
339 		if (as->as.hostnamadr.subq_h_errno)
340 			ar->ar_h_errno = as->as.hostnamadr.subq_h_errno;
341 		else
342 			ar->ar_h_errno = HOST_NOT_FOUND;
343 		async_set_state(as, ASR_STATE_HALT);
344 		break;
345 
346 	case ASR_STATE_HALT:
347 		if (ar->ar_h_errno)
348 			ar->ar_hostent = NULL;
349 		else
350 			ar->ar_errno = 0;
351 		return (ASYNC_DONE);
352 
353 	default:
354 		ar->ar_errno = EOPNOTSUPP;
355 		ar->ar_h_errno = NETDB_INTERNAL;
356 		ar->ar_gai_errno = EAI_SYSTEM;
357 		async_set_state(as, ASR_STATE_HALT);
358 		break;
359 	}
360 	goto next;
361 }
362 
363 /*
364  * Create a hostent from a numeric address string.
365  */
366 static struct hostent_ext *
367 hostent_from_addr(int family, const char *name, const char *addr)
368 {
369 	struct	 hostent_ext *h;
370 
371 	if ((h = hostent_alloc(family)) == NULL)
372 		return (NULL);
373 	if (hostent_set_cname(h, name, 0) == -1)
374 		goto fail;
375 	if (hostent_add_addr(h, addr, h->h.h_length) == -1)
376 		goto fail;
377 	return (h);
378 fail:
379 	free(h);
380 	return (NULL);
381 }
382 
383 /*
384  * Lookup the first matching entry in the hostfile, either by address or by
385  * name depending on reqtype, and build a hostent from the line.
386  */
387 static struct hostent_ext *
388 hostent_file_match(FILE *f, int reqtype, int family, const char *data,
389     int datalen)
390 {
391 	char	*tokens[MAXTOKEN], addr[16], buf[BUFSIZ + 1];
392 	struct	 hostent_ext *h;
393 	int	 n, i;
394 
395 	for (;;) {
396 		n = _asr_parse_namedb_line(f, tokens, MAXTOKEN, buf, sizeof(buf));
397 		if (n == -1) {
398 			errno = 0; /* ignore errors reading the file */
399 			return (NULL);
400 		}
401 
402 		/* there must be an address and at least one name */
403 		if (n < 2)
404 			continue;
405 
406 		if (reqtype == ASR_GETHOSTBYNAME) {
407 			for (i = 1; i < n; i++) {
408 				if (strcasecmp(data, tokens[i]))
409 					continue;
410 				if (inet_pton(family, tokens[0], addr) == 1)
411 					goto found;
412 			}
413 		} else {
414 			if (inet_pton(family, tokens[0], addr) == 1 &&
415 			    memcmp(addr, data, datalen) == 0)
416 				goto found;
417 		}
418 	}
419 
420 found:
421 	if ((h = hostent_alloc(family)) == NULL)
422 		return (NULL);
423 	if (hostent_set_cname(h, tokens[1], 0) == -1)
424 		goto fail;
425 	for (i = 2; i < n; i ++)
426 		if (hostent_add_alias(h, tokens[i], 0) == -1)
427 			goto fail;
428 	if (hostent_add_addr(h, addr, h->h.h_length) == -1)
429 		goto fail;
430 	return (h);
431 fail:
432 	free(h);
433 	return (NULL);
434 }
435 
436 /*
437  * Fill the hostent from the given DNS packet.
438  */
439 static struct hostent_ext *
440 hostent_from_packet(int reqtype, int family, char *pkt, size_t pktlen)
441 {
442 	struct hostent_ext	*h;
443 	struct asr_unpack	 p;
444 	struct asr_dns_header	 hdr;
445 	struct asr_dns_query	 q;
446 	struct asr_dns_rr	 rr;
447 	char			 dname[MAXDNAME];
448 
449 	if ((h = hostent_alloc(family)) == NULL)
450 		return (NULL);
451 
452 	_asr_unpack_init(&p, pkt, pktlen);
453 	_asr_unpack_header(&p, &hdr);
454 	for (; hdr.qdcount; hdr.qdcount--)
455 		_asr_unpack_query(&p, &q);
456 	strlcpy(dname, q.q_dname, sizeof(dname));
457 
458 	for (; hdr.ancount; hdr.ancount--) {
459 		_asr_unpack_rr(&p, &rr);
460 		if (rr.rr_class != C_IN)
461 			continue;
462 		switch (rr.rr_type) {
463 
464 		case T_CNAME:
465 			if (reqtype == ASR_GETHOSTBYNAME) {
466 				if (hostent_add_alias(h, rr.rr_dname, 1) == -1)
467 					goto fail;
468 			} else {
469 				if (strcasecmp(rr.rr_dname, dname) == 0)
470 					strlcpy(dname, rr.rr.cname.cname,
471 					    sizeof(dname));
472 			}
473 			break;
474 
475 		case T_PTR:
476 			if (reqtype != ASR_GETHOSTBYADDR)
477 				break;
478 			if (strcasecmp(rr.rr_dname, dname) != 0)
479 				continue;
480 			if (hostent_set_cname(h, rr.rr.ptr.ptrname, 1) == -1)
481 				hostent_add_alias(h, rr.rr.ptr.ptrname, 1);
482 			break;
483 
484 		case T_A:
485 			if (reqtype != ASR_GETHOSTBYNAME)
486 				break;
487 			if (family != AF_INET)
488 				break;
489 			if (hostent_set_cname(h, rr.rr_dname, 1) == -1)
490 				;
491 			if (hostent_add_addr(h, &rr.rr.in_a.addr, 4) == -1)
492 				goto fail;
493 			break;
494 
495 		case T_AAAA:
496 			if (reqtype != ASR_GETHOSTBYNAME)
497 				break;
498 			if (family != AF_INET6)
499 				break;
500 			if (hostent_set_cname(h, rr.rr_dname, 1) == -1)
501 				;
502 			if (hostent_add_addr(h, &rr.rr.in_aaaa.addr6, 16) == -1)
503 				goto fail;
504 			break;
505 		}
506 	}
507 
508 	return (h);
509 fail:
510 	free(h);
511 	return (NULL);
512 }
513 
514 static struct hostent_ext *
515 hostent_alloc(int family)
516 {
517 	struct hostent_ext	*h;
518 	size_t			alloc;
519 
520 	alloc = sizeof(*h) + 1024;
521 	if ((h = calloc(1, alloc)) == NULL)
522 		return (NULL);
523 
524 	h->h.h_addrtype = family;
525 	h->h.h_length = (family == AF_INET) ? 4 : 16;
526 	h->h.h_aliases = h->aliases;
527 	h->h.h_addr_list = h->addrs;
528 	h->pos = (char *)(h) + sizeof(*h);
529 	h->end = h->pos + 1024;
530 
531 	return (h);
532 }
533 
534 static int
535 hostent_set_cname(struct hostent_ext *h, const char *name, int isdname)
536 {
537 	char	buf[MAXDNAME];
538 	size_t	n;
539 
540 	if (h->h.h_name)
541 		return (-1);
542 
543 	if (isdname) {
544 		_asr_strdname(name, buf, sizeof buf);
545 		buf[strlen(buf) - 1] = '\0';
546 		if (!res_hnok(buf))
547 			return (-1);
548 		name = buf;
549 	}
550 
551 	n = strlen(name) + 1;
552 	if (h->pos + n >= h->end)
553 		return (-1);
554 
555 	h->h.h_name = h->pos;
556 	memmove(h->pos, name, n);
557 	h->pos += n;
558 	return (0);
559 }
560 
561 static int
562 hostent_add_alias(struct hostent_ext *h, const char *name, int isdname)
563 {
564 	char	buf[MAXDNAME];
565 	size_t	i, n;
566 
567 	for (i = 0; i < MAXALIASES; i++)
568 		if (h->aliases[i] == NULL)
569 			break;
570 	if (i == MAXALIASES)
571 		return (0);
572 
573 	if (isdname) {
574 		_asr_strdname(name, buf, sizeof buf);
575 		buf[strlen(buf)-1] = '\0';
576 		if (!res_hnok(buf))
577 			return (-1);
578 		name = buf;
579 	}
580 
581 	n = strlen(name) + 1;
582 	if (h->pos + n >= h->end)
583 		return (0);
584 
585 	h->aliases[i] = h->pos;
586 	memmove(h->pos, name, n);
587 	h->pos += n;
588 	return (0);
589 }
590 
591 static int
592 hostent_add_addr(struct hostent_ext *h, const void *addr, size_t size)
593 {
594 	int	i;
595 
596 	for (i = 0; i < MAXADDRS; i++)
597 		if (h->addrs[i] == NULL)
598 			break;
599 	if (i == MAXADDRS)
600 		return (0);
601 
602 	if (h->pos + size >= h->end)
603 		return (0);
604 
605 	h->addrs[i] = h->pos;
606 	memmove(h->pos, addr, size);
607 	h->pos += size;
608 	return (0);
609 }
610