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