1/*	$NetBSD: getaddresses.c,v 1.7 2023/01/25 21:43:29 christos Exp $	*/
2
3/*
4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5 *
6 * SPDX-License-Identifier: MPL-2.0
7 *
8 * This Source Code Form is subject to the terms of the Mozilla Public
9 * License, v. 2.0. If a copy of the MPL was not distributed with this
10 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11 *
12 * See the COPYRIGHT file distributed with this work for additional
13 * information regarding copyright ownership.
14 */
15
16/*! \file */
17
18#include <inttypes.h>
19#include <stdbool.h>
20#include <string.h>
21
22#include <isc/net.h>
23#include <isc/netaddr.h>
24#include <isc/netdb.h>
25#include <isc/netscope.h>
26#include <isc/result.h>
27#include <isc/sockaddr.h>
28#include <isc/string.h>
29#include <isc/util.h>
30
31#include <bind9/getaddresses.h>
32
33isc_result_t
34bind9_getaddresses(const char *hostname, in_port_t port, isc_sockaddr_t *addrs,
35		   int addrsize, int *addrcount) {
36	struct in_addr in4;
37	struct in6_addr in6;
38	bool have_ipv4, have_ipv6;
39	int i;
40
41	struct addrinfo *ai = NULL, *tmpai, hints;
42	int result;
43
44	REQUIRE(hostname != NULL);
45	REQUIRE(addrs != NULL);
46	REQUIRE(addrcount != NULL);
47	REQUIRE(addrsize > 0);
48
49	have_ipv4 = (isc_net_probeipv4() == ISC_R_SUCCESS);
50	have_ipv6 = (isc_net_probeipv6() == ISC_R_SUCCESS);
51
52	/*
53	 * Try IPv4, then IPv6.  In order to handle the extended format
54	 * for IPv6 scoped addresses (address%scope_ID), we'll use a local
55	 * working buffer of 128 bytes.  The length is an ad-hoc value, but
56	 * should be enough for this purpose; the buffer can contain a string
57	 * of at least 80 bytes for scope_ID in addition to any IPv6 numeric
58	 * addresses (up to 46 bytes), the delimiter character and the
59	 * terminating NULL character.
60	 */
61	if (inet_pton(AF_INET, hostname, &in4) == 1) {
62		if (have_ipv4) {
63			isc_sockaddr_fromin(&addrs[0], &in4, port);
64		} else {
65			isc_sockaddr_v6fromin(&addrs[0], &in4, port);
66		}
67		*addrcount = 1;
68		return (ISC_R_SUCCESS);
69	} else if (strlen(hostname) <= 127U) {
70		char tmpbuf[128], *d;
71		uint32_t zone = 0;
72
73		strlcpy(tmpbuf, hostname, sizeof(tmpbuf));
74		d = strchr(tmpbuf, '%');
75		if (d != NULL) {
76			*d = '\0';
77		}
78
79		if (inet_pton(AF_INET6, tmpbuf, &in6) == 1) {
80			isc_netaddr_t na;
81
82			if (!have_ipv6) {
83				return (ISC_R_FAMILYNOSUPPORT);
84			}
85
86			if (d != NULL) {
87				isc_result_t iresult;
88
89				iresult = isc_netscope_pton(AF_INET6, d + 1,
90							    &in6, &zone);
91
92				if (iresult != ISC_R_SUCCESS) {
93					return (iresult);
94				}
95			}
96
97			isc_netaddr_fromin6(&na, &in6);
98			isc_netaddr_setzone(&na, zone);
99			isc_sockaddr_fromnetaddr(
100				&addrs[0], (const isc_netaddr_t *)&na, port);
101
102			*addrcount = 1;
103			return (ISC_R_SUCCESS);
104		}
105	}
106	memset(&hints, 0, sizeof(hints));
107	if (!have_ipv6) {
108		hints.ai_family = PF_INET;
109	} else if (!have_ipv4) {
110		hints.ai_family = PF_INET6;
111	} else {
112		hints.ai_family = PF_UNSPEC;
113#ifdef AI_ADDRCONFIG
114		hints.ai_flags = AI_ADDRCONFIG;
115#endif /* ifdef AI_ADDRCONFIG */
116	}
117	hints.ai_socktype = SOCK_STREAM;
118#ifdef AI_ADDRCONFIG
119again:
120#endif /* ifdef AI_ADDRCONFIG */
121	result = getaddrinfo(hostname, NULL, &hints, &ai);
122	switch (result) {
123	case 0:
124		break;
125	case EAI_NONAME:
126#if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
127	case EAI_NODATA:
128#endif /* if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME) */
129		return (ISC_R_NOTFOUND);
130#ifdef AI_ADDRCONFIG
131	case EAI_BADFLAGS:
132		if ((hints.ai_flags & AI_ADDRCONFIG) != 0) {
133			hints.ai_flags &= ~AI_ADDRCONFIG;
134			goto again;
135		}
136#endif /* ifdef AI_ADDRCONFIG */
137		FALLTHROUGH;
138	default:
139		return (ISC_R_FAILURE);
140	}
141	for (tmpai = ai, i = 0; tmpai != NULL && i < addrsize;
142	     tmpai = tmpai->ai_next)
143	{
144		if (tmpai->ai_family != AF_INET && tmpai->ai_family != AF_INET6)
145		{
146			continue;
147		}
148		if (tmpai->ai_family == AF_INET) {
149			struct sockaddr_in *sin;
150			sin = (struct sockaddr_in *)tmpai->ai_addr;
151			isc_sockaddr_fromin(&addrs[i], &sin->sin_addr, port);
152		} else {
153			struct sockaddr_in6 *sin6;
154			sin6 = (struct sockaddr_in6 *)tmpai->ai_addr;
155			isc_sockaddr_fromin6(&addrs[i], &sin6->sin6_addr, port);
156		}
157		i++;
158	}
159	freeaddrinfo(ai);
160	*addrcount = i;
161	if (*addrcount == 0) {
162		return (ISC_R_NOTFOUND);
163	} else {
164		return (ISC_R_SUCCESS);
165	}
166}
167