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