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 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