/*++ /* NAME /* myaddrinfo 3 /* SUMMARY /* addrinfo encapsulation and emulation /* SYNOPSIS /* #include /* /* #define MAI_V4ADDR_BITS ... /* #define MAI_V6ADDR_BITS ... /* #define MAI_V4ADDR_BYTES ... /* #define MAI_V6ADDR_BYTES ... /* /* typedef struct { char buf[....]; } MAI_HOSTNAME_STR; /* typedef struct { char buf[....]; } MAI_HOSTADDR_STR; /* typedef struct { char buf[....]; } MAI_SERVNAME_STR; /* typedef struct { char buf[....]; } MAI_SERVPORT_STR; /* /* int hostname_to_sockaddr(hostname, service, socktype, result) /* const char *hostname; /* const char *service; /* int socktype; /* struct addrinfo **result; /* /* int hostname_to_sockaddr_pf(hostname, pf, service, socktype, result) /* const char *hostname; /* int pf; /* const char *service; /* int socktype; /* struct addrinfo **result; /* /* int hostaddr_to_sockaddr(hostaddr, service, socktype, result) /* const char *hostaddr; /* const char *service; /* int socktype; /* struct addrinfo **result; /* /* int sockaddr_to_hostaddr(sa, salen, hostaddr, portnum, socktype) /* const struct sockaddr *sa; /* SOCKADDR_SIZE salen; /* MAI_HOSTADDR_STR *hostaddr; /* MAI_SERVPORT_STR *portnum; /* int socktype; /* /* int sockaddr_to_hostname(sa, salen, hostname, service, socktype) /* const struct sockaddr *sa; /* SOCKADDR_SIZE salen; /* MAI_HOSTNAME_STR *hostname; /* MAI_SERVNAME_STR *service; /* int socktype; /* /* const char *MAI_STRERROR(error) /* int error; /* DESCRIPTION /* This module provides a simplified user interface to the /* getaddrinfo(3) and getnameinfo(3) routines (which provide /* a unified interface to manipulate IPv4 and IPv6 socket /* address structures). /* /* On systems without getaddrinfo(3) and getnameinfo(3) support, /* emulation for IPv4 only can be enabled by defining /* EMULATE_IPV4_ADDRINFO. /* /* hostname_to_sockaddr() looks up the binary addresses for /* the specified symbolic hostname or numeric address. The /* result should be destroyed with freeaddrinfo(). A null host /* pointer converts to the null host address. /* /* hostname_to_sockaddr_pf() is an extended interface that /* provides a protocol family override. /* /* hostaddr_to_sockaddr() converts a printable network address /* into the corresponding binary form. The result should be /* destroyed with freeaddrinfo(). A null host pointer converts /* to the null host address. /* /* sockaddr_to_hostaddr() converts a binary network address /* into printable form. The result buffers should be large /* enough to hold the printable address or port including the /* null terminator. /* This function strips off the IPv6 datalink suffix. /* /* sockaddr_to_hostname() converts a binary network address /* into a hostname or service. The result buffer should be /* large enough to hold the hostname or service including the /* null terminator. This routine rejects malformed hostnames /* or numeric hostnames and pretends that the lookup failed. /* /* MAI_STRERROR() is an unsafe macro (it evaluates the argument /* multiple times) that invokes strerror() or gai_strerror() /* as appropriate. /* /* This module exports the following constants that should be /* user for storage allocation of name or address information: /* .IP MAI_V4ADDR_BITS /* .IP MAI_V6ADDR_BITS /* .IP MAI_V4ADDR_BYTES /* .IP MAI_V6ADDR_BYTES /* The number of bits or bytes needed to store a binary /* IPv4 or IPv6 network address. /* .PP /* The types MAI_HOST{NAME,ADDR}_STR and MAI_SERV{NAME,PORT}_STR /* implement buffers for the storage of the string representations /* of symbolic or numerical hosts or services. Do not use /* buffer types other than the ones that are expected here, /* or things will blow up with buffer overflow problems. /* /* Arguments: /* .IP hostname /* On input to hostname_to_sockaddr(), a numeric or symbolic /* hostname, or a null pointer (meaning the wild-card listen /* address). On output from sockaddr_to_hostname(), storage /* for the result hostname, or a null pointer. /* .IP pf /* Protocol type: PF_UNSPEC (meaning: use any protocol that is /* available), PF_INET, or PF_INET6. This argument is ignored /* in EMULATE_IPV4_ADDRINFO mode. /* .IP hostaddr /* On input to hostaddr_to_sockaddr(), a numeric hostname, /* or a null pointer (meaning the wild-card listen address). /* On output from sockaddr_to_hostaddr(), storage for the /* result hostaddress, or a null pointer. /* .IP service /* On input to hostname/addr_to_sockaddr(), a numeric or /* symbolic service name, or a null pointer in which case the /* socktype argument is ignored. On output from /* sockaddr_to_hostname/addr(), storage for the result service /* name, or a null pointer. /* .IP portnum /* Storage for the result service port number, or a null pointer. /* .IP socktype /* Socket type: SOCK_STREAM, SOCK_DGRAM, etc. This argument is /* ignored when no service or port are specified. /* .IP sa /* Protocol-independent socket address structure. /* .IP salen /* Protocol-dependent socket address structure size in bytes. /* SEE ALSO /* getaddrinfo(3), getnameinfo(3), freeaddrinfo(3), gai_strerror(3) /* DIAGNOSTICS /* All routines either return 0 upon success, or an error code /* that is compatible with gai_strerror(). /* /* On systems where addrinfo support is emulated by Postfix, /* some out-of-memory errors are not reported to the caller, /* but are handled by mymalloc(). /* BUGS /* The IPv4-only emulation code does not support requests that /* specify a service but no socket type. It returns an error /* indication, instead of enumerating all the possible answers. /* /* The hostname/addr_to_sockaddr() routines should accept a /* list of address families that the caller is interested in, /* and they should return only information of those types. /* /* Unfortunately, it is not possible to remove unwanted address /* family results from hostname_to_sockaddr(), because we /* don't know how the system library routine getaddrinfo() /* allocates memory. For example, getaddrinfo() could save /* space by referencing the same string object from multiple /* addrinfo structures; or it could allocate a string object /* and the addrinfo structure as one memory block. /* /* We could get around this by copying getaddrinfo() results /* to our own private data structures, but that would only /* make an already expensive API even more expensive. /* /* A better workaround is to return a vector of addrinfo /* pointers to the elements that contain only the elements /* that the caller is interested in. The pointer to the /* original getaddrinfo() result can be hidden at the end /* after the null terminator, or before the first element. /* LICENSE /* .ad /* .fi /* The Secure Mailer license must be distributed with this software. /* AUTHOR(S) /* Wietse Venema /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA /*--*/ /* System library. */ #include #include #include #include #include #include #include #include #include #include /* sprintf() */ /* Utility library. */ #include #include #include #include #include #include #include #include /* Application-specific. */ /* * Use an old trick to save some space: allocate space for two objects in * one. In Postfix we often use this trick for structures that have an array * of things at the end. */ struct ipv4addrinfo { struct addrinfo info; struct sockaddr_in sin; }; /* * When we're not interested in service ports, we must pick a socket type * otherwise getaddrinfo() will give us duplicate results: one set for TCP, * and another set for UDP. For consistency, we'll use the same default * socket type for the results from emulation mode. */ #define MAI_SOCKTYPE SOCK_STREAM /* getaddrinfo() query */ #ifdef EMULATE_IPV4_ADDRINFO /* clone_ipv4addrinfo - clone ipv4addrinfo structure */ static struct ipv4addrinfo *clone_ipv4addrinfo(struct ipv4addrinfo * tp) { struct ipv4addrinfo *ip; ip = (struct ipv4addrinfo *) mymalloc(sizeof(*ip)); *ip = *tp; ip->info.ai_addr = (struct sockaddr *) &(ip->sin); return (ip); } /* init_ipv4addrinfo - initialize an ipv4addrinfo structure */ static void init_ipv4addrinfo(struct ipv4addrinfo * ip, int socktype) { /* * Portability: null pointers aren't necessarily all-zero bits, so we * make explicit assignments to all the pointers that we're aware of. */ memset((void *) ip, 0, sizeof(*ip)); ip->info.ai_family = PF_INET; ip->info.ai_socktype = socktype; ip->info.ai_protocol = 0; /* XXX */ ip->info.ai_addrlen = sizeof(ip->sin); ip->info.ai_canonname = 0; ip->info.ai_addr = (struct sockaddr *) &(ip->sin); ip->info.ai_next = 0; ip->sin.sin_family = AF_INET; #ifdef HAS_SA_LEN ip->sin.sin_len = sizeof(ip->sin); #endif } /* find_service - translate numeric or symbolic service name */ static int find_service(const char *service, int socktype) { struct servent *sp; const char *proto; unsigned port; if (alldig(service)) { port = atoi(service); return (port < 65536 ? htons(port) : -1); } if (socktype == SOCK_STREAM) { proto = "tcp"; } else if (socktype == SOCK_DGRAM) { proto = "udp"; } else { return (-1); } if ((sp = getservbyname(service, proto)) != 0) { return (sp->s_port); } else { return (-1); } } #endif /* hostname_to_sockaddr_pf - hostname to binary address form */ int hostname_to_sockaddr_pf(const char *hostname, int pf, const char *service, int socktype, struct addrinfo ** res) { #ifdef EMULATE_IPV4_ADDRINFO /* * Emulated getaddrinfo(3) version. */ static struct ipv4addrinfo template; struct ipv4addrinfo *ip; struct ipv4addrinfo *prev; struct in_addr addr; struct hostent *hp; char **name_list; int port; /* * Validate the service. */ if (service) { if ((port = find_service(service, socktype)) < 0) return (EAI_SERVICE); } else { port = 0; socktype = MAI_SOCKTYPE; } /* * No host means INADDR_ANY. */ if (hostname == 0) { ip = (struct ipv4addrinfo *) mymalloc(sizeof(*ip)); init_ipv4addrinfo(ip, socktype); ip->sin.sin_addr.s_addr = INADDR_ANY; ip->sin.sin_port = port; *res = &(ip->info); return (0); } /* * Numeric host. */ if (inet_pton(AF_INET, hostname, (void *) &addr) == 1) { ip = (struct ipv4addrinfo *) mymalloc(sizeof(*ip)); init_ipv4addrinfo(ip, socktype); ip->sin.sin_addr = addr; ip->sin.sin_port = port; *res = &(ip->info); return (0); } /* * Look up the IPv4 address list. */ if ((hp = gethostbyname(hostname)) == 0) return (h_errno == TRY_AGAIN ? EAI_AGAIN : EAI_NODATA); if (hp->h_addrtype != AF_INET || hp->h_length != sizeof(template.sin.sin_addr)) return (EAI_NODATA); /* * Initialize the result template. */ if (template.info.ai_addrlen == 0) init_ipv4addrinfo(&template, socktype); /* * Copy the address information into an addrinfo structure. */ prev = &template; for (name_list = hp->h_addr_list; name_list[0]; name_list++) { ip = clone_ipv4addrinfo(prev); ip->sin.sin_addr = IN_ADDR(name_list[0]); ip->sin.sin_port = port; if (prev == &template) *res = &(ip->info); else prev->info.ai_next = &(ip->info); prev = ip; } return (0); #else /* * Native getaddrinfo(3) version. * * XXX Wild-card listener issues. * * With most IPv4 plus IPv6 systems, an IPv6 wild-card listener also listens * on the IPv4 wild-card address. Connections from IPv4 clients appear as * IPv4-in-IPv6 addresses; when Postfix support for IPv4 is turned on, * Postfix automatically maps these embedded addresses to their original * IPv4 form. So everything seems to be fine. * * However, some applications prefer to use separate listener sockets for * IPv4 and IPv6. The Postfix IPv6 patch provided such an example. And * this is where things become tricky. On many systems the IPv6 and IPv4 * wild-card listeners cannot coexist. When one is already active, the * other fails with EADDRINUSE. Solaris 9, however, will automagically * "do the right thing" and allow both listeners to coexist. * * Recent systems have the IPV6_V6ONLY feature (RFC 3493), which tells the * system that we really mean IPv6 when we say IPv6. This allows us to * set up separate wild-card listener sockets for IPv4 and IPv6. So * everything seems to be fine again. * * The following workaround disables the wild-card IPv4 listener when * IPV6_V6ONLY is unavailable. This is necessary for some Linux versions, * but is not needed for Solaris 9 (which allows IPv4 and IPv6 wild-card * listeners to coexist). Solaris 10 beta already has IPV6_V6ONLY. * * XXX This workaround obviously breaks if we want to support protocols in * addition to IPv6 and IPv4, but it is needed only until IPv6 * implementations catch up with RFC 3493. A nicer fix is to filter the * getaddrinfo() result, and to return a vector of addrinfo pointers to * only those types of elements that the caller has expressed interested * in. * * XXX Vanilla AIX 5.1 getaddrinfo() does not support a null hostname with * AI_PASSIVE. And since we don't know how getaddrinfo() manages its * memory we can't bypass it for this special case, or freeaddrinfo() * might blow up. Instead we turn off IPV6_V6ONLY in inet_listen(), and * supply a protocol-dependent hard-coded string value to getaddrinfo() * below, so that it will convert into the appropriate wild-card address. * * XXX AIX 5.[1-3] getaddrinfo() may return a non-null port when a null * service argument is specified. */ struct addrinfo hints; int err; memset((void *) &hints, 0, sizeof(hints)); hints.ai_family = (pf != PF_UNSPEC) ? pf : inet_proto_info()->ai_family; hints.ai_socktype = service ? socktype : MAI_SOCKTYPE; if (!hostname) { hints.ai_flags = AI_PASSIVE; #if !defined(IPV6_V6ONLY) || defined(BROKEN_AI_PASSIVE_NULL_HOST) switch (hints.ai_family) { case PF_UNSPEC: hints.ai_family = PF_INET6; #ifdef BROKEN_AI_PASSIVE_NULL_HOST case PF_INET6: hostname = "::"; break; case PF_INET: hostname = "0.0.0.0"; break; #endif } #endif } err = getaddrinfo(hostname, service, &hints, res); #if defined(BROKEN_AI_NULL_SERVICE) if (service == 0 && err == 0) { struct addrinfo *r; unsigned short *portp; for (r = *res; r != 0; r = r->ai_next) if (*(portp = SOCK_ADDR_PORTP(r->ai_addr)) != 0) *portp = 0; } #endif return (err); #endif } /* hostaddr_to_sockaddr - printable address to binary address form */ int hostaddr_to_sockaddr(const char *hostaddr, const char *service, int socktype, struct addrinfo ** res) { #ifdef EMULATE_IPV4_ADDRINFO /* * Emulated getaddrinfo(3) version. */ struct ipv4addrinfo *ip; struct in_addr addr; int port; /* * Validate the service. */ if (service) { if ((port = find_service(service, socktype)) < 0) return (EAI_SERVICE); } else { port = 0; socktype = MAI_SOCKTYPE; } /* * No host means INADDR_ANY. */ if (hostaddr == 0) { ip = (struct ipv4addrinfo *) mymalloc(sizeof(*ip)); init_ipv4addrinfo(ip, socktype); ip->sin.sin_addr.s_addr = INADDR_ANY; ip->sin.sin_port = port; *res = &(ip->info); return (0); } /* * Deal with bad address forms. */ switch (inet_pton(AF_INET, hostaddr, (void *) &addr)) { case 1: /* Success */ break; default: /* Unparsable */ return (EAI_NONAME); case -1: /* See errno */ return (EAI_SYSTEM); } /* * Initialize the result structure. */ ip = (struct ipv4addrinfo *) mymalloc(sizeof(*ip)); init_ipv4addrinfo(ip, socktype); /* * And copy the result. */ ip->sin.sin_addr = addr; ip->sin.sin_port = port; *res = &(ip->info); return (0); #else /* * Native getaddrinfo(3) version. See comments in hostname_to_sockaddr(). * * XXX Vanilla AIX 5.1 getaddrinfo() returns multiple results when * converting a printable ipv4 or ipv6 address to socket address with * ai_family=PF_UNSPEC, ai_flags=AI_NUMERICHOST, ai_socktype=SOCK_STREAM, * ai_protocol=0 or IPPROTO_TCP, and service=0. The workaround is to * ignore all but the first result. * * XXX AIX 5.[1-3] getaddrinfo() may return a non-null port when a null * service argument is specified. */ struct addrinfo hints; int err; memset(&hints, 0, sizeof(hints)); hints.ai_family = inet_proto_info()->ai_family; hints.ai_socktype = service ? socktype : MAI_SOCKTYPE; hints.ai_flags = AI_NUMERICHOST; if (!hostaddr) { hints.ai_flags |= AI_PASSIVE; #if !defined(IPV6_V6ONLY) || defined(BROKEN_AI_PASSIVE_NULL_HOST) switch (hints.ai_family) { case PF_UNSPEC: hints.ai_family = PF_INET6; #ifdef BROKEN_AI_PASSIVE_NULL_HOST case PF_INET6: hostaddr = "::"; break; case PF_INET: hostaddr = "0.0.0.0"; break; #endif } #endif } err = getaddrinfo(hostaddr, service, &hints, res); #if defined(BROKEN_AI_NULL_SERVICE) if (service == 0 && err == 0) { struct addrinfo *r; unsigned short *portp; for (r = *res; r != 0; r = r->ai_next) if (*(portp = SOCK_ADDR_PORTP(r->ai_addr)) != 0) *portp = 0; } #endif return (err); #endif } /* sockaddr_to_hostaddr - binary address to printable address form */ int sockaddr_to_hostaddr(const struct sockaddr *sa, SOCKADDR_SIZE salen, MAI_HOSTADDR_STR *hostaddr, MAI_SERVPORT_STR *portnum, int unused_socktype) { #ifdef EMULATE_IPV4_ADDRINFO char portbuf[sizeof("65535")]; ssize_t len; /* * Emulated getnameinfo(3) version. The buffer length includes the space * for the null terminator. */ if (sa->sa_family != AF_INET) { errno = EAFNOSUPPORT; return (EAI_SYSTEM); } if (hostaddr != 0) { if (inet_ntop(AF_INET, (void *) &(SOCK_ADDR_IN_ADDR(sa)), hostaddr->buf, sizeof(hostaddr->buf)) == 0) return (EAI_SYSTEM); } if (portnum != 0) { sprintf(portbuf, "%d", ntohs(SOCK_ADDR_IN_PORT(sa)) & 0xffff); if ((len = strlen(portbuf)) >= sizeof(portnum->buf)) { errno = ENOSPC; return (EAI_SYSTEM); } memcpy(portnum->buf, portbuf, len + 1); } return (0); #else int ret; /* * Native getnameinfo(3) version. */ ret = getnameinfo(sa, salen, hostaddr ? hostaddr->buf : (char *) 0, hostaddr ? sizeof(hostaddr->buf) : 0, portnum ? portnum->buf : (char *) 0, portnum ? sizeof(portnum->buf) : 0, NI_NUMERICHOST | NI_NUMERICSERV); if (hostaddr != 0 && ret == 0 && sa->sa_family == AF_INET6) (void) split_at(hostaddr->buf, '%'); return (ret); #endif } /* sockaddr_to_hostname - binary address to printable hostname */ int sockaddr_to_hostname(const struct sockaddr *sa, SOCKADDR_SIZE salen, MAI_HOSTNAME_STR *hostname, MAI_SERVNAME_STR *service, int socktype) { #ifdef EMULATE_IPV4_ADDRINFO /* * Emulated getnameinfo(3) version. */ struct hostent *hp; struct servent *sp; size_t len; /* * Sanity check. */ if (sa->sa_family != AF_INET) return (EAI_NODATA); /* * Look up the host name. */ if (hostname != 0) { if ((hp = gethostbyaddr((char *) &(SOCK_ADDR_IN_ADDR(sa)), sizeof(SOCK_ADDR_IN_ADDR(sa)), AF_INET)) == 0) return (h_errno == TRY_AGAIN ? EAI_AGAIN : EAI_NONAME); /* * Save the result. The buffer length includes the space for the null * terminator. Hostname sanity checks are at the end of this * function. */ if ((len = strlen(hp->h_name)) >= sizeof(hostname->buf)) { errno = ENOSPC; return (EAI_SYSTEM); } memcpy(hostname->buf, hp->h_name, len + 1); } /* * Look up the service. */ if (service != 0) { if ((sp = getservbyport(ntohs(SOCK_ADDR_IN_PORT(sa)), socktype == SOCK_DGRAM ? "udp" : "tcp")) == 0) return (EAI_NONAME); /* * Save the result. The buffer length includes the space for the null * terminator. */ if ((len = strlen(sp->s_name)) >= sizeof(service->buf)) { errno = ENOSPC; return (EAI_SYSTEM); } memcpy(service->buf, sp->s_name, len + 1); } #else /* * Native getnameinfo(3) version. */ int err; err = getnameinfo(sa, salen, hostname ? hostname->buf : (char *) 0, hostname ? sizeof(hostname->buf) : 0, service ? service->buf : (char *) 0, service ? sizeof(service->buf) : 0, socktype == SOCK_DGRAM ? NI_NAMEREQD | NI_DGRAM : NI_NAMEREQD); if (err != 0) return (err); #endif /* * Hostname sanity checks. */ if (hostname != 0) { if (valid_hostaddr(hostname->buf, DONT_GRIPE)) { msg_warn("numeric hostname: %s", hostname->buf); return (EAI_NONAME); } if (!valid_hostname(hostname->buf, DO_GRIPE)) return (EAI_NONAME); } return (0); } /* myaddrinfo_control - fine control */ void myaddrinfo_control(int name,...) { const char *myname = "myaddrinfo_control"; va_list ap; for (va_start(ap, name); name != 0; name = va_arg(ap, int)) { switch (name) { default: msg_panic("%s: bad name %d", myname, name); } } va_end(ap); } #ifdef EMULATE_IPV4_ADDRINFO /* freeaddrinfo - release storage */ void freeaddrinfo(struct addrinfo * ai) { struct addrinfo *ap; struct addrinfo *next; /* * Artefact of implementation: tolerate a null pointer argument. */ for (ap = ai; ap != 0; ap = next) { next = ap->ai_next; if (ap->ai_canonname) myfree(ap->ai_canonname); /* ap->ai_addr is allocated within this memory block */ myfree((void *) ap); } } static char *ai_errlist[] = { "Success", "Address family for hostname not supported", /* EAI_ADDRFAMILY */ "Temporary failure in name resolution", /* EAI_AGAIN */ "Invalid value for ai_flags", /* EAI_BADFLAGS */ "Non-recoverable failure in name resolution", /* EAI_FAIL */ "ai_family not supported", /* EAI_FAMILY */ "Memory allocation failure", /* EAI_MEMORY */ "No address associated with hostname", /* EAI_NODATA */ "hostname nor servname provided, or not known", /* EAI_NONAME */ "service name not supported for ai_socktype", /* EAI_SERVICE */ "ai_socktype not supported", /* EAI_SOCKTYPE */ "System error returned in errno", /* EAI_SYSTEM */ "Invalid value for hints", /* EAI_BADHINTS */ "Resolved protocol is unknown", /* EAI_PROTOCOL */ "Unknown error", /* EAI_MAX */ }; /* gai_strerror - error number to string */ char *gai_strerror(int ecode) { /* * Note: EAI_SYSTEM errors are not automatically handed over to * strerror(). The application decides. */ if (ecode < 0 || ecode > EAI_MAX) ecode = EAI_MAX; return (ai_errlist[ecode]); } #endif #ifdef TEST /* * A test program that takes some info from the command line and runs it * forward and backward through the above conversion routines. */ #include #include #include #include static int compare_family(const void *a, const void *b) { struct addrinfo *resa = *(struct addrinfo **) a; struct addrinfo *resb = *(struct addrinfo **) b; return (resa->ai_family - resb->ai_family); } int main(int argc, char **argv) { struct addrinfo *info; struct addrinfo *ip; struct addrinfo **resv; MAI_HOSTNAME_STR host; MAI_HOSTADDR_STR addr; size_t len, n; int err; msg_vstream_init(argv[0], VSTREAM_ERR); if (argc != 4) msg_fatal("usage: %s protocols hostname hostaddress", argv[0]); inet_proto_init(argv[0], argv[1]); msg_info("=== hostname %s ===", argv[2]); if ((err = hostname_to_sockaddr(argv[2], (char *) 0, 0, &info)) != 0) { msg_info("hostname_to_sockaddr(%s): %s", argv[2], err == EAI_SYSTEM ? strerror(errno) : gai_strerror(err)); } else { for (len = 0, ip = info; ip != 0; ip = ip->ai_next) len += 1; resv = (struct addrinfo **) mymalloc(len * sizeof(*resv)); for (len = 0, ip = info; ip != 0; ip = ip->ai_next) resv[len++] = ip; qsort((void *) resv, len, sizeof(*resv), compare_family); for (n = 0; n < len; n++) { ip = resv[n]; if ((err = sockaddr_to_hostaddr(ip->ai_addr, ip->ai_addrlen, &addr, (MAI_SERVPORT_STR *) 0, 0)) != 0) { msg_info("sockaddr_to_hostaddr: %s", err == EAI_SYSTEM ? strerror(errno) : gai_strerror(err)); continue; } msg_info("%s -> family=%d sock=%d proto=%d %s", argv[2], ip->ai_family, ip->ai_socktype, ip->ai_protocol, addr.buf); if ((err = sockaddr_to_hostname(ip->ai_addr, ip->ai_addrlen, &host, (MAI_SERVNAME_STR *) 0, 0)) != 0) { msg_info("sockaddr_to_hostname: %s", err == EAI_SYSTEM ? strerror(errno) : gai_strerror(err)); continue; } msg_info("%s -> %s", addr.buf, host.buf); } freeaddrinfo(info); myfree((void *) resv); } msg_info("=== host address %s ===", argv[3]); if ((err = hostaddr_to_sockaddr(argv[3], (char *) 0, 0, &ip)) != 0) { msg_info("hostaddr_to_sockaddr(%s): %s", argv[3], err == EAI_SYSTEM ? strerror(errno) : gai_strerror(err)); } else { if ((err = sockaddr_to_hostaddr(ip->ai_addr, ip->ai_addrlen, &addr, (MAI_SERVPORT_STR *) 0, 0)) != 0) { msg_info("sockaddr_to_hostaddr: %s", err == EAI_SYSTEM ? strerror(errno) : gai_strerror(err)); } else { msg_info("%s -> family=%d sock=%d proto=%d %s", argv[3], ip->ai_family, ip->ai_socktype, ip->ai_protocol, addr.buf); if ((err = sockaddr_to_hostname(ip->ai_addr, ip->ai_addrlen, &host, (MAI_SERVNAME_STR *) 0, 0)) != 0) { msg_info("sockaddr_to_hostname: %s", err == EAI_SYSTEM ? strerror(errno) : gai_strerror(err)); } else msg_info("%s -> %s", addr.buf, host.buf); freeaddrinfo(ip); } } exit(0); } #endif