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