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