xref: /netbsd-src/external/mpl/dhcp/bind/dist/lib/irs/getnameinfo.c (revision 4afad4b7fa6d4a0d3dedf41d1587a7250710ae54)
1 /*	$NetBSD: getnameinfo.c,v 1.1 2024/02/18 20:57:47 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 /*
19  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
20  * All rights reserved.
21  *
22  * Redistribution and use in source and binary forms, with or without
23  * modification, are permitted provided that the following conditions
24  * are met:
25  * 1. Redistributions of source code must retain the above copyright
26  *    notice, this list of conditions and the following disclaimer.
27  * 2. Redistributions in binary form must reproduce the above copyright
28  *    notice, this list of conditions and the following disclaimer in the
29  *    documentation and/or other materials provided with the distribution.
30  * 3. Neither the name of the project nor the names of its contributors
31  *    may be used to endorse or promote products derived from this software
32  *    without specific prior written permission.
33  *
34  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
35  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
36  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
37  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
38  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
39  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
40  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
41  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
42  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
43  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
44  * SUCH DAMAGE.
45  */
46 
47 /**
48  *    getnameinfo() returns the hostname for the struct sockaddr sa which is
49  *    salen bytes long. The hostname is of length hostlen and is returned via
50  *    *host. The maximum length of the hostname is 1025 bytes: #NI_MAXHOST.
51  *
52  *    The name of the service associated with the port number in sa is
53  *    returned in *serv. It is servlen bytes long. The maximum length of the
54  *    service name is #NI_MAXSERV - 32 bytes.
55  *
56  *    The flags argument sets the following bits:
57  *
58  * \li   #NI_NOFQDN:
59  *           A fully qualified domain name is not required for local hosts.
60  *           The local part of the fully qualified domain name is returned
61  *           instead.
62  *
63  * \li   #NI_NUMERICHOST
64  *           Return the address in numeric form, as if calling inet_ntop(),
65  *           instead of a host name.
66  *
67  * \li   #NI_NAMEREQD
68  *           A name is required. If the hostname cannot be found in the DNS
69  *           and this flag is set, a non-zero error code is returned. If the
70  *           hostname is not found and the flag is not set, the address is
71  *           returned in numeric form.
72  *
73  * \li   #NI_NUMERICSERV
74  *           The service name is returned as a digit string representing the
75  *           port number.
76  *
77  * \li   #NI_DGRAM
78  *           Specifies that the service being looked up is a datagram
79  *           service, and causes getservbyport() to be called with a second
80  *           argument of "udp" instead of its default of "tcp". This is
81  *           required for the few ports (512-514) that have different
82  *           services for UDP and TCP.
83  *
84  * \section getnameinfo_return Return Values
85  *
86  *    getnameinfo() returns 0 on success or a non-zero error code if
87  *    an error occurs.
88  *
89  * \section getname_see See Also
90  *
91  *    RFC3493, getservbyport(),
92  *    getnamebyaddr(). inet_ntop().
93  */
94 
95 #include <stdbool.h>
96 #include <stdio.h>
97 #include <string.h>
98 
99 #include <isc/netaddr.h>
100 #include <isc/print.h>
101 #include <isc/sockaddr.h>
102 #include <isc/string.h>
103 #include <isc/util.h>
104 
105 #include <dns/byaddr.h>
106 #include <dns/client.h>
107 #include <dns/fixedname.h>
108 #include <dns/name.h>
109 #include <dns/rdata.h>
110 #include <dns/rdataset.h>
111 #include <dns/rdatastruct.h>
112 #include <dns/result.h>
113 
114 #include <irs/context.h>
115 #include <irs/netdb.h>
116 
117 #define SUCCESS 0
118 
119 /*% afd structure definition */
120 static struct afd {
121 	int a_af;
122 	size_t a_addrlen;
123 	size_t a_socklen;
124 } afdl[] = {
125 	/*!
126 	 * First entry is linked last...
127 	 */
128 	{ AF_INET, sizeof(struct in_addr), sizeof(struct sockaddr_in) },
129 	{ AF_INET6, sizeof(struct in6_addr), sizeof(struct sockaddr_in6) },
130 	{ 0, 0, 0 },
131 };
132 
133 /*!
134  * The test against 0 is there to keep the Solaris compiler
135  * from complaining about "end-of-loop code not reached".
136  */
137 #define ERR(code)                     \
138 	do {                          \
139 		result = (code);      \
140 		if (result != 0)      \
141 			goto cleanup; \
142 	} while (0)
143 
144 #ifdef _WIN32
145 int
getnameinfo(const struct sockaddr * sa,socklen_t salen,char * host,DWORD hostlen,char * serv,DWORD servlen,int flags)146 getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host,
147 	    DWORD hostlen, char *serv, DWORD servlen, int flags) {
148 #else
149 int
150 getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host,
151 	    socklen_t hostlen, char *serv, socklen_t servlen, int flags) {
152 #endif
153 	struct afd *afd = NULL;
154 	struct servent *sp;
155 	unsigned short port = 0;
156 #ifdef IRS_PLATFORM_HAVESALEN
157 	size_t len;
158 #endif /* ifdef IRS_PLATFORM_HAVESALEN */
159 	int family, i;
160 	const void *addr = NULL;
161 	char *p;
162 #if 0
163 	unsigned long v4a;
164 	unsigned char pfx;
165 #endif /* if 0 */
166 	char numserv[sizeof("65000")];
167 	char numaddr[sizeof("abcd:abcd:abcd:abcd:abcd:abcd:255.255.255.255") +
168 		     1 + sizeof("4294967295")];
169 	const char *proto;
170 	int result = SUCCESS;
171 
172 	if (sa == NULL) {
173 		ERR(EAI_FAIL);
174 	}
175 
176 #ifdef IRS_PLATFORM_HAVESALEN
177 	len = sa->sa_len;
178 	if (len != salen) {
179 		ERR(EAI_FAIL);
180 	}
181 #endif /* ifdef IRS_PLATFORM_HAVESALEN */
182 
183 	family = sa->sa_family;
184 	for (i = 0; afdl[i].a_af; i++) {
185 		if (afdl[i].a_af == family) {
186 			{
187 				afd = &afdl[i];
188 				goto found;
189 			}
190 		}
191 	}
192 	ERR(EAI_FAMILY);
193 
194 found:
195 	if (salen != afd->a_socklen) {
196 		ERR(EAI_FAIL);
197 	}
198 
199 	switch (family) {
200 	case AF_INET:
201 		port = ((const struct sockaddr_in *)sa)->sin_port;
202 		addr = &((const struct sockaddr_in *)sa)->sin_addr.s_addr;
203 		break;
204 
205 	case AF_INET6:
206 		port = ((const struct sockaddr_in6 *)sa)->sin6_port;
207 		addr = ((const struct sockaddr_in6 *)sa)->sin6_addr.s6_addr;
208 		break;
209 
210 	default:
211 		UNREACHABLE();
212 	}
213 	proto = ((flags & NI_DGRAM) != 0) ? "udp" : "tcp";
214 
215 	if (serv == NULL || servlen == 0U) {
216 		/*
217 		 * Caller does not want service.
218 		 */
219 	} else if ((flags & NI_NUMERICSERV) != 0 ||
220 		   (sp = getservbyport(port, proto)) == NULL)
221 	{
222 		snprintf(numserv, sizeof(numserv), "%d", ntohs(port));
223 		if ((strlen(numserv) + 1) > servlen) {
224 			ERR(EAI_OVERFLOW);
225 		}
226 		strlcpy(serv, numserv, servlen);
227 	} else {
228 		if ((strlen(sp->s_name) + 1) > servlen) {
229 			ERR(EAI_OVERFLOW);
230 		}
231 		strlcpy(serv, sp->s_name, servlen);
232 	}
233 
234 #if 0
235 	switch (sa->sa_family) {
236 	case AF_INET:
237 		v4a = ((struct sockaddr_in *)sa)->sin_addr.s_addr;
238 		if (IN_MULTICAST(v4a) || IN_EXPERIMENTAL(v4a)) {
239 			flags |= NI_NUMERICHOST;
240 		}
241 		v4a >>= IN_CLASSA_NSHIFT;
242 		if (v4a == 0 || v4a == IN_LOOPBACKNET) {
243 			flags |= NI_NUMERICHOST;
244 		}
245 		break;
246 
247 	case AF_INET6:
248 		pfx = ((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr[0];
249 		if (pfx == 0 || pfx == 0xfe || pfx == 0xff) {
250 			flags |= NI_NUMERICHOST;
251 		}
252 		break;
253 	}
254 #endif /* if 0 */
255 
256 	if (host == NULL || hostlen == 0U) {
257 		/*
258 		 * do nothing in this case.
259 		 * in case you are wondering if "&&" is more correct than
260 		 * "||" here: RFC3493 says that host == NULL or hostlen == 0
261 		 * means that the caller does not want the result.
262 		 */
263 	} else if ((flags & NI_NUMERICHOST) != 0) {
264 		if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr)) ==
265 		    NULL)
266 		{
267 			ERR(EAI_SYSTEM);
268 		}
269 #if defined(IRS_HAVE_SIN6_SCOPE_ID)
270 		if (afd->a_af == AF_INET6 &&
271 		    ((const struct sockaddr_in6 *)sa)->sin6_scope_id)
272 		{
273 			char *p = numaddr + strlen(numaddr);
274 			const char *stringscope = NULL;
275 #ifdef VENDOR_SPECIFIC
276 			/*
277 			 * Vendors may want to add support for
278 			 * non-numeric scope identifier.
279 			 */
280 			stringscope = foo;
281 #endif /* ifdef VENDOR_SPECIFIC */
282 			if (stringscope == NULL) {
283 				snprintf(p, sizeof(numaddr) - (p - numaddr),
284 					 "%%%u",
285 					 ((const struct sockaddr_in6 *)sa)
286 						 ->sin6_scope_id);
287 			} else {
288 				snprintf(p, sizeof(numaddr) - (p - numaddr),
289 					 "%%%s", stringscope);
290 			}
291 		}
292 #endif /* if defined(IRS_HAVE_SIN6_SCOPE_ID) */
293 		if (strlen(numaddr) + 1 > hostlen) {
294 			ERR(EAI_OVERFLOW);
295 		}
296 		strlcpy(host, numaddr, hostlen);
297 	} else {
298 		isc_netaddr_t netaddr;
299 		dns_fixedname_t ptrfname;
300 		dns_name_t *ptrname;
301 		irs_context_t *irsctx = NULL;
302 		dns_client_t *client;
303 		bool found = false;
304 		dns_namelist_t answerlist;
305 		dns_rdataset_t *rdataset;
306 		isc_region_t hostregion;
307 		char hoststr[1024]; /* is this enough? */
308 		isc_result_t iresult;
309 
310 		/* Get IRS context and the associated DNS client object */
311 		iresult = irs_context_get(&irsctx);
312 		if (iresult != ISC_R_SUCCESS) {
313 			ERR(EAI_FAIL);
314 		}
315 		client = irs_context_getdnsclient(irsctx);
316 
317 		/* Make query name */
318 		isc_netaddr_fromsockaddr(&netaddr, (const isc_sockaddr_t *)sa);
319 		ptrname = dns_fixedname_initname(&ptrfname);
320 		iresult = dns_byaddr_createptrname(&netaddr, 0, ptrname);
321 		if (iresult != ISC_R_SUCCESS) {
322 			ERR(EAI_FAIL);
323 		}
324 
325 		/* Get the PTR RRset */
326 		ISC_LIST_INIT(answerlist);
327 		iresult = dns_client_resolve(client, ptrname, dns_rdataclass_in,
328 					     dns_rdatatype_ptr, 0, &answerlist);
329 		switch (iresult) {
330 		case ISC_R_SUCCESS:
331 		/*
332 		 * a 'non-existent' error is not necessarily fatal for
333 		 * getnameinfo().
334 		 */
335 		case DNS_R_NCACHENXDOMAIN:
336 		case DNS_R_NCACHENXRRSET:
337 			break;
338 		case DNS_R_SIGINVALID:
339 		case DNS_R_SIGEXPIRED:
340 		case DNS_R_SIGFUTURE:
341 		case DNS_R_KEYUNAUTHORIZED:
342 		case DNS_R_MUSTBESECURE:
343 		case DNS_R_COVERINGNSEC:
344 		case DNS_R_NOTAUTHORITATIVE:
345 		case DNS_R_NOVALIDKEY:
346 		case DNS_R_NOVALIDDS:
347 		case DNS_R_NOVALIDSIG:
348 			/*
349 			 * Don't use ERR as GCC 7 wants to raise a
350 			 * warning with ERR about possible falling
351 			 * through which is impossible.
352 			 */
353 			result = EAI_INSECUREDATA;
354 			goto cleanup;
355 		default:
356 			ERR(EAI_FAIL);
357 		}
358 
359 		/* Parse the answer for the hostname */
360 		for (ptrname = ISC_LIST_HEAD(answerlist); ptrname != NULL;
361 		     ptrname = ISC_LIST_NEXT(ptrname, link))
362 		{
363 			for (rdataset = ISC_LIST_HEAD(ptrname->list);
364 			     rdataset != NULL;
365 			     rdataset = ISC_LIST_NEXT(rdataset, link))
366 			{
367 				if (!dns_rdataset_isassociated(rdataset)) {
368 					continue;
369 				}
370 				if (rdataset->type != dns_rdatatype_ptr) {
371 					continue;
372 				}
373 
374 				for (iresult = dns_rdataset_first(rdataset);
375 				     iresult == ISC_R_SUCCESS;
376 				     iresult = dns_rdataset_next(rdataset))
377 				{
378 					dns_rdata_t rdata;
379 					dns_rdata_ptr_t rdata_ptr;
380 					isc_buffer_t b;
381 
382 					dns_rdata_init(&rdata);
383 					dns_rdataset_current(rdataset, &rdata);
384 					dns_rdata_tostruct(&rdata, &rdata_ptr,
385 							   NULL);
386 
387 					isc_buffer_init(&b, hoststr,
388 							sizeof(hoststr));
389 					iresult = dns_name_totext(
390 						&rdata_ptr.ptr, true, &b);
391 					dns_rdata_freestruct(&rdata_ptr);
392 					if (iresult == ISC_R_SUCCESS) {
393 						/*
394 						 * We ignore the rest of the
395 						 * answer.  After all,
396 						 * getnameinfo() can return
397 						 * at most one hostname.
398 						 */
399 						found = true;
400 						isc_buffer_usedregion(
401 							&b, &hostregion);
402 						goto ptrfound;
403 					}
404 				}
405 			}
406 		}
407 	ptrfound:
408 		dns_client_freeresanswer(client, &answerlist);
409 		if (found) {
410 			if ((flags & NI_NOFQDN) != 0) {
411 				p = strchr(hoststr, '.');
412 				if (p) {
413 					*p = '\0';
414 				}
415 			}
416 			if (hostregion.length + 1 > hostlen) {
417 				ERR(EAI_OVERFLOW);
418 			}
419 			snprintf(host, hostlen, "%.*s", (int)hostregion.length,
420 				 (char *)hostregion.base);
421 		} else {
422 			if ((flags & NI_NAMEREQD) != 0) {
423 				ERR(EAI_NONAME);
424 			}
425 			if (inet_ntop(afd->a_af, addr, numaddr,
426 				      sizeof(numaddr)) == NULL)
427 			{
428 				ERR(EAI_SYSTEM);
429 			}
430 			if ((strlen(numaddr) + 1) > hostlen) {
431 				ERR(EAI_OVERFLOW);
432 			}
433 			strlcpy(host, numaddr, hostlen);
434 		}
435 	}
436 	result = SUCCESS;
437 
438 cleanup:
439 	return (result);
440 }
441