xref: /netbsd-src/external/ibm-public/postfix/dist/src/util/myaddrinfo.c (revision 6a493d6bc668897c91594964a732d38505b70cbb)
1 /*	$NetBSD: myaddrinfo.c,v 1.1.1.3 2012/12/18 09:02:23 tron 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 
186 /* System library. */
187 
188 #include <sys_defs.h>
189 #include <sys/types.h>
190 #include <sys/socket.h>
191 #include <netinet/in.h>
192 #include <arpa/inet.h>
193 #include <netdb.h>
194 #include <string.h>
195 #include <errno.h>
196 #include <stdlib.h>
197 #include <stdio.h>			/* sprintf() */
198 
199 /* Utility library. */
200 
201 #include <mymalloc.h>
202 #include <valid_hostname.h>
203 #include <sock_addr.h>
204 #include <stringops.h>
205 #include <msg.h>
206 #include <inet_proto.h>
207 #include <myaddrinfo.h>
208 #include <split_at.h>
209 
210 /* Application-specific. */
211 
212  /*
213   * Use an old trick to save some space: allocate space for two objects in
214   * one. In Postfix we often use this trick for structures that have an array
215   * of things at the end.
216   */
217 struct ipv4addrinfo {
218     struct addrinfo info;
219     struct sockaddr_in sin;
220 };
221 
222  /*
223   * When we're not interested in service ports, we must pick a socket type
224   * otherwise getaddrinfo() will give us duplicate results: one set for TCP,
225   * and another set for UDP. For consistency, we'll use the same default
226   * socket type for the results from emulation mode.
227   */
228 #define MAI_SOCKTYPE	SOCK_STREAM	/* getaddrinfo() query */
229 
230 #ifdef EMULATE_IPV4_ADDRINFO
231 
232 /* clone_ipv4addrinfo - clone ipv4addrinfo structure */
233 
234 static struct ipv4addrinfo *clone_ipv4addrinfo(struct ipv4addrinfo * tp)
235 {
236     struct ipv4addrinfo *ip;
237 
238     ip = (struct ipv4addrinfo *) mymalloc(sizeof(*ip));
239     *ip = *tp;
240     ip->info.ai_addr = (struct sockaddr *) & (ip->sin);
241     return (ip);
242 }
243 
244 /* init_ipv4addrinfo - initialize an ipv4addrinfo structure */
245 
246 static void init_ipv4addrinfo(struct ipv4addrinfo * ip, int socktype)
247 {
248 
249     /*
250      * Portability: null pointers aren't necessarily all-zero bits, so we
251      * make explicit assignments to all the pointers that we're aware of.
252      */
253     memset((char *) ip, 0, sizeof(*ip));
254     ip->info.ai_family = PF_INET;
255     ip->info.ai_socktype = socktype;
256     ip->info.ai_protocol = 0;			/* XXX */
257     ip->info.ai_addrlen = sizeof(ip->sin);
258     ip->info.ai_canonname = 0;
259     ip->info.ai_addr = (struct sockaddr *) & (ip->sin);
260     ip->info.ai_next = 0;
261     ip->sin.sin_family = AF_INET;
262 #ifdef HAS_SA_LEN
263     ip->sin.sin_len = sizeof(ip->sin);
264 #endif
265 }
266 
267 /* find_service - translate numeric or symbolic service name */
268 
269 static int find_service(const char *service, int socktype)
270 {
271     struct servent *sp;
272     const char *proto;
273     unsigned port;
274 
275     if (alldig(service)) {
276 	port = atoi(service);
277 	return (port < 65536 ? htons(port) : -1);
278     }
279     if (socktype == SOCK_STREAM) {
280 	proto = "tcp";
281     } else if (socktype == SOCK_DGRAM) {
282 	proto = "udp";
283     } else {
284 	return (-1);
285     }
286     if ((sp = getservbyname(service, proto)) != 0) {
287 	return (sp->s_port);
288     } else {
289 	return (-1);
290     }
291 }
292 
293 #endif
294 
295 /* hostname_to_sockaddr_pf - hostname to binary address form */
296 
297 int     hostname_to_sockaddr_pf(const char *hostname, int pf,
298 			             const char *service, int socktype,
299 			             struct addrinfo ** res)
300 {
301 #ifdef EMULATE_IPV4_ADDRINFO
302 
303     /*
304      * Emulated getaddrinfo(3) version.
305      */
306     static struct ipv4addrinfo template;
307     struct ipv4addrinfo *ip;
308     struct ipv4addrinfo *prev;
309     struct in_addr addr;
310     struct hostent *hp;
311     char  **name_list;
312     int     port;
313 
314     /*
315      * Validate the service.
316      */
317     if (service) {
318 	if ((port = find_service(service, socktype)) < 0)
319 	    return (EAI_SERVICE);
320     } else {
321 	port = 0;
322 	socktype = MAI_SOCKTYPE;
323     }
324 
325     /*
326      * No host means INADDR_ANY.
327      */
328     if (hostname == 0) {
329 	ip = (struct ipv4addrinfo *) mymalloc(sizeof(*ip));
330 	init_ipv4addrinfo(ip, socktype);
331 	ip->sin.sin_addr.s_addr = INADDR_ANY;
332 	ip->sin.sin_port = port;
333 	*res = &(ip->info);
334 	return (0);
335     }
336 
337     /*
338      * Numeric host.
339      */
340     if (inet_pton(AF_INET, hostname, (void *) &addr) == 1) {
341 	ip = (struct ipv4addrinfo *) mymalloc(sizeof(*ip));
342 	init_ipv4addrinfo(ip, socktype);
343 	ip->sin.sin_addr = addr;
344 	ip->sin.sin_port = port;
345 	*res = &(ip->info);
346 	return (0);
347     }
348 
349     /*
350      * Look up the IPv4 address list.
351      */
352     if ((hp = gethostbyname(hostname)) == 0)
353 	return (h_errno == TRY_AGAIN ? EAI_AGAIN : EAI_NODATA);
354     if (hp->h_addrtype != AF_INET
355 	|| hp->h_length != sizeof(template.sin.sin_addr))
356 	return (EAI_NODATA);
357 
358     /*
359      * Initialize the result template.
360      */
361     if (template.info.ai_addrlen == 0)
362 	init_ipv4addrinfo(&template, socktype);
363 
364     /*
365      * Copy the address information into an addrinfo structure.
366      */
367     prev = &template;
368     for (name_list = hp->h_addr_list; name_list[0]; name_list++) {
369 	ip = clone_ipv4addrinfo(prev);
370 	ip->sin.sin_addr = IN_ADDR(name_list[0]);
371 	ip->sin.sin_port = port;
372 	if (prev == &template)
373 	    *res = &(ip->info);
374 	else
375 	    prev->info.ai_next = &(ip->info);
376 	prev = ip;
377     }
378     return (0);
379 #else
380 
381     /*
382      * Native getaddrinfo(3) version.
383      *
384      * XXX Wild-card listener issues.
385      *
386      * With most IPv4 plus IPv6 systems, an IPv6 wild-card listener also listens
387      * on the IPv4 wild-card address. Connections from IPv4 clients appear as
388      * IPv4-in-IPv6 addresses; when Postfix support for IPv4 is turned on,
389      * Postfix automatically maps these embedded addresses to their original
390      * IPv4 form. So everything seems to be fine.
391      *
392      * However, some applications prefer to use separate listener sockets for
393      * IPv4 and IPv6. The Postfix IPv6 patch provided such an example. And
394      * this is where things become tricky. On many systems the IPv6 and IPv4
395      * wild-card listeners cannot coexist. When one is already active, the
396      * other fails with EADDRINUSE. Solaris 9, however, will automagically
397      * "do the right thing" and allow both listeners to coexist.
398      *
399      * Recent systems have the IPV6_V6ONLY feature (RFC 3493), which tells the
400      * system that we really mean IPv6 when we say IPv6. This allows us to
401      * set up separate wild-card listener sockets for IPv4 and IPv6. So
402      * everything seems to be fine again.
403      *
404      * The following workaround disables the wild-card IPv4 listener when
405      * IPV6_V6ONLY is unavailable. This is necessary for some Linux versions,
406      * but is not needed for Solaris 9 (which allows IPv4 and IPv6 wild-card
407      * listeners to coexist). Solaris 10 beta already has IPV6_V6ONLY.
408      *
409      * XXX This workaround obviously breaks if we want to support protocols in
410      * addition to IPv6 and IPv4, but it is needed only until IPv6
411      * implementations catch up with RFC 3493. A nicer fix is to filter the
412      * getaddrinfo() result, and to return a vector of addrinfo pointers to
413      * only those types of elements that the caller has expressed interested
414      * in.
415      *
416      * XXX Vanilla AIX 5.1 getaddrinfo() does not support a null hostname with
417      * AI_PASSIVE. And since we don't know how getaddrinfo() manages its
418      * memory we can't bypass it for this special case, or freeaddrinfo()
419      * might blow up. Instead we turn off IPV6_V6ONLY in inet_listen(), and
420      * supply a protocol-dependent hard-coded string value to getaddrinfo()
421      * below, so that it will convert into the appropriate wild-card address.
422      *
423      * XXX AIX 5.[1-3] getaddrinfo() may return a non-null port when a null
424      * service argument is specified.
425      */
426     struct addrinfo hints;
427     int     err;
428 
429     memset((char *) &hints, 0, sizeof(hints));
430     hints.ai_family = (pf != PF_UNSPEC) ? pf : inet_proto_info()->ai_family;
431     hints.ai_socktype = service ? socktype : MAI_SOCKTYPE;
432     if (!hostname) {
433 	hints.ai_flags = AI_PASSIVE;
434 #if !defined(IPV6_V6ONLY) || defined(BROKEN_AI_PASSIVE_NULL_HOST)
435 	switch (hints.ai_family) {
436 	case PF_UNSPEC:
437 	    hints.ai_family = PF_INET6;
438 #ifdef BROKEN_AI_PASSIVE_NULL_HOST
439 	case PF_INET6:
440 	    hostname = "::";
441 	    break;
442 	case PF_INET:
443 	    hostname = "0.0.0.0";
444 	    break;
445 #endif
446 	}
447 #endif
448     }
449     err = getaddrinfo(hostname, service, &hints, res);
450 #if defined(BROKEN_AI_NULL_SERVICE)
451     if (service == 0 && err == 0) {
452 	struct addrinfo *r;
453 	unsigned short *portp;
454 
455 	for (r = *res; r != 0; r = r->ai_next)
456 	    if (*(portp = SOCK_ADDR_PORTP(r->ai_addr)) != 0)
457 		*portp = 0;
458     }
459 #endif
460     return (err);
461 #endif
462 }
463 
464 /* hostaddr_to_sockaddr - printable address to binary address form */
465 
466 int     hostaddr_to_sockaddr(const char *hostaddr, const char *service,
467 			             int socktype, struct addrinfo ** res)
468 {
469 #ifdef EMULATE_IPV4_ADDRINFO
470 
471     /*
472      * Emulated getaddrinfo(3) version.
473      */
474     struct ipv4addrinfo *ip;
475     struct in_addr addr;
476     int     port;
477 
478     /*
479      * Validate the service.
480      */
481     if (service) {
482 	if ((port = find_service(service, socktype)) < 0)
483 	    return (EAI_SERVICE);
484     } else {
485 	port = 0;
486 	socktype = MAI_SOCKTYPE;
487     }
488 
489     /*
490      * No host means INADDR_ANY.
491      */
492     if (hostaddr == 0) {
493 	ip = (struct ipv4addrinfo *) mymalloc(sizeof(*ip));
494 	init_ipv4addrinfo(ip, socktype);
495 	ip->sin.sin_addr.s_addr = INADDR_ANY;
496 	ip->sin.sin_port = port;
497 	*res = &(ip->info);
498 	return (0);
499     }
500 
501     /*
502      * Deal with bad address forms.
503      */
504     switch (inet_pton(AF_INET, hostaddr, (void *) &addr)) {
505     case 1:					/* Success */
506 	break;
507     default:					/* Unparsable */
508 	return (EAI_NONAME);
509     case -1:					/* See errno */
510 	return (EAI_SYSTEM);
511     }
512 
513     /*
514      * Initialize the result structure.
515      */
516     ip = (struct ipv4addrinfo *) mymalloc(sizeof(*ip));
517     init_ipv4addrinfo(ip, socktype);
518 
519     /*
520      * And copy the result.
521      */
522     ip->sin.sin_addr = addr;
523     ip->sin.sin_port = port;
524     *res = &(ip->info);
525 
526     return (0);
527 #else
528 
529     /*
530      * Native getaddrinfo(3) version. See comments in hostname_to_sockaddr().
531      *
532      * XXX Vanilla AIX 5.1 getaddrinfo() returns multiple results when
533      * converting a printable ipv4 or ipv6 address to socket address with
534      * ai_family=PF_UNSPEC, ai_flags=AI_NUMERICHOST, ai_socktype=SOCK_STREAM,
535      * ai_protocol=0 or IPPROTO_TCP, and service=0. The workaround is to
536      * ignore all but the first result.
537      *
538      * XXX AIX 5.[1-3] getaddrinfo() may return a non-null port when a null
539      * service argument is specified.
540      */
541     struct addrinfo hints;
542     int     err;
543 
544     memset(&hints, 0, sizeof(hints));
545     hints.ai_family = inet_proto_info()->ai_family;
546     hints.ai_socktype = service ? socktype : MAI_SOCKTYPE;
547     hints.ai_flags = AI_NUMERICHOST;
548     if (!hostaddr) {
549 	hints.ai_flags |= AI_PASSIVE;
550 #if !defined(IPV6_V6ONLY) || defined(BROKEN_AI_PASSIVE_NULL_HOST)
551 	switch (hints.ai_family) {
552 	case PF_UNSPEC:
553 	    hints.ai_family = PF_INET6;
554 #ifdef BROKEN_AI_PASSIVE_NULL_HOST
555 	case PF_INET6:
556 	    hostaddr = "::";
557 	    break;
558 	case PF_INET:
559 	    hostaddr = "0.0.0.0";
560 	    break;
561 #endif
562 	}
563 #endif
564     }
565     err = getaddrinfo(hostaddr, service, &hints, res);
566 #if defined(BROKEN_AI_NULL_SERVICE)
567     if (service == 0 && err == 0) {
568 	struct addrinfo *r;
569 	unsigned short *portp;
570 
571 	for (r = *res; r != 0; r = r->ai_next)
572 	    if (*(portp = SOCK_ADDR_PORTP(r->ai_addr)) != 0)
573 		*portp = 0;
574     }
575 #endif
576     return (err);
577 #endif
578 }
579 
580 /* sockaddr_to_hostaddr - binary address to printable address form */
581 
582 int     sockaddr_to_hostaddr(const struct sockaddr * sa, SOCKADDR_SIZE salen,
583 			             MAI_HOSTADDR_STR *hostaddr,
584 			             MAI_SERVPORT_STR *portnum,
585 			             int unused_socktype)
586 {
587 #ifdef EMULATE_IPV4_ADDRINFO
588     char    portbuf[sizeof("65535")];
589     ssize_t len;
590 
591     /*
592      * Emulated getnameinfo(3) version. The buffer length includes the space
593      * for the null terminator.
594      */
595     if (sa->sa_family != AF_INET) {
596 	errno = EAFNOSUPPORT;
597 	return (EAI_SYSTEM);
598     }
599     if (hostaddr != 0) {
600 	if (inet_ntop(AF_INET, (void *) &(SOCK_ADDR_IN_ADDR(sa)),
601 		      hostaddr->buf, sizeof(hostaddr->buf)) == 0)
602 	    return (EAI_SYSTEM);
603     }
604     if (portnum != 0) {
605 	sprintf(portbuf, "%d", ntohs(SOCK_ADDR_IN_PORT(sa)) & 0xffff);
606 	if ((len = strlen(portbuf)) >= sizeof(portnum->buf)) {
607 	    errno = ENOSPC;
608 	    return (EAI_SYSTEM);
609 	}
610 	memcpy(portnum->buf, portbuf, len + 1);
611     }
612     return (0);
613 #else
614     int     ret;
615 
616     /*
617      * Native getnameinfo(3) version.
618      */
619     ret = getnameinfo(sa, salen,
620 		      hostaddr ? hostaddr->buf : (char *) 0,
621 		      hostaddr ? sizeof(hostaddr->buf) : 0,
622 		      portnum ? portnum->buf : (char *) 0,
623 		      portnum ? sizeof(portnum->buf) : 0,
624 		      NI_NUMERICHOST | NI_NUMERICSERV);
625     if (hostaddr != 0 && ret == 0 && sa->sa_family == AF_INET6)
626 	(void) split_at(hostaddr->buf, '%');
627     return (ret);
628 #endif
629 }
630 
631 /* sockaddr_to_hostname - binary address to printable hostname */
632 
633 int     sockaddr_to_hostname(const struct sockaddr * sa, SOCKADDR_SIZE salen,
634 			             MAI_HOSTNAME_STR *hostname,
635 			             MAI_SERVNAME_STR *service,
636 			             int socktype)
637 {
638 #ifdef EMULATE_IPV4_ADDRINFO
639 
640     /*
641      * Emulated getnameinfo(3) version.
642      */
643     struct hostent *hp;
644     struct servent *sp;
645     size_t  len;
646 
647     /*
648      * Sanity check.
649      */
650     if (sa->sa_family != AF_INET)
651 	return (EAI_NODATA);
652 
653     /*
654      * Look up the host name.
655      */
656     if (hostname != 0) {
657 	if ((hp = gethostbyaddr((char *) &(SOCK_ADDR_IN_ADDR(sa)),
658 				sizeof(SOCK_ADDR_IN_ADDR(sa)),
659 				AF_INET)) == 0)
660 	    return (h_errno == TRY_AGAIN ? EAI_AGAIN : EAI_NONAME);
661 
662 	/*
663 	 * Save the result. The buffer length includes the space for the null
664 	 * terminator. Hostname sanity checks are at the end of this
665 	 * function.
666 	 */
667 	if ((len = strlen(hp->h_name)) >= sizeof(hostname->buf)) {
668 	    errno = ENOSPC;
669 	    return (EAI_SYSTEM);
670 	}
671 	memcpy(hostname->buf, hp->h_name, len + 1);
672     }
673 
674     /*
675      * Look up the service.
676      */
677     if (service != 0) {
678 	if ((sp = getservbyport(ntohs(SOCK_ADDR_IN_PORT(sa)),
679 			      socktype == SOCK_DGRAM ? "udp" : "tcp")) == 0)
680 	    return (EAI_NONAME);
681 
682 	/*
683 	 * Save the result. The buffer length includes the space for the null
684 	 * terminator.
685 	 */
686 	if ((len = strlen(sp->s_name)) >= sizeof(service->buf)) {
687 	    errno = ENOSPC;
688 	    return (EAI_SYSTEM);
689 	}
690 	memcpy(service->buf, sp->s_name, len + 1);
691     }
692 #else
693 
694     /*
695      * Native getnameinfo(3) version.
696      */
697     int     err;
698 
699     err = getnameinfo(sa, salen,
700 		      hostname ? hostname->buf : (char *) 0,
701 		      hostname ? sizeof(hostname->buf) : 0,
702 		      service ? service->buf : (char *) 0,
703 		      service ? sizeof(service->buf) : 0,
704 		      socktype == SOCK_DGRAM ?
705 		      NI_NAMEREQD | NI_DGRAM : NI_NAMEREQD);
706     if (err != 0)
707 	return (err);
708 #endif
709 
710     /*
711      * Hostname sanity checks.
712      */
713     if (hostname != 0) {
714 	if (valid_hostaddr(hostname->buf, DONT_GRIPE)) {
715 	    msg_warn("numeric hostname: %s", hostname->buf);
716 	    return (EAI_NONAME);
717 	}
718 	if (!valid_hostname(hostname->buf, DO_GRIPE))
719 	    return (EAI_NONAME);
720     }
721     return (0);
722 }
723 
724 /* myaddrinfo_control - fine control */
725 
726 void    myaddrinfo_control(int name,...)
727 {
728     const char *myname = "myaddrinfo_control";
729     va_list ap;
730 
731     for (va_start(ap, name); name != 0; name = va_arg(ap, int)) {
732 	switch (name) {
733 	default:
734 	    msg_panic("%s: bad name %d", myname, name);
735 	}
736     }
737     va_end(ap);
738 }
739 
740 #ifdef EMULATE_IPV4_ADDRINFO
741 
742 /* freeaddrinfo - release storage */
743 
744 void    freeaddrinfo(struct addrinfo * ai)
745 {
746     struct addrinfo *ap;
747     struct addrinfo *next;
748 
749     /*
750      * Artefact of implementation: tolerate a null pointer argument.
751      */
752     for (ap = ai; ap != 0; ap = next) {
753 	next = ap->ai_next;
754 	if (ap->ai_canonname)
755 	    myfree(ap->ai_canonname);
756 	/* ap->ai_addr is allocated within this memory block */
757 	myfree((char *) ap);
758     }
759 }
760 
761 static char *ai_errlist[] = {
762     "Success",
763     "Address family for hostname not supported",	/* EAI_ADDRFAMILY */
764     "Temporary failure in name resolution",	/* EAI_AGAIN	 */
765     "Invalid value for ai_flags",	/* EAI_BADFLAGS   */
766     "Non-recoverable failure in name resolution",	/* EAI_FAIL	 */
767     "ai_family not supported",		/* EAI_FAMILY     */
768     "Memory allocation failure",	/* EAI_MEMORY     */
769     "No address associated with hostname",	/* EAI_NODATA     */
770     "hostname nor servname provided, or not known",	/* EAI_NONAME     */
771     "service name not supported for ai_socktype",	/* EAI_SERVICE    */
772     "ai_socktype not supported",	/* EAI_SOCKTYPE   */
773     "System error returned in errno",	/* EAI_SYSTEM     */
774     "Invalid value for hints",		/* EAI_BADHINTS   */
775     "Resolved protocol is unknown",	/* EAI_PROTOCOL   */
776     "Unknown error",			/* EAI_MAX	  */
777 };
778 
779 /* gai_strerror - error number to string */
780 
781 char   *gai_strerror(int ecode)
782 {
783 
784     /*
785      * Note: EAI_SYSTEM errors are not automatically handed over to
786      * strerror(). The application decides.
787      */
788     if (ecode < 0 || ecode > EAI_MAX)
789 	ecode = EAI_MAX;
790     return (ai_errlist[ecode]);
791 }
792 
793 #endif
794 
795 #ifdef TEST
796 
797  /*
798   * A test program that takes some info from the command line and runs it
799   * forward and backward through the above conversion routines.
800   */
801 #include <msg.h>
802 #include <vstream.h>
803 #include <msg_vstream.h>
804 
805 int     main(int argc, char **argv)
806 {
807     struct addrinfo *info;
808     struct addrinfo *ip;
809     MAI_HOSTNAME_STR host;
810     MAI_HOSTADDR_STR addr;
811     int     err;
812 
813     msg_vstream_init(argv[0], VSTREAM_ERR);
814 
815     if (argc != 4)
816 	msg_fatal("usage: %s protocols hostname hostaddress", argv[0]);
817 
818     inet_proto_init(argv[0], argv[1]);
819 
820     msg_info("=== hostname %s ===", argv[2]);
821 
822     if ((err = hostname_to_sockaddr(argv[2], (char *) 0, 0, &info)) != 0) {
823 	msg_info("hostname_to_sockaddr(%s): %s",
824 	  argv[2], err == EAI_SYSTEM ? strerror(errno) : gai_strerror(err));
825     } else {
826 	for (ip = info; ip != 0; ip = ip->ai_next) {
827 	    if ((err = sockaddr_to_hostaddr(ip->ai_addr, ip->ai_addrlen, &addr,
828 					 (MAI_SERVPORT_STR *) 0, 0)) != 0) {
829 		msg_info("sockaddr_to_hostaddr: %s",
830 		   err == EAI_SYSTEM ? strerror(errno) : gai_strerror(err));
831 		continue;
832 	    }
833 	    msg_info("%s -> family=%d sock=%d proto=%d %s", argv[2],
834 		 ip->ai_family, ip->ai_socktype, ip->ai_protocol, addr.buf);
835 	    if ((err = sockaddr_to_hostname(ip->ai_addr, ip->ai_addrlen, &host,
836 					 (MAI_SERVNAME_STR *) 0, 0)) != 0) {
837 		msg_info("sockaddr_to_hostname: %s",
838 		   err == EAI_SYSTEM ? strerror(errno) : gai_strerror(err));
839 		continue;
840 	    }
841 	    msg_info("%s -> %s", addr.buf, host.buf);
842 	}
843 	freeaddrinfo(info);
844     }
845 
846     msg_info("=== host address %s ===", argv[3]);
847 
848     if ((err = hostaddr_to_sockaddr(argv[3], (char *) 0, 0, &ip)) != 0) {
849 	msg_info("hostaddr_to_sockaddr(%s): %s",
850 	  argv[3], err == EAI_SYSTEM ? strerror(errno) : gai_strerror(err));
851     } else {
852 	if ((err = sockaddr_to_hostaddr(ip->ai_addr, ip->ai_addrlen, &addr,
853 					(MAI_SERVPORT_STR *) 0, 0)) != 0) {
854 	    msg_info("sockaddr_to_hostaddr: %s",
855 		   err == EAI_SYSTEM ? strerror(errno) : gai_strerror(err));
856 	} else {
857 	    msg_info("%s -> family=%d sock=%d proto=%d %s", argv[3],
858 		 ip->ai_family, ip->ai_socktype, ip->ai_protocol, addr.buf);
859 	    if ((err = sockaddr_to_hostname(ip->ai_addr, ip->ai_addrlen, &host,
860 					 (MAI_SERVNAME_STR *) 0, 0)) != 0) {
861 		msg_info("sockaddr_to_hostname: %s",
862 		   err == EAI_SYSTEM ? strerror(errno) : gai_strerror(err));
863 	    } else
864 		msg_info("%s -> %s", addr.buf, host.buf);
865 	    freeaddrinfo(ip);
866 	}
867     }
868     exit(0);
869 }
870 
871 #endif
872