| /** @file | |
| Copyright (c) 2010 - 2014, Intel Corporation. All rights reserved.<BR> | |
| This program and the accompanying materials are licensed and made available | |
| under the terms and conditions of the BSD License which accompanies this | |
| distribution. The full text of the license may be found at | |
| http://opensource.org/licenses/bsd-license.php. | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
| **/ | |
| /* $NetBSD: getnameinfo.c,v 1.45 2006/10/15 16:14:46 christos Exp $ */ | |
| /* $KAME: getnameinfo.c,v 1.45 2000/09/25 22:43:56 itojun Exp $ */ | |
| /* | |
| * Copyright (c) 2000 Ben Harris. | |
| * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. | |
| * All rights reserved. | |
| * | |
| * Redistribution and use in source and binary forms, with or without | |
| * modification, are permitted provided that the following conditions | |
| * are met: | |
| * 1. Redistributions of source code must retain the above copyright | |
| * notice, this list of conditions and the following disclaimer. | |
| * 2. Redistributions in binary form must reproduce the above copyright | |
| * notice, this list of conditions and the following disclaimer in the | |
| * documentation and/or other materials provided with the distribution. | |
| * 3. Neither the name of the project nor the names of its contributors | |
| * may be used to endorse or promote products derived from this software | |
| * without specific prior written permission. | |
| * | |
| * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND | |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE | |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
| * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| * SUCH DAMAGE. | |
| */ | |
| /* | |
| * Issues to be discussed: | |
| * - Thread safe-ness must be checked | |
| * - RFC2553 says that we should raise error on short buffer. X/Open says | |
| * we need to truncate the result. We obey RFC2553 (and X/Open should be | |
| * modified). ipngwg rough consensus seems to follow RFC2553. | |
| * - What is "local" in NI_FQDN? | |
| * - NI_NAMEREQD and NI_NUMERICHOST conflict with each other. | |
| * - (KAME extension) always attach textual scopeid (fe80::1%lo0), if | |
| * sin6_scope_id is filled - standardization status? | |
| * XXX breaks backward compat for code that expects no scopeid. | |
| * beware on merge. | |
| */ | |
| #define INET6 1 | |
| #include <sys/cdefs.h> | |
| #if defined(LIBC_SCCS) && !defined(lint) | |
| __RCSID("$NetBSD: getnameinfo.c,v 1.45 2006/10/15 16:14:46 christos Exp $"); | |
| #endif /* LIBC_SCCS and not lint */ | |
| #include "namespace.h" | |
| #include <sys/types.h> | |
| #include <sys/socket.h> | |
| #include <net/if.h> | |
| #include <net/if_dl.h> | |
| //#include <net/if_ieee1394.h> | |
| //#include <net/if_types.h> | |
| #include <netinet/in.h> | |
| #include <arpa/inet.h> | |
| #include <arpa/nameser.h> | |
| #include <assert.h> | |
| #include <limits.h> | |
| #include <netdb.h> | |
| #include <resolv.h> | |
| #include <stddef.h> | |
| #include <string.h> | |
| #include <net/servent.h> | |
| #define CLLADDR(x) ( LLADDR(x) ) | |
| #define endservent_r(svd) endservent() | |
| #define getservbyport_r(Port,pProto,pSv,pSvd) getservbyport(Port,pProto) | |
| #ifdef __weak_alias | |
| __weak_alias(getnameinfo,_getnameinfo) | |
| #endif | |
| static | |
| int | |
| hexname( | |
| const u_int8_t * cp, | |
| size_t len, | |
| char * host, | |
| socklen_t hostlen | |
| ); | |
| static const struct afd { | |
| int a_af; | |
| socklen_t a_addrlen; | |
| socklen_t a_socklen; | |
| int a_off; | |
| } afdl [] = { | |
| #ifdef INET6 | |
| {PF_INET6, sizeof(struct in6_addr), sizeof(struct sockaddr_in6), | |
| offsetof(struct sockaddr_in6, sin6_addr)}, | |
| #endif | |
| {PF_INET, sizeof(struct in_addr), sizeof(struct sockaddr_in), | |
| offsetof(struct sockaddr_in, sin_addr)}, | |
| {0, 0, 0, 0}, | |
| }; | |
| struct sockinet { | |
| u_char si_len; | |
| u_char si_family; | |
| u_short si_port; | |
| }; | |
| static int getnameinfo_inet __P((const struct sockaddr *, socklen_t, char *, | |
| socklen_t, char *, socklen_t, int)); | |
| #ifdef INET6 | |
| static int ip6_parsenumeric __P((const struct sockaddr *, const char *, char *, | |
| socklen_t, int)); | |
| static int ip6_sa2str __P((const struct sockaddr_in6 *, char *, size_t, | |
| int)); | |
| #endif | |
| static int getnameinfo_link __P((const struct sockaddr *, socklen_t, char *, | |
| socklen_t, char *, socklen_t, int)); | |
| static int hexname __P((const u_int8_t *, size_t, char *, socklen_t)); | |
| /* | |
| * Top-level getnameinfo() code. Look at the address family, and pick an | |
| * appropriate function to call. | |
| */ | |
| int | |
| getnameinfo( | |
| const struct sockaddr * sa, | |
| socklen_t salen, | |
| char * host, | |
| socklen_t hostlen, | |
| char * serv, | |
| socklen_t servlen, | |
| int flags | |
| ) | |
| { | |
| switch (sa->sa_family) { | |
| case AF_INET: | |
| case AF_INET6: | |
| return getnameinfo_inet(sa, salen, host, hostlen, | |
| serv, servlen, flags); | |
| case AF_LINK: | |
| return getnameinfo_link(sa, salen, host, hostlen, | |
| serv, servlen, flags); | |
| default: | |
| return EAI_FAMILY; | |
| } | |
| } | |
| /* | |
| * getnameinfo_inet(): | |
| * Format an IPv4 or IPv6 sockaddr into a printable string. | |
| */ | |
| static | |
| int | |
| getnameinfo_inet( | |
| const struct sockaddr * sa, | |
| socklen_t salen, | |
| char * host, | |
| socklen_t hostlen, | |
| char * serv, | |
| socklen_t servlen, | |
| int flags | |
| ) | |
| { | |
| const struct afd *afd; | |
| struct servent *sp; | |
| struct hostent *hp; | |
| u_short port; | |
| int family, i; | |
| const char *addr; | |
| u_int32_t v4a; | |
| char numserv[512]; | |
| char numaddr[512]; | |
| /* sa is checked below */ | |
| /* host may be NULL */ | |
| /* serv may be NULL */ | |
| if (sa == NULL) | |
| return EAI_FAIL; | |
| #ifdef BSD4_4 | |
| if (sa->sa_len != salen) | |
| return EAI_FAIL; | |
| #endif | |
| family = sa->sa_family; | |
| for (i = 0; afdl[i].a_af; i++) | |
| if (afdl[i].a_af == family) { | |
| afd = &afdl[i]; | |
| goto found; | |
| } | |
| return EAI_FAMILY; | |
| found: | |
| if (salen != afd->a_socklen) | |
| return EAI_FAIL; | |
| /* network byte order */ | |
| port = ((const struct sockinet *)(const void *)sa)->si_port; | |
| addr = (const char *)(const void *)sa + afd->a_off; | |
| if (serv == NULL || servlen == 0) { | |
| /* | |
| * do nothing in this case. | |
| * in case you are wondering if "&&" is more correct than | |
| * "||" here: rfc2553bis-03 says that serv == NULL OR | |
| * servlen == 0 means that the caller does not want the result. | |
| */ | |
| } else { | |
| if (flags & NI_NUMERICSERV) | |
| sp = NULL; | |
| else { | |
| struct servent_data svd; | |
| // struct servent sv; | |
| (void)memset(&svd, 0, sizeof(svd)); | |
| sp = getservbyport_r(port, | |
| (flags & NI_DGRAM) ? "udp" : "tcp", &sv, &svd); | |
| endservent_r(&svd); | |
| } | |
| if (sp) { | |
| if (strlen(sp->s_name) + 1 > servlen) | |
| return EAI_MEMORY; | |
| strlcpy(serv, sp->s_name, servlen); | |
| } else { | |
| snprintf(numserv, sizeof(numserv), "%u", ntohs(port)); | |
| if (strlen(numserv) + 1 > servlen) | |
| return EAI_MEMORY; | |
| strlcpy(serv, numserv, servlen); | |
| } | |
| } | |
| switch (sa->sa_family) { | |
| case AF_INET: | |
| v4a = (u_int32_t) | |
| ntohl(((const struct sockaddr_in *) | |
| (const void *)sa)->sin_addr.s_addr); | |
| if (IN_MULTICAST(v4a) || IN_EXPERIMENTAL(v4a)) | |
| flags |= NI_NUMERICHOST; | |
| v4a >>= IN_CLASSA_NSHIFT; | |
| if (v4a == 0) | |
| flags |= NI_NUMERICHOST; | |
| break; | |
| #ifdef INET6 | |
| case AF_INET6: | |
| { | |
| const struct sockaddr_in6 *sin6; | |
| sin6 = (const struct sockaddr_in6 *)(const void *)sa; | |
| switch (sin6->sin6_addr.s6_addr[0]) { | |
| case 0x00: | |
| if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) | |
| ; | |
| else if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr)) | |
| ; | |
| else | |
| flags |= NI_NUMERICHOST; | |
| break; | |
| default: | |
| if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { | |
| flags |= NI_NUMERICHOST; | |
| } | |
| else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) | |
| flags |= NI_NUMERICHOST; | |
| break; | |
| } | |
| } | |
| break; | |
| #endif | |
| } | |
| if (host == NULL || hostlen == 0) { | |
| /* | |
| * do nothing in this case. | |
| * in case you are wondering if "&&" is more correct than | |
| * "||" here: rfc2553bis-03 says that host == NULL or | |
| * hostlen == 0 means that the caller does not want the result. | |
| */ | |
| } else if (flags & NI_NUMERICHOST) { | |
| size_t numaddrlen; | |
| /* NUMERICHOST and NAMEREQD conflicts with each other */ | |
| if (flags & NI_NAMEREQD) | |
| return EAI_NONAME; | |
| switch(afd->a_af) { | |
| #ifdef INET6 | |
| case AF_INET6: | |
| { | |
| int error; | |
| if ((error = ip6_parsenumeric(sa, addr, host, | |
| hostlen, flags)) != 0) | |
| return(error); | |
| break; | |
| } | |
| #endif | |
| default: | |
| if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr)) | |
| == NULL) | |
| return EAI_SYSTEM; | |
| numaddrlen = strlen(numaddr); | |
| if (numaddrlen + 1 > hostlen) /* don't forget terminator */ | |
| return EAI_MEMORY; | |
| strlcpy(host, numaddr, hostlen); | |
| break; | |
| } | |
| } else { | |
| hp = gethostbyaddr(addr, afd->a_addrlen, afd->a_af); | |
| if (hp) { | |
| #if 0 | |
| /* | |
| * commented out, since "for local host" is not | |
| * implemented here - see RFC2553 p30 | |
| */ | |
| if (flags & NI_NOFQDN) { | |
| char *p; | |
| p = strchr(hp->h_name, '.'); | |
| if (p) | |
| *p = '\0'; | |
| } | |
| #endif | |
| if (strlen(hp->h_name) + 1 > hostlen) { | |
| return EAI_MEMORY; | |
| } | |
| strlcpy(host, hp->h_name, hostlen); | |
| } else { | |
| if (flags & NI_NAMEREQD) | |
| return EAI_NONAME; | |
| switch(afd->a_af) { | |
| #ifdef INET6 | |
| case AF_INET6: | |
| { | |
| int error; | |
| if ((error = ip6_parsenumeric(sa, addr, host, | |
| hostlen, | |
| flags)) != 0) | |
| return(error); | |
| break; | |
| } | |
| #endif | |
| default: | |
| if (inet_ntop(afd->a_af, addr, host, | |
| hostlen) == NULL) | |
| return EAI_SYSTEM; | |
| break; | |
| } | |
| } | |
| } | |
| return(0); | |
| } | |
| #ifdef INET6 | |
| static int | |
| ip6_parsenumeric( | |
| const struct sockaddr *sa, | |
| const char *addr, | |
| char *host, | |
| socklen_t hostlen, | |
| int flags | |
| ) | |
| { | |
| size_t numaddrlen; | |
| char numaddr[512]; | |
| _DIAGASSERT(sa != NULL); | |
| _DIAGASSERT(addr != NULL); | |
| _DIAGASSERT(host != NULL); | |
| if (inet_ntop(AF_INET6, addr, numaddr, sizeof(numaddr)) == NULL) | |
| return EAI_SYSTEM; | |
| numaddrlen = strlen(numaddr); | |
| if (numaddrlen + 1 > hostlen) /* don't forget terminator */ | |
| return EAI_OVERFLOW; | |
| strlcpy(host, numaddr, hostlen); | |
| if (((const struct sockaddr_in6 *)(const void *)sa)->sin6_scope_id) { | |
| char zonebuf[MAXHOSTNAMELEN]; | |
| int zonelen; | |
| zonelen = ip6_sa2str( | |
| (const struct sockaddr_in6 *)(const void *)sa, | |
| zonebuf, sizeof(zonebuf), flags); | |
| if (zonelen < 0) | |
| return EAI_OVERFLOW; | |
| if ((size_t) zonelen + 1 + numaddrlen + 1 > hostlen) | |
| return EAI_OVERFLOW; | |
| /* construct <numeric-addr><delim><zoneid> */ | |
| memcpy(host + numaddrlen + 1, zonebuf, | |
| (size_t)zonelen); | |
| host[numaddrlen] = SCOPE_DELIMITER; | |
| host[numaddrlen + 1 + zonelen] = '\0'; | |
| } | |
| return 0; | |
| } | |
| /* ARGSUSED */ | |
| static int | |
| ip6_sa2str( | |
| const struct sockaddr_in6 *sa6, | |
| char *buf, | |
| size_t bufsiz, | |
| int flags | |
| ) | |
| { | |
| #if 0 | |
| unsigned int ifindex; | |
| const struct in6_addr *a6; | |
| #endif | |
| int n; | |
| _DIAGASSERT(sa6 != NULL); | |
| _DIAGASSERT(buf != NULL); | |
| #if 0 | |
| ifindex = (unsigned int)sa6->sin6_scope_id; | |
| a6 = &sa6->sin6_addr; | |
| #endif | |
| #ifdef NI_NUMERICSCOPE | |
| if ((flags & NI_NUMERICSCOPE) != 0) { | |
| n = snprintf(buf, bufsiz, "%u", sa6->sin6_scope_id); | |
| if ((n < 0) || ((size_t)n >= bufsiz)) | |
| return -1; | |
| else | |
| return n; | |
| } | |
| #endif | |
| #if 0 | |
| /* if_indextoname() does not take buffer size. not a good api... */ | |
| if ((IN6_IS_ADDR_LINKLOCAL(a6) || IN6_IS_ADDR_MC_LINKLOCAL(a6)) && | |
| bufsiz >= IF_NAMESIZE) { | |
| char *p = if_indextoname(ifindex, buf); | |
| if (p) { | |
| return(strlen(p)); | |
| } | |
| } | |
| #endif // 0 | |
| /* last resort */ | |
| n = snprintf(buf, bufsiz, "%u", sa6->sin6_scope_id); | |
| if (n < 0 || (size_t) n >= bufsiz) | |
| return -1; | |
| else | |
| return n; | |
| } | |
| #endif /* INET6 */ | |
| /* | |
| * getnameinfo_link(): | |
| * Format a link-layer address into a printable format, paying attention to | |
| * the interface type. | |
| */ | |
| /* ARGSUSED */ | |
| static | |
| int | |
| getnameinfo_link ( | |
| const struct sockaddr * sa, | |
| socklen_t salen, | |
| char * host, | |
| socklen_t hostlen, | |
| char * serv, | |
| socklen_t servlen, | |
| int flags | |
| ) | |
| { | |
| const struct sockaddr_dl *sdl = | |
| (const struct sockaddr_dl *)(const void *)sa; | |
| // const struct ieee1394_hwaddr *iha; | |
| int n; | |
| if (serv != NULL && servlen > 0) | |
| *serv = '\0'; | |
| if (sdl->sdl_nlen == 0 && sdl->sdl_alen == 0 && sdl->sdl_slen == 0) { | |
| n = snprintf(host, hostlen, "link#%u", sdl->sdl_index); | |
| if (n < 0 || (socklen_t) n > hostlen) { | |
| *host = '\0'; | |
| return EAI_MEMORY; | |
| } | |
| return 0; | |
| } | |
| #if 0 | |
| switch (sdl->sdl_type) { | |
| #ifdef IFT_ECONET | |
| case IFT_ECONET: | |
| if (sdl->sdl_alen < 2) | |
| return EAI_FAMILY; | |
| if (CLLADDR(sdl)[1] == 0) | |
| n = snprintf(host, hostlen, "%u", CLLADDR(sdl)[0]); | |
| else | |
| n = snprintf(host, hostlen, "%u.%u", | |
| CLLADDR(sdl)[1], CLLADDR(sdl)[0]); | |
| if (n < 0 || (socklen_t) n >= hostlen) { | |
| *host = '\0'; | |
| return EAI_MEMORY; | |
| } else | |
| return 0; | |
| #endif | |
| case IFT_IEEE1394: | |
| if (sdl->sdl_alen < sizeof(iha->iha_uid)) | |
| return EAI_FAMILY; | |
| iha = | |
| (const struct ieee1394_hwaddr *)(const void *)CLLADDR(sdl); | |
| return hexname(iha->iha_uid, sizeof(iha->iha_uid), | |
| host, hostlen); | |
| /* | |
| * The following have zero-length addresses. | |
| * IFT_ATM (net/if_atmsubr.c) | |
| * IFT_FAITH (net/if_faith.c) | |
| * IFT_GIF (net/if_gif.c) | |
| * IFT_LOOP (net/if_loop.c) | |
| * IFT_PPP (net/if_ppp.c, net/if_spppsubr.c) | |
| * IFT_SLIP (net/if_sl.c, net/if_strip.c) | |
| * IFT_STF (net/if_stf.c) | |
| * IFT_L2VLAN (net/if_vlan.c) | |
| * IFT_PROPVIRTUAL (net/if_bridge.h> | |
| */ | |
| /* | |
| * The following use IPv4 addresses as link-layer addresses: | |
| * IFT_OTHER (net/if_gre.c) | |
| */ | |
| case IFT_ARCNET: /* default below is believed correct for all these. */ | |
| case IFT_ETHER: | |
| case IFT_FDDI: | |
| case IFT_HIPPI: | |
| case IFT_ISO88025: | |
| default: | |
| #endif // 0 | |
| return hexname((const u_int8_t *)CLLADDR(sdl), | |
| (size_t)sdl->sdl_alen, host, hostlen); | |
| // } | |
| } | |
| static | |
| int | |
| hexname( | |
| const u_int8_t * cp, | |
| size_t len, | |
| char * host, | |
| socklen_t hostlen | |
| ) | |
| { | |
| int n; | |
| size_t i; | |
| char *outp = host; | |
| *outp = '\0'; | |
| for (i = 0; i < len; i++) { | |
| n = snprintf(outp, hostlen, "%s%02x", | |
| i ? ":" : "", cp[i]); | |
| if (n < 0 || (socklen_t) n >= hostlen) { | |
| *host = '\0'; | |
| return EAI_MEMORY; | |
| } | |
| outp += n; | |
| hostlen -= n; | |
| } | |
| return 0; | |
| } |