1/*	$OpenBSD: gethostnamadr_async.c,v 1.49 2023/11/22 13:19:31 florian 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
39struct hostent_ext {
40	struct hostent	 h;
41	char		*aliases[MAXALIASES + 1];
42	char		*addrs[MAXADDRS + 1];
43	char		*end;
44	char		*pos;
45};
46
47struct netent_ext {
48	struct netent	 n;
49	char		*aliases[MAXALIASES + 1];
50	char		*end;
51	char		*pos;
52};
53
54static int gethostnamadr_async_run(struct asr_query *, struct asr_result *);
55static struct hostent_ext *hostent_alloc(int);
56static int hostent_set_cname(struct hostent_ext *, const char *, int);
57static int hostent_add_alias(struct hostent_ext *, const char *, int);
58static int hostent_add_addr(struct hostent_ext *, const void *, size_t);
59static struct hostent_ext *hostent_from_addr(int, const char *, const char *);
60static struct hostent_ext *hostent_file_match(FILE *, int, int, const char *,
61    int);
62static struct hostent_ext *hostent_from_packet(int, int, char *, size_t);
63static void netent_from_hostent(struct asr_result *ar);
64
65struct asr_query *
66gethostbyname_async(const char *name, void *asr)
67{
68	return gethostbyname2_async(name, AF_INET, asr);
69}
70DEF_WEAK(gethostbyname_async);
71
72struct asr_query *
73gethostbyname2_async(const char *name, int af, void *asr)
74{
75	struct asr_ctx	 *ac;
76	struct asr_query *as;
77
78	/* the original segfaults */
79	if (name == NULL) {
80		errno = EINVAL;
81		return (NULL);
82	}
83
84	ac = _asr_use_resolver(asr);
85	if ((as = _asr_async_new(ac, ASR_GETHOSTBYNAME)) == NULL)
86		goto abort; /* errno set */
87	as->as_run = gethostnamadr_async_run;
88
89	as->as.hostnamadr.family = af;
90	if (af == AF_INET)
91		as->as.hostnamadr.addrlen = INADDRSZ;
92	else if (af == AF_INET6)
93		as->as.hostnamadr.addrlen = IN6ADDRSZ;
94	as->as.hostnamadr.name = strdup(name);
95	if (as->as.hostnamadr.name == NULL)
96		goto abort; /* errno set */
97
98	_asr_ctx_unref(ac);
99	return (as);
100
101    abort:
102	if (as)
103		_asr_async_free(as);
104	_asr_ctx_unref(ac);
105	return (NULL);
106}
107DEF_WEAK(gethostbyname2_async);
108
109struct asr_query *
110gethostbyaddr_async(const void *addr, socklen_t len, int af, void *asr)
111{
112	struct asr_ctx	 *ac;
113	struct asr_query *as;
114
115	ac = _asr_use_resolver(asr);
116	as = _gethostbyaddr_async_ctx(addr, len, af, ac);
117	_asr_ctx_unref(ac);
118
119	return (as);
120}
121DEF_WEAK(gethostbyaddr_async);
122
123struct asr_query *
124_gethostbyaddr_async_ctx(const void *addr, socklen_t len, int af,
125    struct asr_ctx *ac)
126{
127	struct asr_query *as;
128
129	if ((as = _asr_async_new(ac, ASR_GETHOSTBYADDR)) == NULL)
130		goto abort; /* errno set */
131	as->as_run = gethostnamadr_async_run;
132
133	as->as.hostnamadr.family = af;
134	as->as.hostnamadr.addrlen = len;
135	if (len > 0)
136		memmove(as->as.hostnamadr.addr, addr, (len > 16) ? 16 : len);
137
138	return (as);
139
140    abort:
141	if (as)
142		_asr_async_free(as);
143	return (NULL);
144}
145
146static int
147gethostnamadr_async_run(struct asr_query *as, struct asr_result *ar)
148{
149	struct hostent_ext	*h;
150	int			 r, type, saved_errno;
151	FILE			*f;
152	char			 name[MAXDNAME], *data, addr[16], *c;
153
154    next:
155	switch (as->as_state) {
156
157	case ASR_STATE_INIT:
158
159		if (as->as.hostnamadr.family != AF_INET &&
160		    as->as.hostnamadr.family != AF_INET6) {
161			ar->ar_h_errno = NETDB_INTERNAL;
162			ar->ar_errno = EAFNOSUPPORT;
163			async_set_state(as, ASR_STATE_HALT);
164			break;
165		}
166
167		if ((as->as.hostnamadr.family == AF_INET &&
168		     as->as.hostnamadr.addrlen != INADDRSZ) ||
169		    (as->as.hostnamadr.family == AF_INET6 &&
170		     as->as.hostnamadr.addrlen != IN6ADDRSZ)) {
171			ar->ar_h_errno = NETDB_INTERNAL;
172			ar->ar_errno = EINVAL;
173			async_set_state(as, ASR_STATE_HALT);
174			break;
175		}
176
177		if (as->as_type == ASR_GETHOSTBYNAME) {
178
179			if (as->as.hostnamadr.name[0] == '\0') {
180				ar->ar_h_errno = NO_DATA;
181				async_set_state(as, ASR_STATE_HALT);
182				break;
183			}
184
185			/* Name might be an IP address string */
186			for (c = as->as.hostnamadr.name; *c; c++)
187				if (!isdigit((unsigned char)*c) &&
188				     *c != '.' && *c != ':')
189					break;
190			if (*c == 0 &&
191			    inet_pton(as->as.hostnamadr.family,
192			    as->as.hostnamadr.name, addr) == 1) {
193				h = hostent_from_addr(as->as.hostnamadr.family,
194				    as->as.hostnamadr.name, addr);
195				if (h == NULL) {
196					ar->ar_errno = errno;
197					ar->ar_h_errno = NETDB_INTERNAL;
198				}
199				else {
200					ar->ar_hostent = &h->h;
201					ar->ar_h_errno = NETDB_SUCCESS;
202				}
203				async_set_state(as, ASR_STATE_HALT);
204				break;
205			}
206
207			if (!hnok_lenient(as->as.hostnamadr.name)) {
208				ar->ar_h_errno = NETDB_INTERNAL;
209				ar->ar_errno = EINVAL;
210				async_set_state(as, ASR_STATE_HALT);
211				break;
212			}
213
214			/*
215			 * If hostname is "localhost" or falls within the
216			 * ".localhost." domain, use local address.
217			 * RFC 6761, 6.3:
218			 * 3. Name resolution APIs and libraries SHOULD
219			 * recognize localhost names as special and SHOULD
220			 * always return the IP loopback address for address
221			 * queries and negative responses for all other query
222			 * types.  Name resolution APIs SHOULD NOT send queries
223			 * for localhost names to their configured caching DNS
224			 * server(s).
225			 */
226
227			if (_asr_is_localhost(as->as.hostnamadr.name)) {
228				inet_pton(as->as.hostnamadr.family,
229				    as->as.hostnamadr.family == AF_INET ?
230				    "127.0.0.1" : "::1", addr);
231				h = hostent_from_addr(as->as.hostnamadr.family,
232				    as->as.hostnamadr.name, addr);
233				if (h == NULL) {
234					ar->ar_errno = errno;
235					ar->ar_h_errno = NETDB_INTERNAL;
236				}
237				else {
238					ar->ar_hostent = &h->h;
239					ar->ar_h_errno = NETDB_SUCCESS;
240				}
241				async_set_state(as, ASR_STATE_HALT);
242				break;
243			}
244		}
245		async_set_state(as, ASR_STATE_NEXT_DB);
246		break;
247
248	case ASR_STATE_NEXT_DB:
249
250		if (_asr_iter_db(as) == -1) {
251			async_set_state(as, ASR_STATE_NOT_FOUND);
252			break;
253		}
254
255		switch (AS_DB(as)) {
256
257		case ASR_DB_DNS:
258
259			/* Create a subquery to do the DNS lookup */
260
261			if (as->as_type == ASR_GETHOSTBYNAME) {
262				type = (as->as.hostnamadr.family == AF_INET) ?
263				    T_A : T_AAAA;
264				as->as_subq = _res_search_async_ctx(
265				    as->as.hostnamadr.name,
266				    C_IN, type, as->as_ctx);
267			} else {
268				_asr_addr_as_fqdn(as->as.hostnamadr.addr,
269				    as->as.hostnamadr.family,
270				    name, sizeof(name));
271				as->as_subq = _res_query_async_ctx(
272				    name, C_IN, T_PTR, as->as_ctx);
273			}
274
275			if (as->as_subq == NULL) {
276				ar->ar_errno = errno;
277				ar->ar_h_errno = NETDB_INTERNAL;
278				async_set_state(as, ASR_STATE_HALT);
279				break;
280			}
281
282			async_set_state(as, ASR_STATE_SUBQUERY);
283			break;
284
285		case ASR_DB_FILE:
286
287			/* Try to find a match in the host file */
288
289			if ((f = fopen(_PATH_HOSTS, "re")) == NULL)
290				break;
291
292			if (as->as_type == ASR_GETHOSTBYNAME)
293				data = as->as.hostnamadr.name;
294			else
295				data = as->as.hostnamadr.addr;
296
297			h = hostent_file_match(f, as->as_type,
298			    as->as.hostnamadr.family, data,
299			    as->as.hostnamadr.addrlen);
300			saved_errno = errno;
301			fclose(f);
302			errno = saved_errno;
303
304			if (h == NULL) {
305				if (errno) {
306					ar->ar_errno = errno;
307					ar->ar_h_errno = NETDB_INTERNAL;
308					async_set_state(as, ASR_STATE_HALT);
309				}
310				/* otherwise not found */
311				break;
312			}
313			ar->ar_hostent = &h->h;
314			ar->ar_h_errno = NETDB_SUCCESS;
315			async_set_state(as, ASR_STATE_HALT);
316			break;
317		}
318		break;
319
320	case ASR_STATE_SUBQUERY:
321
322		/* Run the DNS subquery. */
323
324		if ((r = asr_run(as->as_subq, ar)) == ASYNC_COND)
325			return (ASYNC_COND);
326
327		/* Done. */
328		as->as_subq = NULL;
329
330		/*
331		 * We either got no packet or a packet without an answer.
332		 * Saveguard the h_errno and use the next DB.
333		 */
334		if (ar->ar_count == 0) {
335			free(ar->ar_data);
336			as->as.hostnamadr.subq_h_errno = ar->ar_h_errno;
337			async_set_state(as, ASR_STATE_NEXT_DB);
338			break;
339		}
340
341		/* Read the hostent from the packet. */
342
343		h = hostent_from_packet(as->as_type,
344		    as->as.hostnamadr.family, ar->ar_data, ar->ar_datalen);
345		free(ar->ar_data);
346		if (h == NULL) {
347			ar->ar_errno = errno;
348			ar->ar_h_errno = NETDB_INTERNAL;
349			async_set_state(as, ASR_STATE_HALT);
350			break;
351		}
352
353		if (as->as_type == ASR_GETHOSTBYADDR) {
354			if (hostent_add_addr(h, as->as.hostnamadr.addr,
355			    as->as.hostnamadr.addrlen) == -1) {
356				free(h);
357				ar->ar_errno = errno;
358				ar->ar_h_errno = NETDB_INTERNAL;
359				async_set_state(as, ASR_STATE_HALT);
360				break;
361			}
362		}
363
364		/*
365		 * No valid hostname or address found in the dns packet.
366		 * Ignore it.
367		 */
368		if ((as->as_type == ASR_GETHOSTBYNAME &&
369		     h->h.h_addr_list[0] == NULL) ||
370		    h->h.h_name == NULL) {
371			free(h);
372			async_set_state(as, ASR_STATE_NEXT_DB);
373			break;
374		}
375
376		ar->ar_hostent = &h->h;
377		ar->ar_h_errno = NETDB_SUCCESS;
378		async_set_state(as, ASR_STATE_HALT);
379		break;
380
381	case ASR_STATE_NOT_FOUND:
382		ar->ar_errno = 0;
383		if (as->as.hostnamadr.subq_h_errno)
384			ar->ar_h_errno = as->as.hostnamadr.subq_h_errno;
385		else
386			ar->ar_h_errno = HOST_NOT_FOUND;
387		async_set_state(as, ASR_STATE_HALT);
388		break;
389
390	case ASR_STATE_HALT:
391		if (ar->ar_h_errno == NETDB_SUCCESS &&
392		    as->as_flags & ASYNC_GETNET)
393			netent_from_hostent(ar);
394		if (ar->ar_h_errno) {
395			ar->ar_hostent = NULL;
396			ar->ar_netent = NULL;
397		} else
398			ar->ar_errno = 0;
399		return (ASYNC_DONE);
400
401	default:
402		ar->ar_errno = EOPNOTSUPP;
403		ar->ar_h_errno = NETDB_INTERNAL;
404		ar->ar_gai_errno = EAI_SYSTEM;
405		async_set_state(as, ASR_STATE_HALT);
406		break;
407	}
408	goto next;
409}
410
411/*
412 * Create a hostent from a numeric address string.
413 */
414static struct hostent_ext *
415hostent_from_addr(int family, const char *name, const char *addr)
416{
417	struct	 hostent_ext *h;
418
419	if ((h = hostent_alloc(family)) == NULL)
420		return (NULL);
421	if (hostent_set_cname(h, name, 0) == -1)
422		goto fail;
423	if (hostent_add_addr(h, addr, h->h.h_length) == -1)
424		goto fail;
425	return (h);
426fail:
427	free(h);
428	return (NULL);
429}
430
431/*
432 * Lookup the first matching entry in the hostfile, either by address or by
433 * name depending on reqtype, and build a hostent from the line.
434 */
435static struct hostent_ext *
436hostent_file_match(FILE *f, int reqtype, int family, const char *data,
437    int datalen)
438{
439	char	*tokens[MAXTOKEN], addr[16], buf[BUFSIZ + 1];
440	struct	 hostent_ext *h;
441	int	 n, i;
442
443	for (;;) {
444		n = _asr_parse_namedb_line(f, tokens, MAXTOKEN, buf, sizeof(buf));
445		if (n == -1) {
446			errno = 0; /* ignore errors reading the file */
447			return (NULL);
448		}
449
450		/* there must be an address and at least one name */
451		if (n < 2)
452			continue;
453
454		if (reqtype == ASR_GETHOSTBYNAME) {
455			for (i = 1; i < n; i++) {
456				if (strcasecmp(data, tokens[i]))
457					continue;
458				if (inet_pton(family, tokens[0], addr) == 1)
459					goto found;
460			}
461		} else {
462			if (inet_pton(family, tokens[0], addr) == 1 &&
463			    memcmp(addr, data, datalen) == 0)
464				goto found;
465		}
466	}
467
468found:
469	if ((h = hostent_alloc(family)) == NULL)
470		return (NULL);
471	if (hostent_set_cname(h, tokens[1], 0) == -1)
472		goto fail;
473	for (i = 2; i < n; i ++)
474		if (hostent_add_alias(h, tokens[i], 0) == -1)
475			goto fail;
476	if (hostent_add_addr(h, addr, h->h.h_length) == -1)
477		goto fail;
478	return (h);
479fail:
480	free(h);
481	return (NULL);
482}
483
484/*
485 * Fill the hostent from the given DNS packet.
486 */
487static struct hostent_ext *
488hostent_from_packet(int reqtype, int family, char *pkt, size_t pktlen)
489{
490	struct hostent_ext	*h;
491	struct asr_unpack	 p;
492	struct asr_dns_header	 hdr;
493	struct asr_dns_query	 q;
494	struct asr_dns_rr	 rr;
495	char			 dname[MAXDNAME];
496
497	if ((h = hostent_alloc(family)) == NULL)
498		return (NULL);
499
500	_asr_unpack_init(&p, pkt, pktlen);
501	_asr_unpack_header(&p, &hdr);
502	for (; hdr.qdcount; hdr.qdcount--)
503		_asr_unpack_query(&p, &q);
504	strlcpy(dname, q.q_dname, sizeof(dname));
505
506	for (; hdr.ancount; hdr.ancount--) {
507		_asr_unpack_rr(&p, &rr);
508		if (rr.rr_class != C_IN)
509			continue;
510		switch (rr.rr_type) {
511
512		case T_CNAME:
513			if (reqtype == ASR_GETHOSTBYNAME) {
514				if (hostent_add_alias(h, rr.rr_dname, 1) == -1)
515					goto fail;
516			} else {
517				if (strcasecmp(rr.rr_dname, dname) == 0)
518					strlcpy(dname, rr.rr.cname.cname,
519					    sizeof(dname));
520			}
521			break;
522
523		case T_PTR:
524			if (reqtype != ASR_GETHOSTBYADDR)
525				break;
526			if (strcasecmp(rr.rr_dname, dname) != 0)
527				continue;
528			if (hostent_set_cname(h, rr.rr.ptr.ptrname, 1) == -1)
529				hostent_add_alias(h, rr.rr.ptr.ptrname, 1);
530			break;
531
532		case T_A:
533			if (reqtype != ASR_GETHOSTBYNAME)
534				break;
535			if (family != AF_INET)
536				break;
537			if (hostent_set_cname(h, rr.rr_dname, 1) == -1)
538				;
539			if (hostent_add_addr(h, &rr.rr.in_a.addr, 4) == -1)
540				goto fail;
541			break;
542
543		case T_AAAA:
544			if (reqtype != ASR_GETHOSTBYNAME)
545				break;
546			if (family != AF_INET6)
547				break;
548			if (hostent_set_cname(h, rr.rr_dname, 1) == -1)
549				;
550			if (hostent_add_addr(h, &rr.rr.in_aaaa.addr6, 16) == -1)
551				goto fail;
552			break;
553		}
554	}
555
556	return (h);
557fail:
558	free(h);
559	return (NULL);
560}
561
562static struct hostent_ext *
563hostent_alloc(int family)
564{
565	struct hostent_ext	*h;
566	size_t			alloc;
567
568	alloc = sizeof(*h) + 1024;
569	if ((h = calloc(1, alloc)) == NULL)
570		return (NULL);
571
572	h->h.h_addrtype = family;
573	h->h.h_length = (family == AF_INET) ? 4 : 16;
574	h->h.h_aliases = h->aliases;
575	h->h.h_addr_list = h->addrs;
576	h->pos = (char *)(h) + sizeof(*h);
577	h->end = h->pos + 1024;
578
579	return (h);
580}
581
582static int
583hostent_set_cname(struct hostent_ext *h, const char *name, int isdname)
584{
585	char	buf[MAXDNAME];
586	size_t	n;
587
588	if (h->h.h_name)
589		return (-1);
590
591	if (isdname) {
592		_asr_strdname(name, buf, sizeof buf);
593		buf[strlen(buf) - 1] = '\0';
594		if (!res_hnok(buf))
595			return (-1);
596		name = buf;
597	}
598
599	n = strlen(name) + 1;
600	if (h->pos + n >= h->end)
601		return (-1);
602
603	h->h.h_name = h->pos;
604	memmove(h->pos, name, n);
605	h->pos += n;
606	return (0);
607}
608
609static int
610hostent_add_alias(struct hostent_ext *h, const char *name, int isdname)
611{
612	char	buf[MAXDNAME];
613	size_t	i, n;
614
615	for (i = 0; i < MAXALIASES; i++)
616		if (h->aliases[i] == NULL)
617			break;
618	if (i == MAXALIASES)
619		return (0);
620
621	if (isdname) {
622		_asr_strdname(name, buf, sizeof buf);
623		buf[strlen(buf)-1] = '\0';
624		if (!res_hnok(buf))
625			return (-1);
626		name = buf;
627	}
628
629	n = strlen(name) + 1;
630	if (h->pos + n >= h->end)
631		return (0);
632
633	h->aliases[i] = h->pos;
634	memmove(h->pos, name, n);
635	h->pos += n;
636	return (0);
637}
638
639static int
640hostent_add_addr(struct hostent_ext *h, const void *addr, size_t size)
641{
642	int	i;
643
644	for (i = 0; i < MAXADDRS; i++)
645		if (h->addrs[i] == NULL)
646			break;
647	if (i == MAXADDRS)
648		return (0);
649
650	if (h->pos + size >= h->end)
651		return (0);
652
653	h->addrs[i] = h->pos;
654	memmove(h->pos, addr, size);
655	h->pos += size;
656	return (0);
657}
658
659static void
660netent_from_hostent(struct asr_result *ar)
661{
662	struct in_addr		 *addr;
663	struct netent_ext	 *n;
664	struct hostent_ext	 *h;
665	char			**na, **ha;
666	size_t			  sz;
667
668	/* Allocate and initialize the output. */
669	if ((n = calloc(1, sizeof(*n) + 1024)) == NULL) {
670		ar->ar_h_errno = NETDB_INTERNAL;
671		ar->ar_errno = errno;
672		goto out;
673	}
674	n->pos = (char *)(n) + sizeof(*n);
675	n->end = n->pos + 1024;
676	n->n.n_name = n->pos;
677	n->n.n_aliases = n->aliases;
678
679	/* Copy the fixed-size data. */
680	h = (struct hostent_ext *)ar->ar_hostent;
681	addr = (struct in_addr *)h->h.h_addr;
682	n->n.n_net = ntohl(addr->s_addr);
683	n->n.n_addrtype = h->h.h_addrtype;
684
685	/* Copy the network name. */
686	sz = strlen(h->h.h_name) + 1;
687	memcpy(n->pos, h->h.h_name, sz);
688	n->pos += sz;
689
690	/*
691	 * Copy the aliases.
692	 * No overflow check is needed because we are merely copying
693	 * a part of the data from a structure of the same size.
694	 */
695	na = n->aliases;
696	for (ha = h->aliases; *ha != NULL; ha++) {
697		sz = strlen(*ha) + 1;
698		memcpy(n->pos, *ha, sz);
699		*na++ = n->pos;
700		n->pos += sz;
701	}
702	*na = NULL;
703
704	/* Handle the return values. */
705	ar->ar_netent = &n->n;
706out:
707	free(ar->ar_hostent);
708	ar->ar_hostent = NULL;
709}
710