1/*	$NetBSD: myaddrinfo.c,v 1.3 2022/10/08 16:12:50 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  */
223struct 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
240static 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
252static 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
275static 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
304int     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
478int     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
599int     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
650int     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
743void    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
761void    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
778static 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
798char   *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
823static 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
831int     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    size_t  len, n;
839    int     err;
840
841    msg_vstream_init(argv[0], VSTREAM_ERR);
842
843    if (argc != 4)
844	msg_fatal("usage: %s protocols hostname hostaddress", argv[0]);
845
846    inet_proto_init(argv[0], argv[1]);
847
848    msg_info("=== hostname %s ===", argv[2]);
849
850    if ((err = hostname_to_sockaddr(argv[2], (char *) 0, 0, &info)) != 0) {
851	msg_info("hostname_to_sockaddr(%s): %s",
852	  argv[2], err == EAI_SYSTEM ? strerror(errno) : gai_strerror(err));
853    } else {
854	for (len = 0, ip = info; ip != 0; ip = ip->ai_next)
855	    len += 1;
856	resv = (struct addrinfo **) mymalloc(len * sizeof(*resv));
857	for (len = 0, ip = info; ip != 0; ip = ip->ai_next)
858	    resv[len++] = ip;
859	qsort((void *) resv, len, sizeof(*resv), compare_family);
860	for (n = 0; n < len; n++) {
861	    ip = resv[n];
862	    if ((err = sockaddr_to_hostaddr(ip->ai_addr, ip->ai_addrlen, &addr,
863					 (MAI_SERVPORT_STR *) 0, 0)) != 0) {
864		msg_info("sockaddr_to_hostaddr: %s",
865		   err == EAI_SYSTEM ? strerror(errno) : gai_strerror(err));
866		continue;
867	    }
868	    msg_info("%s -> family=%d sock=%d proto=%d %s", argv[2],
869		 ip->ai_family, ip->ai_socktype, ip->ai_protocol, addr.buf);
870	    if ((err = sockaddr_to_hostname(ip->ai_addr, ip->ai_addrlen, &host,
871					 (MAI_SERVNAME_STR *) 0, 0)) != 0) {
872		msg_info("sockaddr_to_hostname: %s",
873		   err == EAI_SYSTEM ? strerror(errno) : gai_strerror(err));
874		continue;
875	    }
876	    msg_info("%s -> %s", addr.buf, host.buf);
877	}
878	freeaddrinfo(info);
879	myfree((void *) resv);
880    }
881
882    msg_info("=== host address %s ===", argv[3]);
883
884    if ((err = hostaddr_to_sockaddr(argv[3], (char *) 0, 0, &ip)) != 0) {
885	msg_info("hostaddr_to_sockaddr(%s): %s",
886	  argv[3], err == EAI_SYSTEM ? strerror(errno) : gai_strerror(err));
887    } else {
888	if ((err = sockaddr_to_hostaddr(ip->ai_addr, ip->ai_addrlen, &addr,
889					(MAI_SERVPORT_STR *) 0, 0)) != 0) {
890	    msg_info("sockaddr_to_hostaddr: %s",
891		   err == EAI_SYSTEM ? strerror(errno) : gai_strerror(err));
892	} else {
893	    msg_info("%s -> family=%d sock=%d proto=%d %s", argv[3],
894		 ip->ai_family, ip->ai_socktype, ip->ai_protocol, addr.buf);
895	    if ((err = sockaddr_to_hostname(ip->ai_addr, ip->ai_addrlen, &host,
896					 (MAI_SERVNAME_STR *) 0, 0)) != 0) {
897		msg_info("sockaddr_to_hostname: %s",
898		   err == EAI_SYSTEM ? strerror(errno) : gai_strerror(err));
899	    } else
900		msg_info("%s -> %s", addr.buf, host.buf);
901	    freeaddrinfo(ip);
902	}
903    }
904    exit(0);
905}
906
907#endif
908