Patrick Welche <prlw1@cam.ac.uk>
[netbsd-mini2440.git] / external / ibm-public / postfix / dist / src / util / myaddrinfo.c
blobc7cbdc2e247850d39a2a819bbb2367ee3796a965
1 /* $NetBSD$ */
3 /*++
4 /* NAME
5 /* myaddrinfo 3
6 /* SUMMARY
7 /* addrinfo encapsulation and emulation
8 /* SYNOPSIS
9 /* #include <myaddrinfo.h>
11 /* #define MAI_V4ADDR_BITS ...
12 /* #define MAI_V6ADDR_BITS ...
13 /* #define MAI_V4ADDR_BYTES ...
14 /* #define MAI_V6ADDR_BYTES ...
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;
21 /* int hostname_to_sockaddr(hostname, service, socktype, result)
22 /* const char *hostname;
23 /* const char *service;
24 /* int socktype;
25 /* struct addrinfo **result;
27 /* int hostaddr_to_sockaddr(hostaddr, service, socktype, result)
28 /* const char *hostaddr;
29 /* const char *service;
30 /* int socktype;
31 /* struct addrinfo **result;
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;
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;
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).
55 /* On systems without getaddrinfo(3) and getnameinfo(3) support,
56 /* emulation for IPv4 only can be enabled by defining
57 /* EMULATE_IPV4_ADDRINFO.
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.
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.
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.
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.
80 /* MAI_STRERROR() is an unsafe macro (it evaluates the argument
81 /* multiple times) that invokes strerror() or gai_strerror()
82 /* as appropriate.
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.
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().
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.
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.
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.
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.
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 /*--*/
171 /* System library. */
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() */
184 /* Utility library. */
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>
194 /* Application-specific. */
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.
201 struct ipv4addrinfo {
202 struct addrinfo info;
203 struct sockaddr_in sin;
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.
212 #define MAI_SOCKTYPE SOCK_STREAM /* getaddrinfo() query */
214 #ifdef EMULATE_IPV4_ADDRINFO
216 /* clone_ipv4addrinfo - clone ipv4addrinfo structure */
218 static struct ipv4addrinfo *clone_ipv4addrinfo(struct ipv4addrinfo * tp)
220 struct ipv4addrinfo *ip;
222 ip = (struct ipv4addrinfo *) mymalloc(sizeof(*ip));
223 *ip = *tp;
224 ip->info.ai_addr = (struct sockaddr *) & (ip->sin);
225 return (ip);
228 /* init_ipv4addrinfo - initialize an ipv4addrinfo structure */
230 static void init_ipv4addrinfo(struct ipv4addrinfo * ip, int socktype)
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.
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
251 /* find_service - translate numeric or symbolic service name */
253 static int find_service(const char *service, int socktype)
255 struct servent *sp;
256 const char *proto;
257 unsigned port;
259 if (alldig(service)) {
260 port = atoi(service);
261 return (port < 65536 ? htons(port) : -1);
263 if (socktype == SOCK_STREAM) {
264 proto = "tcp";
265 } else if (socktype == SOCK_DGRAM) {
266 proto = "udp";
267 } else {
268 return (-1);
270 if ((sp = getservbyname(service, proto)) != 0) {
271 return (sp->s_port);
272 } else {
273 return (-1);
277 #endif
279 /* hostname_to_sockaddr - hostname to binary address form */
281 int hostname_to_sockaddr(const char *hostname, const char *service,
282 int socktype, struct addrinfo ** res)
284 #ifdef EMULATE_IPV4_ADDRINFO
287 * Emulated getaddrinfo(3) version.
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;
298 * Validate the service.
300 if (service) {
301 if ((port = find_service(service, socktype)) < 0)
302 return (EAI_SERVICE);
303 } else {
304 port = 0;
305 socktype = MAI_SOCKTYPE;
309 * No host means INADDR_ANY.
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);
321 * Numeric host.
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);
333 * Look up the IPv4 address list.
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);
342 * Initialize the result template.
344 if (template.info.ai_addrlen == 0)
345 init_ipv4addrinfo(&template, socktype);
348 * Copy the address information into an addrinfo structure.
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;
361 return (0);
362 #else
365 * Native getaddrinfo(3) version.
367 * XXX Wild-card listener issues.
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.
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.
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.
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.
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.
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.
406 * XXX AIX 5.[1-3] getaddrinfo() may return a non-null port when a null
407 * service argument is specified.
409 struct addrinfo hints;
410 int err;
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
430 #endif
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;
438 for (r = *res; r != 0; r = r->ai_next)
439 if (*(portp = SOCK_ADDR_PORTP(r->ai_addr)) != 0)
440 *portp = 0;
442 #endif
443 return (err);
444 #endif
447 /* hostaddr_to_sockaddr - printable address to binary address form */
449 int hostaddr_to_sockaddr(const char *hostaddr, const char *service,
450 int socktype, struct addrinfo ** res)
452 #ifdef EMULATE_IPV4_ADDRINFO
455 * Emulated getaddrinfo(3) version.
457 struct ipv4addrinfo *ip;
458 struct in_addr addr;
459 int port;
462 * Validate the service.
464 if (service) {
465 if ((port = find_service(service, socktype)) < 0)
466 return (EAI_SERVICE);
467 } else {
468 port = 0;
469 socktype = MAI_SOCKTYPE;
473 * No host means INADDR_ANY.
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);
485 * Deal with bad address forms.
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);
497 * Initialize the result structure.
499 ip = (struct ipv4addrinfo *) mymalloc(sizeof(*ip));
500 init_ipv4addrinfo(ip, socktype);
503 * And copy the result.
505 ip->sin.sin_addr = addr;
506 ip->sin.sin_port = port;
507 *res = &(ip->info);
509 return (0);
510 #else
513 * Native getaddrinfo(3) version. See comments in hostname_to_sockaddr().
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.
521 * XXX AIX 5.[1-3] getaddrinfo() may return a non-null port when a null
522 * service argument is specified.
524 struct addrinfo hints;
525 int err;
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
546 #endif
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;
554 for (r = *res; r != 0; r = r->ai_next)
555 if (*(portp = SOCK_ADDR_PORTP(r->ai_addr)) != 0)
556 *portp = 0;
558 #endif
559 return (err);
560 #endif
563 /* sockaddr_to_hostaddr - binary address to printable address form */
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)
570 #ifdef EMULATE_IPV4_ADDRINFO
571 char portbuf[sizeof("65535")];
572 ssize_t len;
575 * Emulated getnameinfo(3) version. The buffer length includes the space
576 * for the null terminator.
578 if (sa->sa_family != AF_INET) {
579 errno = EAFNOSUPPORT;
580 return (EAI_SYSTEM);
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);
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);
593 memcpy(portnum->buf, portbuf, len + 1);
595 return (0);
596 #else
599 * Native getnameinfo(3) version.
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
610 /* sockaddr_to_hostname - binary address to printable hostname */
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)
617 #ifdef EMULATE_IPV4_ADDRINFO
620 * Emulated getnameinfo(3) version.
622 struct hostent *hp;
623 struct servent *sp;
624 size_t len;
627 * Sanity check.
629 if (sa->sa_family != AF_INET)
630 return (EAI_NODATA);
633 * Look up the host name.
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);
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.
646 if ((len = strlen(hp->h_name)) >= sizeof(hostname->buf)) {
647 errno = ENOSPC;
648 return (EAI_SYSTEM);
650 memcpy(hostname->buf, hp->h_name, len + 1);
654 * Look up the service.
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);
662 * Save the result. The buffer length includes the space for the null
663 * terminator.
665 if ((len = strlen(sp->s_name)) >= sizeof(service->buf)) {
666 errno = ENOSPC;
667 return (EAI_SYSTEM);
669 memcpy(service->buf, sp->s_name, len + 1);
671 #else
674 * Native getnameinfo(3) version.
676 int err;
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
690 * Hostname sanity checks.
692 if (hostname != 0) {
693 if (valid_hostaddr(hostname->buf, DONT_GRIPE)) {
694 msg_warn("numeric hostname: %s", hostname->buf);
695 return (EAI_NONAME);
697 if (!valid_hostname(hostname->buf, DO_GRIPE))
698 return (EAI_NONAME);
700 return (0);
703 /* myaddrinfo_control - fine control */
705 void myaddrinfo_control(int name,...)
707 const char *myname = "myaddrinfo_control";
708 va_list ap;
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);
716 va_end(ap);
719 #ifdef EMULATE_IPV4_ADDRINFO
721 /* freeaddrinfo - release storage */
723 void freeaddrinfo(struct addrinfo * ai)
725 struct addrinfo *ap;
726 struct addrinfo *next;
729 * Artefact of implementation: tolerate a null pointer argument.
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);
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 */
758 /* gai_strerror - error number to string */
760 char *gai_strerror(int ecode)
764 * Note: EAI_SYSTEM errors are not automatically handed over to
765 * strerror(). The application decides.
767 if (ecode < 0 || ecode > EAI_MAX)
768 ecode = EAI_MAX;
769 return (ai_errlist[ecode]);
772 #endif
774 #ifdef TEST
777 * A test program that takes some info from the command line and runs it
778 * forward and backward through the above conversion routines.
780 #include <msg.h>
781 #include <vstream.h>
782 #include <msg_vstream.h>
784 int main(int argc, char **argv)
786 struct addrinfo *info;
787 struct addrinfo *ip;
788 MAI_HOSTNAME_STR host;
789 MAI_HOSTADDR_STR addr;
790 int err;
792 msg_vstream_init(argv[0], VSTREAM_ERR);
794 if (argc != 4)
795 msg_fatal("usage: %s protocols hostname hostaddress", argv[0]);
797 inet_proto_init(argv[0], argv[1]);
799 msg_info("=== hostname %s ===", argv[2]);
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;
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;
820 msg_info("%s -> %s", addr.buf, host.buf);
822 freeaddrinfo(info);
825 msg_info("=== host address %s ===", argv[3]);
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);
847 exit(0);
850 #endif