1 /* $NetBSD: myaddrinfo.c,v 1.4 2026/05/09 18:49:22 christos Exp $ */ 2 3 /*++ 4 /* NAME 5 /* myaddrinfo 3 6 /* SUMMARY 7 /* addrinfo encapsulation and emulation 8 /* SYNOPSIS 9 /* #include <myaddrinfo.h> 10 /* 11 /* #define MAI_V4ADDR_BITS ... 12 /* #define MAI_V6ADDR_BITS ... 13 /* #define MAI_V4ADDR_BYTES ... 14 /* #define MAI_V6ADDR_BYTES ... 15 /* 16 /* typedef struct { char buf[....]; } MAI_HOSTNAME_STR; 17 /* typedef struct { char buf[....]; } MAI_HOSTADDR_STR; 18 /* typedef struct { char buf[....]; } MAI_SERVNAME_STR; 19 /* typedef struct { char buf[....]; } MAI_SERVPORT_STR; 20 /* 21 /* int hostname_to_sockaddr(hostname, service, socktype, result) 22 /* const char *hostname; 23 /* const char *service; 24 /* int socktype; 25 /* struct addrinfo **result; 26 /* 27 /* int hostname_to_sockaddr_pf(hostname, pf, service, socktype, result) 28 /* const char *hostname; 29 /* int pf; 30 /* const char *service; 31 /* int socktype; 32 /* struct addrinfo **result; 33 /* 34 /* int hostaddr_to_sockaddr(hostaddr, service, socktype, result) 35 /* const char *hostaddr; 36 /* const char *service; 37 /* int socktype; 38 /* struct addrinfo **result; 39 /* 40 /* int sockaddr_to_hostaddr(sa, salen, hostaddr, portnum, socktype) 41 /* const struct sockaddr *sa; 42 /* SOCKADDR_SIZE salen; 43 /* MAI_HOSTADDR_STR *hostaddr; 44 /* MAI_SERVPORT_STR *portnum; 45 /* int socktype; 46 /* 47 /* int sockaddr_to_hostname(sa, salen, hostname, service, socktype) 48 /* const struct sockaddr *sa; 49 /* SOCKADDR_SIZE salen; 50 /* MAI_HOSTNAME_STR *hostname; 51 /* MAI_SERVNAME_STR *service; 52 /* int socktype; 53 /* 54 /* const char *MAI_STRERROR(error) 55 /* int error; 56 /* DESCRIPTION 57 /* This module provides a simplified user interface to the 58 /* getaddrinfo(3) and getnameinfo(3) routines (which provide 59 /* a unified interface to manipulate IPv4 and IPv6 socket 60 /* address structures). 61 /* 62 /* On systems without getaddrinfo(3) and getnameinfo(3) support, 63 /* emulation for IPv4 only can be enabled by defining 64 /* EMULATE_IPV4_ADDRINFO. 65 /* 66 /* hostname_to_sockaddr() looks up the binary addresses for 67 /* the specified symbolic hostname or numeric address. The 68 /* result should be destroyed with freeaddrinfo(). A null host 69 /* pointer converts to the null host address. 70 /* 71 /* hostname_to_sockaddr_pf() is an extended interface that 72 /* provides a protocol family override. 73 /* 74 /* hostaddr_to_sockaddr() converts a printable network address 75 /* into the corresponding binary form. The result should be 76 /* destroyed with freeaddrinfo(). A null host pointer converts 77 /* to the null host address. 78 /* 79 /* sockaddr_to_hostaddr() converts a binary network address 80 /* into printable form. The result buffers should be large 81 /* enough to hold the printable address or port including the 82 /* null terminator. 83 /* This function strips off the IPv6 datalink suffix. 84 /* 85 /* sockaddr_to_hostname() converts a binary network address 86 /* into a hostname or service. The result buffer should be 87 /* large enough to hold the hostname or service including the 88 /* null terminator. This routine rejects malformed hostnames 89 /* or numeric hostnames and pretends that the lookup failed. 90 /* 91 /* MAI_STRERROR() is an unsafe macro (it evaluates the argument 92 /* multiple times) that invokes strerror() or gai_strerror() 93 /* as appropriate. 94 /* 95 /* This module exports the following constants that should be 96 /* user for storage allocation of name or address information: 97 /* .IP MAI_V4ADDR_BITS 98 /* .IP MAI_V6ADDR_BITS 99 /* .IP MAI_V4ADDR_BYTES 100 /* .IP MAI_V6ADDR_BYTES 101 /* The number of bits or bytes needed to store a binary 102 /* IPv4 or IPv6 network address. 103 /* .PP 104 /* The types MAI_HOST{NAME,ADDR}_STR and MAI_SERV{NAME,PORT}_STR 105 /* implement buffers for the storage of the string representations 106 /* of symbolic or numerical hosts or services. Do not use 107 /* buffer types other than the ones that are expected here, 108 /* or things will blow up with buffer overflow problems. 109 /* 110 /* Arguments: 111 /* .IP hostname 112 /* On input to hostname_to_sockaddr(), a numeric or symbolic 113 /* hostname, or a null pointer (meaning the wild-card listen 114 /* address). On output from sockaddr_to_hostname(), storage 115 /* for the result hostname, or a null pointer. 116 /* .IP pf 117 /* Protocol type: PF_UNSPEC (meaning: use any protocol that is 118 /* available), PF_INET, or PF_INET6. This argument is ignored 119 /* in EMULATE_IPV4_ADDRINFO mode. 120 /* .IP hostaddr 121 /* On input to hostaddr_to_sockaddr(), a numeric hostname, 122 /* or a null pointer (meaning the wild-card listen address). 123 /* On output from sockaddr_to_hostaddr(), storage for the 124 /* result hostaddress, or a null pointer. 125 /* .IP service 126 /* On input to hostname/addr_to_sockaddr(), a numeric or 127 /* symbolic service name, or a null pointer in which case the 128 /* socktype argument is ignored. On output from 129 /* sockaddr_to_hostname/addr(), storage for the result service 130 /* name, or a null pointer. 131 /* .IP portnum 132 /* Storage for the result service port number, or a null pointer. 133 /* .IP socktype 134 /* Socket type: SOCK_STREAM, SOCK_DGRAM, etc. This argument is 135 /* ignored when no service or port are specified. 136 /* .IP sa 137 /* Protocol-independent socket address structure. 138 /* .IP salen 139 /* Protocol-dependent socket address structure size in bytes. 140 /* SEE ALSO 141 /* getaddrinfo(3), getnameinfo(3), freeaddrinfo(3), gai_strerror(3) 142 /* DIAGNOSTICS 143 /* All routines either return 0 upon success, or an error code 144 /* that is compatible with gai_strerror(). 145 /* 146 /* On systems where addrinfo support is emulated by Postfix, 147 /* some out-of-memory errors are not reported to the caller, 148 /* but are handled by mymalloc(). 149 /* BUGS 150 /* The IPv4-only emulation code does not support requests that 151 /* specify a service but no socket type. It returns an error 152 /* indication, instead of enumerating all the possible answers. 153 /* 154 /* The hostname/addr_to_sockaddr() routines should accept a 155 /* list of address families that the caller is interested in, 156 /* and they should return only information of those types. 157 /* 158 /* Unfortunately, it is not possible to remove unwanted address 159 /* family results from hostname_to_sockaddr(), because we 160 /* don't know how the system library routine getaddrinfo() 161 /* allocates memory. For example, getaddrinfo() could save 162 /* space by referencing the same string object from multiple 163 /* addrinfo structures; or it could allocate a string object 164 /* and the addrinfo structure as one memory block. 165 /* 166 /* We could get around this by copying getaddrinfo() results 167 /* to our own private data structures, but that would only 168 /* make an already expensive API even more expensive. 169 /* 170 /* A better workaround is to return a vector of addrinfo 171 /* pointers to the elements that contain only the elements 172 /* that the caller is interested in. The pointer to the 173 /* original getaddrinfo() result can be hidden at the end 174 /* after the null terminator, or before the first element. 175 /* LICENSE 176 /* .ad 177 /* .fi 178 /* The Secure Mailer license must be distributed with this software. 179 /* AUTHOR(S) 180 /* Wietse Venema 181 /* IBM T.J. Watson Research 182 /* P.O. Box 704 183 /* Yorktown Heights, NY 10598, USA 184 /* 185 /* Wietse Venema 186 /* Google, Inc. 187 /* 111 8th Avenue 188 /* New York, NY 10011, USA 189 /*--*/ 190 191 /* System library. */ 192 193 #include <sys_defs.h> 194 #include <sys/types.h> 195 #include <sys/socket.h> 196 #include <netinet/in.h> 197 #include <arpa/inet.h> 198 #include <netdb.h> 199 #include <string.h> 200 #include <errno.h> 201 #include <stdlib.h> 202 #include <stdio.h> /* sprintf() */ 203 204 /* Utility library. */ 205 206 #include <mymalloc.h> 207 #include <valid_hostname.h> 208 #include <sock_addr.h> 209 #include <stringops.h> 210 #include <msg.h> 211 #include <inet_proto.h> 212 #include <myaddrinfo.h> 213 #include <split_at.h> 214 #include <known_tcp_ports.h> 215 216 /* Application-specific. */ 217 218 /* 219 * Use an old trick to save some space: allocate space for two objects in 220 * one. In Postfix we often use this trick for structures that have an array 221 * of things at the end. 222 */ 223 struct ipv4addrinfo { 224 struct addrinfo info; 225 struct sockaddr_in sin; 226 }; 227 228 /* 229 * When we're not interested in service ports, we must pick a socket type 230 * otherwise getaddrinfo() will give us duplicate results: one set for TCP, 231 * and another set for UDP. For consistency, we'll use the same default 232 * socket type for the results from emulation mode. 233 */ 234 #define MAI_SOCKTYPE SOCK_STREAM /* getaddrinfo() query */ 235 236 #ifdef EMULATE_IPV4_ADDRINFO 237 238 /* clone_ipv4addrinfo - clone ipv4addrinfo structure */ 239 240 static struct ipv4addrinfo *clone_ipv4addrinfo(struct ipv4addrinfo * tp) 241 { 242 struct ipv4addrinfo *ip; 243 244 ip = (struct ipv4addrinfo *) mymalloc(sizeof(*ip)); 245 *ip = *tp; 246 ip->info.ai_addr = (struct sockaddr *) &(ip->sin); 247 return (ip); 248 } 249 250 /* init_ipv4addrinfo - initialize an ipv4addrinfo structure */ 251 252 static void init_ipv4addrinfo(struct ipv4addrinfo * ip, int socktype) 253 { 254 255 /* 256 * Portability: null pointers aren't necessarily all-zero bits, so we 257 * make explicit assignments to all the pointers that we're aware of. 258 */ 259 memset((void *) ip, 0, sizeof(*ip)); 260 ip->info.ai_family = PF_INET; 261 ip->info.ai_socktype = socktype; 262 ip->info.ai_protocol = 0; /* XXX */ 263 ip->info.ai_addrlen = sizeof(ip->sin); 264 ip->info.ai_canonname = 0; 265 ip->info.ai_addr = (struct sockaddr *) &(ip->sin); 266 ip->info.ai_next = 0; 267 ip->sin.sin_family = AF_INET; 268 #ifdef HAS_SA_LEN 269 ip->sin.sin_len = sizeof(ip->sin); 270 #endif 271 } 272 273 /* find_service - translate numeric or symbolic service name */ 274 275 static int find_service(const char *service, int socktype) 276 { 277 struct servent *sp; 278 const char *proto; 279 unsigned port; 280 281 service = filter_known_tcp_port(service); 282 if (alldig(service)) { 283 port = atoi(service); 284 return (port < 65536 ? htons(port) : -1); 285 } 286 if (socktype == SOCK_STREAM) { 287 proto = "tcp"; 288 } else if (socktype == SOCK_DGRAM) { 289 proto = "udp"; 290 } else { 291 return (-1); 292 } 293 if ((sp = getservbyname(service, proto)) != 0) { 294 return (sp->s_port); 295 } else { 296 return (-1); 297 } 298 } 299 300 #endif 301 302 /* hostname_to_sockaddr_pf - hostname to binary address form */ 303 304 int hostname_to_sockaddr_pf(const char *hostname, int pf, 305 const char *service, int socktype, 306 struct addrinfo ** res) 307 { 308 #ifdef EMULATE_IPV4_ADDRINFO 309 310 /* 311 * Emulated getaddrinfo(3) version. 312 */ 313 static struct ipv4addrinfo template; 314 struct ipv4addrinfo *ip; 315 struct ipv4addrinfo *prev; 316 struct in_addr addr; 317 struct hostent *hp; 318 char **name_list; 319 int port; 320 321 /* 322 * Validate the service. 323 */ 324 if (service) { 325 if ((port = find_service(service, socktype)) < 0) 326 return (EAI_SERVICE); 327 } else { 328 port = 0; 329 socktype = MAI_SOCKTYPE; 330 } 331 332 /* 333 * No host means INADDR_ANY. 334 */ 335 if (hostname == 0) { 336 ip = (struct ipv4addrinfo *) mymalloc(sizeof(*ip)); 337 init_ipv4addrinfo(ip, socktype); 338 ip->sin.sin_addr.s_addr = INADDR_ANY; 339 ip->sin.sin_port = port; 340 *res = &(ip->info); 341 return (0); 342 } 343 344 /* 345 * Numeric host. 346 */ 347 if (inet_pton(AF_INET, hostname, (void *) &addr) == 1) { 348 ip = (struct ipv4addrinfo *) mymalloc(sizeof(*ip)); 349 init_ipv4addrinfo(ip, socktype); 350 ip->sin.sin_addr = addr; 351 ip->sin.sin_port = port; 352 *res = &(ip->info); 353 return (0); 354 } 355 356 /* 357 * Look up the IPv4 address list. 358 */ 359 if ((hp = gethostbyname(hostname)) == 0) 360 return (h_errno == TRY_AGAIN ? EAI_AGAIN : EAI_NODATA); 361 if (hp->h_addrtype != AF_INET 362 || hp->h_length != sizeof(template.sin.sin_addr)) 363 return (EAI_NODATA); 364 365 /* 366 * Initialize the result template. 367 */ 368 if (template.info.ai_addrlen == 0) 369 init_ipv4addrinfo(&template, socktype); 370 371 /* 372 * Copy the address information into an addrinfo structure. 373 */ 374 prev = &template; 375 for (name_list = hp->h_addr_list; name_list[0]; name_list++) { 376 ip = clone_ipv4addrinfo(prev); 377 ip->sin.sin_addr = IN_ADDR(name_list[0]); 378 ip->sin.sin_port = port; 379 if (prev == &template) 380 *res = &(ip->info); 381 else 382 prev->info.ai_next = &(ip->info); 383 prev = ip; 384 } 385 return (0); 386 #else 387 388 /* 389 * Native getaddrinfo(3) version. 390 * 391 * XXX Wild-card listener issues. 392 * 393 * With most IPv4 plus IPv6 systems, an IPv6 wild-card listener also listens 394 * on the IPv4 wild-card address. Connections from IPv4 clients appear as 395 * IPv4-in-IPv6 addresses; when Postfix support for IPv4 is turned on, 396 * Postfix automatically maps these embedded addresses to their original 397 * IPv4 form. So everything seems to be fine. 398 * 399 * However, some applications prefer to use separate listener sockets for 400 * IPv4 and IPv6. The Postfix IPv6 patch provided such an example. And 401 * this is where things become tricky. On many systems the IPv6 and IPv4 402 * wild-card listeners cannot coexist. When one is already active, the 403 * other fails with EADDRINUSE. Solaris 9, however, will automagically 404 * "do the right thing" and allow both listeners to coexist. 405 * 406 * Recent systems have the IPV6_V6ONLY feature (RFC 3493), which tells the 407 * system that we really mean IPv6 when we say IPv6. This allows us to 408 * set up separate wild-card listener sockets for IPv4 and IPv6. So 409 * everything seems to be fine again. 410 * 411 * The following workaround disables the wild-card IPv4 listener when 412 * IPV6_V6ONLY is unavailable. This is necessary for some Linux versions, 413 * but is not needed for Solaris 9 (which allows IPv4 and IPv6 wild-card 414 * listeners to coexist). Solaris 10 beta already has IPV6_V6ONLY. 415 * 416 * XXX This workaround obviously breaks if we want to support protocols in 417 * addition to IPv6 and IPv4, but it is needed only until IPv6 418 * implementations catch up with RFC 3493. A nicer fix is to filter the 419 * getaddrinfo() result, and to return a vector of addrinfo pointers to 420 * only those types of elements that the caller has expressed interested 421 * in. 422 * 423 * XXX Vanilla AIX 5.1 getaddrinfo() does not support a null hostname with 424 * AI_PASSIVE. And since we don't know how getaddrinfo() manages its 425 * memory we can't bypass it for this special case, or freeaddrinfo() 426 * might blow up. Instead we turn off IPV6_V6ONLY in inet_listen(), and 427 * supply a protocol-dependent hard-coded string value to getaddrinfo() 428 * below, so that it will convert into the appropriate wild-card address. 429 * 430 * XXX AIX 5.[1-3] getaddrinfo() may return a non-null port when a null 431 * service argument is specified. 432 */ 433 struct addrinfo hints; 434 int err; 435 436 memset((void *) &hints, 0, sizeof(hints)); 437 hints.ai_family = (pf != PF_UNSPEC) ? pf : inet_proto_info()->ai_family; 438 hints.ai_socktype = service ? socktype : MAI_SOCKTYPE; 439 if (!hostname) { 440 hints.ai_flags = AI_PASSIVE; 441 #if !defined(IPV6_V6ONLY) || defined(BROKEN_AI_PASSIVE_NULL_HOST) 442 switch (hints.ai_family) { 443 case PF_UNSPEC: 444 hints.ai_family = PF_INET6; 445 #ifdef BROKEN_AI_PASSIVE_NULL_HOST 446 case PF_INET6: 447 hostname = "::"; 448 break; 449 case PF_INET: 450 hostname = "0.0.0.0"; 451 break; 452 #endif 453 } 454 #endif 455 } 456 if (service) { 457 service = filter_known_tcp_port(service); 458 if (alldig(service)) 459 hints.ai_flags |= AI_NUMERICSERV; 460 } 461 err = getaddrinfo(hostname, service, &hints, res); 462 #if defined(BROKEN_AI_NULL_SERVICE) 463 if (service == 0 && err == 0) { 464 struct addrinfo *r; 465 unsigned short *portp; 466 467 for (r = *res; r != 0; r = r->ai_next) 468 if (*(portp = SOCK_ADDR_PORTP(r->ai_addr)) != 0) 469 *portp = 0; 470 } 471 #endif 472 return (err); 473 #endif 474 } 475 476 /* hostaddr_to_sockaddr - printable address to binary address form */ 477 478 int hostaddr_to_sockaddr(const char *hostaddr, const char *service, 479 int socktype, struct addrinfo ** res) 480 { 481 #ifdef EMULATE_IPV4_ADDRINFO 482 483 /* 484 * Emulated getaddrinfo(3) version. 485 */ 486 struct ipv4addrinfo *ip; 487 struct in_addr addr; 488 int port; 489 490 /* 491 * Validate the service. 492 */ 493 if (service) { 494 if ((port = find_service(service, socktype)) < 0) 495 return (EAI_SERVICE); 496 } else { 497 port = 0; 498 socktype = MAI_SOCKTYPE; 499 } 500 501 /* 502 * No host means INADDR_ANY. 503 */ 504 if (hostaddr == 0) { 505 ip = (struct ipv4addrinfo *) mymalloc(sizeof(*ip)); 506 init_ipv4addrinfo(ip, socktype); 507 ip->sin.sin_addr.s_addr = INADDR_ANY; 508 ip->sin.sin_port = port; 509 *res = &(ip->info); 510 return (0); 511 } 512 513 /* 514 * Deal with bad address forms. 515 */ 516 switch (inet_pton(AF_INET, hostaddr, (void *) &addr)) { 517 case 1: /* Success */ 518 break; 519 default: /* Unparsable */ 520 return (EAI_NONAME); 521 case -1: /* See errno */ 522 return (EAI_SYSTEM); 523 } 524 525 /* 526 * Initialize the result structure. 527 */ 528 ip = (struct ipv4addrinfo *) mymalloc(sizeof(*ip)); 529 init_ipv4addrinfo(ip, socktype); 530 531 /* 532 * And copy the result. 533 */ 534 ip->sin.sin_addr = addr; 535 ip->sin.sin_port = port; 536 *res = &(ip->info); 537 538 return (0); 539 #else 540 541 /* 542 * Native getaddrinfo(3) version. See comments in hostname_to_sockaddr(). 543 * 544 * XXX Vanilla AIX 5.1 getaddrinfo() returns multiple results when 545 * converting a printable ipv4 or ipv6 address to socket address with 546 * ai_family=PF_UNSPEC, ai_flags=AI_NUMERICHOST, ai_socktype=SOCK_STREAM, 547 * ai_protocol=0 or IPPROTO_TCP, and service=0. The workaround is to 548 * ignore all but the first result. 549 * 550 * XXX AIX 5.[1-3] getaddrinfo() may return a non-null port when a null 551 * service argument is specified. 552 */ 553 struct addrinfo hints; 554 int err; 555 556 memset(&hints, 0, sizeof(hints)); 557 hints.ai_family = inet_proto_info()->ai_family; 558 hints.ai_socktype = service ? socktype : MAI_SOCKTYPE; 559 hints.ai_flags = AI_NUMERICHOST; 560 if (!hostaddr) { 561 hints.ai_flags |= AI_PASSIVE; 562 #if !defined(IPV6_V6ONLY) || defined(BROKEN_AI_PASSIVE_NULL_HOST) 563 switch (hints.ai_family) { 564 case PF_UNSPEC: 565 hints.ai_family = PF_INET6; 566 #ifdef BROKEN_AI_PASSIVE_NULL_HOST 567 case PF_INET6: 568 hostaddr = "::"; 569 break; 570 case PF_INET: 571 hostaddr = "0.0.0.0"; 572 break; 573 #endif 574 } 575 #endif 576 } 577 if (service) { 578 service = filter_known_tcp_port(service); 579 if (alldig(service)) 580 hints.ai_flags |= AI_NUMERICSERV; 581 } 582 err = getaddrinfo(hostaddr, service, &hints, res); 583 #if defined(BROKEN_AI_NULL_SERVICE) 584 if (service == 0 && err == 0) { 585 struct addrinfo *r; 586 unsigned short *portp; 587 588 for (r = *res; r != 0; r = r->ai_next) 589 if (*(portp = SOCK_ADDR_PORTP(r->ai_addr)) != 0) 590 *portp = 0; 591 } 592 #endif 593 return (err); 594 #endif 595 } 596 597 /* sockaddr_to_hostaddr - binary address to printable address form */ 598 599 int sockaddr_to_hostaddr(const struct sockaddr *sa, SOCKADDR_SIZE salen, 600 MAI_HOSTADDR_STR *hostaddr, 601 MAI_SERVPORT_STR *portnum, 602 int unused_socktype) 603 { 604 #ifdef EMULATE_IPV4_ADDRINFO 605 char portbuf[sizeof("65535")]; 606 ssize_t len; 607 608 /* 609 * Emulated getnameinfo(3) version. The buffer length includes the space 610 * for the null terminator. 611 */ 612 if (sa->sa_family != AF_INET) { 613 errno = EAFNOSUPPORT; 614 return (EAI_SYSTEM); 615 } 616 if (hostaddr != 0) { 617 if (inet_ntop(AF_INET, (void *) &(SOCK_ADDR_IN_ADDR(sa)), 618 hostaddr->buf, sizeof(hostaddr->buf)) == 0) 619 return (EAI_SYSTEM); 620 } 621 if (portnum != 0) { 622 sprintf(portbuf, "%d", ntohs(SOCK_ADDR_IN_PORT(sa)) & 0xffff); 623 if ((len = strlen(portbuf)) >= sizeof(portnum->buf)) { 624 errno = ENOSPC; 625 return (EAI_SYSTEM); 626 } 627 memcpy(portnum->buf, portbuf, len + 1); 628 } 629 return (0); 630 #else 631 int ret; 632 633 /* 634 * Native getnameinfo(3) version. 635 */ 636 ret = getnameinfo(sa, salen, 637 hostaddr ? hostaddr->buf : (char *) 0, 638 hostaddr ? sizeof(hostaddr->buf) : 0, 639 portnum ? portnum->buf : (char *) 0, 640 portnum ? sizeof(portnum->buf) : 0, 641 NI_NUMERICHOST | NI_NUMERICSERV); 642 if (hostaddr != 0 && ret == 0 && sa->sa_family == AF_INET6) 643 (void) split_at(hostaddr->buf, '%'); 644 return (ret); 645 #endif 646 } 647 648 /* sockaddr_to_hostname - binary address to printable hostname */ 649 650 int sockaddr_to_hostname(const struct sockaddr *sa, SOCKADDR_SIZE salen, 651 MAI_HOSTNAME_STR *hostname, 652 MAI_SERVNAME_STR *service, 653 int socktype) 654 { 655 #ifdef EMULATE_IPV4_ADDRINFO 656 657 /* 658 * Emulated getnameinfo(3) version. 659 */ 660 struct hostent *hp; 661 struct servent *sp; 662 size_t len; 663 664 /* 665 * Sanity check. 666 */ 667 if (sa->sa_family != AF_INET) 668 return (EAI_NODATA); 669 670 /* 671 * Look up the host name. 672 */ 673 if (hostname != 0) { 674 if ((hp = gethostbyaddr((char *) &(SOCK_ADDR_IN_ADDR(sa)), 675 sizeof(SOCK_ADDR_IN_ADDR(sa)), 676 AF_INET)) == 0) 677 return (h_errno == TRY_AGAIN ? EAI_AGAIN : EAI_NONAME); 678 679 /* 680 * Save the result. The buffer length includes the space for the null 681 * terminator. Hostname sanity checks are at the end of this 682 * function. 683 */ 684 if ((len = strlen(hp->h_name)) >= sizeof(hostname->buf)) { 685 errno = ENOSPC; 686 return (EAI_SYSTEM); 687 } 688 memcpy(hostname->buf, hp->h_name, len + 1); 689 } 690 691 /* 692 * Look up the service. 693 */ 694 if (service != 0) { 695 if ((sp = getservbyport(ntohs(SOCK_ADDR_IN_PORT(sa)), 696 socktype == SOCK_DGRAM ? "udp" : "tcp")) == 0) 697 return (EAI_NONAME); 698 699 /* 700 * Save the result. The buffer length includes the space for the null 701 * terminator. 702 */ 703 if ((len = strlen(sp->s_name)) >= sizeof(service->buf)) { 704 errno = ENOSPC; 705 return (EAI_SYSTEM); 706 } 707 memcpy(service->buf, sp->s_name, len + 1); 708 } 709 #else 710 711 /* 712 * Native getnameinfo(3) version. 713 */ 714 int err; 715 716 err = getnameinfo(sa, salen, 717 hostname ? hostname->buf : (char *) 0, 718 hostname ? sizeof(hostname->buf) : 0, 719 service ? service->buf : (char *) 0, 720 service ? sizeof(service->buf) : 0, 721 socktype == SOCK_DGRAM ? 722 NI_NAMEREQD | NI_DGRAM : NI_NAMEREQD); 723 if (err != 0) 724 return (err); 725 #endif 726 727 /* 728 * Hostname sanity checks. 729 */ 730 if (hostname != 0) { 731 if (valid_hostaddr(hostname->buf, DONT_GRIPE)) { 732 msg_warn("numeric hostname: %s", hostname->buf); 733 return (EAI_NONAME); 734 } 735 if (!valid_hostname(hostname->buf, DO_GRIPE)) 736 return (EAI_NONAME); 737 } 738 return (0); 739 } 740 741 /* myaddrinfo_control - fine control */ 742 743 void myaddrinfo_control(int name,...) 744 { 745 const char *myname = "myaddrinfo_control"; 746 va_list ap; 747 748 for (va_start(ap, name); name != 0; name = va_arg(ap, int)) { 749 switch (name) { 750 default: 751 msg_panic("%s: bad name %d", myname, name); 752 } 753 } 754 va_end(ap); 755 } 756 757 #ifdef EMULATE_IPV4_ADDRINFO 758 759 /* freeaddrinfo - release storage */ 760 761 void freeaddrinfo(struct addrinfo * ai) 762 { 763 struct addrinfo *ap; 764 struct addrinfo *next; 765 766 /* 767 * Artifact of implementation: tolerate a null pointer argument. 768 */ 769 for (ap = ai; ap != 0; ap = next) { 770 next = ap->ai_next; 771 if (ap->ai_canonname) 772 myfree(ap->ai_canonname); 773 /* ap->ai_addr is allocated within this memory block */ 774 myfree((void *) ap); 775 } 776 } 777 778 static char *ai_errlist[] = { 779 "Success", 780 "Address family for hostname not supported", /* EAI_ADDRFAMILY */ 781 "Temporary failure in name resolution", /* EAI_AGAIN */ 782 "Invalid value for ai_flags", /* EAI_BADFLAGS */ 783 "Non-recoverable failure in name resolution", /* EAI_FAIL */ 784 "ai_family not supported", /* EAI_FAMILY */ 785 "Memory allocation failure", /* EAI_MEMORY */ 786 "No address associated with hostname", /* EAI_NODATA */ 787 "hostname nor servname provided, or not known", /* EAI_NONAME */ 788 "service name not supported for ai_socktype", /* EAI_SERVICE */ 789 "ai_socktype not supported", /* EAI_SOCKTYPE */ 790 "System error returned in errno", /* EAI_SYSTEM */ 791 "Invalid value for hints", /* EAI_BADHINTS */ 792 "Resolved protocol is unknown", /* EAI_PROTOCOL */ 793 "Unknown error", /* EAI_MAX */ 794 }; 795 796 /* gai_strerror - error number to string */ 797 798 char *gai_strerror(int ecode) 799 { 800 801 /* 802 * Note: EAI_SYSTEM errors are not automatically handed over to 803 * strerror(). The application decides. 804 */ 805 if (ecode < 0 || ecode > EAI_MAX) 806 ecode = EAI_MAX; 807 return (ai_errlist[ecode]); 808 } 809 810 #endif 811 812 #ifdef TEST 813 814 /* 815 * A test program that takes some info from the command line and runs it 816 * forward and backward through the above conversion routines. 817 */ 818 #include <stdlib.h> 819 #include <msg.h> 820 #include <vstream.h> 821 #include <msg_vstream.h> 822 823 static int compare_family(const void *a, const void *b) 824 { 825 struct addrinfo *resa = *(struct addrinfo **) a; 826 struct addrinfo *resb = *(struct addrinfo **) b; 827 828 return (resa->ai_family - resb->ai_family); 829 } 830 831 int main(int argc, char **argv) 832 { 833 struct addrinfo *info; 834 struct addrinfo *ip; 835 struct addrinfo **resv; 836 MAI_HOSTNAME_STR host; 837 MAI_HOSTADDR_STR addr; 838 MAI_SERVNAME_STR serv; 839 MAI_SERVPORT_STR port; 840 size_t len, n; 841 int err; 842 char *aport; 843 844 msg_vstream_init(argv[0], VSTREAM_ERR); 845 846 if (argc != 4) 847 msg_fatal("usage: %s protocols hostname hostaddress", argv[0]); 848 849 inet_proto_init(argv[0], argv[1]); 850 851 msg_info("=== hostname %s ===", argv[2]); 852 853 #define STR_OR_NULL(s) ((s) ? (s) : "(null)") 854 855 aport = split_at(argv[2], ':'); 856 if ((err = hostname_to_sockaddr(argv[2], aport, 0, &info)) != 0) { 857 msg_warn("hostname_to_sockaddr(%s:%s): %s", 858 argv[2], STR_OR_NULL(aport), err == EAI_SYSTEM ? 859 strerror(errno) : gai_strerror(err)); 860 } else { 861 for (len = 0, ip = info; ip != 0; ip = ip->ai_next) 862 len += 1; 863 resv = (struct addrinfo **) mymalloc(len * sizeof(*resv)); 864 for (len = 0, ip = info; ip != 0; ip = ip->ai_next) 865 resv[len++] = ip; 866 qsort((void *) resv, len, sizeof(*resv), compare_family); 867 for (n = 0; n < len; n++) { 868 ip = resv[n]; 869 if ((err = sockaddr_to_hostaddr(ip->ai_addr, ip->ai_addrlen, &addr, 870 &port, 0)) != 0) { 871 msg_warn("sockaddr_to_hostaddr: %s", 872 err == EAI_SYSTEM ? strerror(errno) : gai_strerror(err)); 873 continue; 874 } 875 msg_info("%s:%s -> family=%d sock=%d proto=%d %s:%s", 876 argv[2], STR_OR_NULL(aport), ip->ai_family, 877 ip->ai_socktype, ip->ai_protocol, addr.buf, port.buf); 878 if ((err = sockaddr_to_hostname(ip->ai_addr, ip->ai_addrlen, &host, 879 &serv, 0)) != 0) { 880 msg_warn("sockaddr_to_hostname: %s", 881 err == EAI_SYSTEM ? strerror(errno) : gai_strerror(err)); 882 continue; 883 } 884 msg_info("%s:%s -> %s:%s", addr.buf, port.buf, host.buf, serv.buf); 885 } 886 freeaddrinfo(info); 887 myfree((void *) resv); 888 } 889 890 msg_info("=== host address %s ===", argv[3]); 891 892 aport = split_at(argv[3], ':'); 893 if ((err = hostaddr_to_sockaddr(argv[3], aport, 0, &ip)) != 0) { 894 msg_warn("hostaddr_to_sockaddr(%s:%s): %s", 895 argv[3], STR_OR_NULL(aport), err == EAI_SYSTEM ? 896 strerror(errno) : gai_strerror(err)); 897 } else { 898 if ((err = sockaddr_to_hostaddr(ip->ai_addr, ip->ai_addrlen, &addr, 899 &port, 0)) != 0) { 900 msg_warn("sockaddr_to_hostaddr: %s", 901 err == EAI_SYSTEM ? strerror(errno) : gai_strerror(err)); 902 } else { 903 msg_info("%s:%s -> family=%d sock=%d proto=%d %s:%s", argv[3], STR_OR_NULL(aport), 904 ip->ai_family, ip->ai_socktype, ip->ai_protocol, addr.buf, port.buf); 905 if ((err = sockaddr_to_hostname(ip->ai_addr, ip->ai_addrlen, &host, 906 &serv, 0)) != 0) { 907 msg_warn("sockaddr_to_hostname: %s", 908 err == EAI_SYSTEM ? strerror(errno) : gai_strerror(err)); 909 } else 910 msg_info("%s:%s -> %s:%s", addr.buf, port.buf, host.buf, serv.buf); 911 freeaddrinfo(ip); 912 } 913 } 914 exit(0); 915 } 916 917 #endif 918