Home | History | Annotate | Line # | Download | only in util
      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