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