getnameinfo.c revision 66374
162615Sitojun/* $FreeBSD: head/lib/libc/net/getnameinfo.c 66374 2000-09-25 23:04:36Z itojun $ */ 266374Sitojun/* $KAME: getnameinfo.c,v 1.45 2000/09/25 22:43:56 itojun Exp $ */ 362615Sitojun 455163Sshin/* 555163Sshin * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 655163Sshin * All rights reserved. 755163Sshin * 855163Sshin * Redistribution and use in source and binary forms, with or without 955163Sshin * modification, are permitted provided that the following conditions 1055163Sshin * are met: 1155163Sshin * 1. Redistributions of source code must retain the above copyright 1255163Sshin * notice, this list of conditions and the following disclaimer. 1355163Sshin * 2. Redistributions in binary form must reproduce the above copyright 1455163Sshin * notice, this list of conditions and the following disclaimer in the 1555163Sshin * documentation and/or other materials provided with the distribution. 1655163Sshin * 3. Neither the name of the project nor the names of its contributors 1755163Sshin * may be used to endorse or promote products derived from this software 1855163Sshin * without specific prior written permission. 1955163Sshin * 2055163Sshin * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 2155163Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2255163Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2355163Sshin * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 2455163Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2555163Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2655163Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2755163Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2855163Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2955163Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3055163Sshin * SUCH DAMAGE. 3155163Sshin */ 3255163Sshin 3355163Sshin/* 3455163Sshin * Issues to be discussed: 3555163Sshin * - Thread safe-ness must be checked 3662615Sitojun * - RFC2553 says that we should raise error on short buffer. X/Open says 3762615Sitojun * we need to truncate the result. We obey RFC2553 (and X/Open should be 3866374Sitojun * modified). ipngwg rough consensus seems to follow RFC2553. 3962615Sitojun * - What is "local" in NI_FQDN? 4062615Sitojun * - NI_NAMEREQD and NI_NUMERICHOST conflict with each other. 4162615Sitojun * - (KAME extension) NI_WITHSCOPEID when called with global address, 4262615Sitojun * and sin6_scope_id filled 4355163Sshin */ 4455163Sshin 4555163Sshin#include <sys/types.h> 4655163Sshin#include <sys/socket.h> 4755163Sshin#include <net/if.h> 4855163Sshin#include <netinet/in.h> 4955163Sshin#include <arpa/inet.h> 5055163Sshin#include <arpa/nameser.h> 5155163Sshin#include <netdb.h> 5255163Sshin#include <resolv.h> 5355163Sshin#include <string.h> 5455163Sshin#include <stddef.h> 5562615Sitojun#include <errno.h> 5655163Sshin 5762615Sitojun#define SUCCESS 0 5862615Sitojun#define ANY 0 5962615Sitojun#define YES 1 6062615Sitojun#define NO 0 6155163Sshin 6255163Sshinstatic struct afd { 6355163Sshin int a_af; 6455163Sshin int a_addrlen; 6555163Sshin int a_socklen; 6655163Sshin int a_off; 6755163Sshin} afdl [] = { 6855163Sshin#ifdef INET6 6955163Sshin {PF_INET6, sizeof(struct in6_addr), sizeof(struct sockaddr_in6), 7055163Sshin offsetof(struct sockaddr_in6, sin6_addr)}, 7155163Sshin#endif 7255163Sshin {PF_INET, sizeof(struct in_addr), sizeof(struct sockaddr_in), 7355163Sshin offsetof(struct sockaddr_in, sin_addr)}, 7455163Sshin {0, 0, 0}, 7555163Sshin}; 7655163Sshin 7755163Sshinstruct sockinet { 7855163Sshin u_char si_len; 7955163Sshin u_char si_family; 8055163Sshin u_short si_port; 8155163Sshin}; 8255163Sshin 8362615Sitojun#ifdef INET6 8462615Sitojunstatic int ip6_parsenumeric __P((const struct sockaddr *, const char *, char *, 8562615Sitojun size_t, int)); 8662615Sitojunstatic int ip6_sa2str __P((const struct sockaddr_in6 *, char *, size_t, int)); 8762615Sitojun#endif 8855163Sshin 8966374Sitojun/* 2553bis: use EAI_xx for getnameinfo */ 9062615Sitojun#define ENI_NOSOCKET EAI_FAIL /*XXX*/ 9162615Sitojun#define ENI_NOSERVNAME EAI_NONAME 9262615Sitojun#define ENI_NOHOSTNAME EAI_NONAME 9362615Sitojun#define ENI_MEMORY EAI_MEMORY 9462615Sitojun#define ENI_SYSTEM EAI_SYSTEM 9562615Sitojun#define ENI_FAMILY EAI_FAMILY 9662615Sitojun#define ENI_SALEN EAI_FAMILY 9762615Sitojun 9855163Sshinint 9955163Sshingetnameinfo(sa, salen, host, hostlen, serv, servlen, flags) 10055163Sshin const struct sockaddr *sa; 10155163Sshin size_t salen; 10255163Sshin char *host; 10355163Sshin size_t hostlen; 10455163Sshin char *serv; 10555163Sshin size_t servlen; 10655163Sshin int flags; 10755163Sshin{ 10855163Sshin struct afd *afd; 10955163Sshin struct servent *sp; 11055163Sshin struct hostent *hp; 11155163Sshin u_short port; 11255163Sshin int family, i; 11362615Sitojun const char *addr; 11462615Sitojun u_int32_t v4a; 11555163Sshin int h_error; 11655163Sshin char numserv[512]; 11755163Sshin char numaddr[512]; 11855163Sshin 11955163Sshin if (sa == NULL) 12055163Sshin return ENI_NOSOCKET; 12155163Sshin 12255163Sshin if (sa->sa_len != salen) 12355163Sshin return ENI_SALEN; 12462615Sitojun 12555163Sshin family = sa->sa_family; 12655163Sshin for (i = 0; afdl[i].a_af; i++) 12755163Sshin if (afdl[i].a_af == family) { 12855163Sshin afd = &afdl[i]; 12955163Sshin goto found; 13055163Sshin } 13155163Sshin return ENI_FAMILY; 13262615Sitojun 13355163Sshin found: 13455163Sshin if (salen != afd->a_socklen) 13555163Sshin return ENI_SALEN; 13662615Sitojun 13762615Sitojun /* network byte order */ 13862615Sitojun port = ((const struct sockinet *)sa)->si_port; 13962615Sitojun addr = (const char *)sa + afd->a_off; 14055163Sshin 14155163Sshin if (serv == NULL || servlen == 0) { 14262615Sitojun /* 14362615Sitojun * do nothing in this case. 14462615Sitojun * in case you are wondering if "&&" is more correct than 14562615Sitojun * "||" here: RFC2553 says that serv == NULL OR servlen == 0 14662615Sitojun * means that the caller does not want the result. 14762615Sitojun */ 14855163Sshin } else { 14955163Sshin if (flags & NI_NUMERICSERV) 15055163Sshin sp = NULL; 15155163Sshin else { 15255163Sshin sp = getservbyport(port, 15355163Sshin (flags & NI_DGRAM) ? "udp" : "tcp"); 15455163Sshin } 15555163Sshin if (sp) { 15666374Sitojun if (strlen(sp->s_name) + 1 > servlen) 15755163Sshin return ENI_MEMORY; 15855163Sshin strcpy(serv, sp->s_name); 15955163Sshin } else { 16055163Sshin snprintf(numserv, sizeof(numserv), "%d", ntohs(port)); 16166374Sitojun if (strlen(numserv) + 1 > servlen) 16255163Sshin return ENI_MEMORY; 16355163Sshin strcpy(serv, numserv); 16455163Sshin } 16555163Sshin } 16655163Sshin 16755163Sshin switch (sa->sa_family) { 16855163Sshin case AF_INET: 16962615Sitojun v4a = (u_int32_t) 17062615Sitojun ntohl(((const struct sockaddr_in *)sa)->sin_addr.s_addr); 17155163Sshin if (IN_MULTICAST(v4a) || IN_EXPERIMENTAL(v4a)) 17255163Sshin flags |= NI_NUMERICHOST; 17355163Sshin v4a >>= IN_CLASSA_NSHIFT; 17456671Sshin if (v4a == 0) 17562615Sitojun flags |= NI_NUMERICHOST; 17655163Sshin break; 17755163Sshin#ifdef INET6 17855163Sshin case AF_INET6: 17955163Sshin { 18062615Sitojun const struct sockaddr_in6 *sin6; 18162615Sitojun sin6 = (const struct sockaddr_in6 *)sa; 18262615Sitojun switch (sin6->sin6_addr.s6_addr[0]) { 18362615Sitojun case 0x00: 18462615Sitojun if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) 18562615Sitojun ; 18662615Sitojun else if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr)) 18762615Sitojun ; 18862615Sitojun else 18962615Sitojun flags |= NI_NUMERICHOST; 19062615Sitojun break; 19162615Sitojun default: 19262615Sitojun if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { 19362615Sitojun flags |= NI_NUMERICHOST; 19462615Sitojun } 19562615Sitojun else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) 19662615Sitojun flags |= NI_NUMERICHOST; 19762615Sitojun break; 19862615Sitojun } 19955163Sshin } 20055163Sshin break; 20155163Sshin#endif 20255163Sshin } 20355163Sshin if (host == NULL || hostlen == 0) { 20462615Sitojun /* 20562615Sitojun * do nothing in this case. 20662615Sitojun * in case you are wondering if "&&" is more correct than 20762615Sitojun * "||" here: RFC2553 says that host == NULL OR hostlen == 0 20862615Sitojun * means that the caller does not want the result. 20962615Sitojun */ 21055163Sshin } else if (flags & NI_NUMERICHOST) { 21162615Sitojun int numaddrlen; 21262615Sitojun 21355163Sshin /* NUMERICHOST and NAMEREQD conflicts with each other */ 21455163Sshin if (flags & NI_NAMEREQD) 21555163Sshin return ENI_NOHOSTNAME; 21662615Sitojun 21762615Sitojun switch(afd->a_af) { 21855163Sshin#ifdef INET6 21962615Sitojun case AF_INET6: 22062615Sitojun { 22162615Sitojun int error; 22255163Sshin 22362615Sitojun if ((error = ip6_parsenumeric(sa, addr, host, 22462615Sitojun hostlen, flags)) != 0) 22562615Sitojun return(error); 22662615Sitojun break; 22755163Sshin } 22862615Sitojun#endif 22962615Sitojun default: 23062615Sitojun if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr)) 23162615Sitojun == NULL) 23262615Sitojun return ENI_SYSTEM; 23362615Sitojun numaddrlen = strlen(numaddr); 23462615Sitojun if (numaddrlen + 1 > hostlen) /* don't forget terminator */ 23562615Sitojun return ENI_MEMORY; 23662615Sitojun strcpy(host, numaddr); 23762615Sitojun break; 23862615Sitojun } 23955163Sshin } else { 24055163Sshin hp = getipnodebyaddr(addr, afd->a_addrlen, afd->a_af, &h_error); 24162615Sitojun 24255163Sshin if (hp) { 24362615Sitojun#if 0 24462615Sitojun /* 24562615Sitojun * commented out, since "for local host" is not 24662615Sitojun * implemented here - see RFC2553 p30 24762615Sitojun */ 24855163Sshin if (flags & NI_NOFQDN) { 24962615Sitojun char *p; 25055163Sshin p = strchr(hp->h_name, '.'); 25162615Sitojun if (p) 25262615Sitojun *p = '\0'; 25355163Sshin } 25462615Sitojun#endif 25566374Sitojun if (strlen(hp->h_name) + 1 > hostlen) { 25655163Sshin freehostent(hp); 25755163Sshin return ENI_MEMORY; 25855163Sshin } 25955163Sshin strcpy(host, hp->h_name); 26055163Sshin freehostent(hp); 26155163Sshin } else { 26255163Sshin if (flags & NI_NAMEREQD) 26355163Sshin return ENI_NOHOSTNAME; 26462615Sitojun switch(afd->a_af) { 26562615Sitojun#ifdef INET6 26662615Sitojun case AF_INET6: 26762615Sitojun { 26862615Sitojun int error; 26962615Sitojun 27062615Sitojun if ((error = ip6_parsenumeric(sa, addr, host, 27162615Sitojun hostlen, 27262615Sitojun flags)) != 0) 27362615Sitojun return(error); 27462615Sitojun break; 27562615Sitojun } 27662615Sitojun#endif 27762615Sitojun default: 27862615Sitojun if (inet_ntop(afd->a_af, addr, host, 27962615Sitojun hostlen) == NULL) 28062615Sitojun return ENI_SYSTEM; 28162615Sitojun break; 28262615Sitojun } 28355163Sshin } 28455163Sshin } 28555163Sshin return SUCCESS; 28655163Sshin} 28762615Sitojun 28862615Sitojun#ifdef INET6 28962615Sitojunstatic int 29062615Sitojunip6_parsenumeric(sa, addr, host, hostlen, flags) 29162615Sitojun const struct sockaddr *sa; 29262615Sitojun const char *addr; 29362615Sitojun char *host; 29462615Sitojun size_t hostlen; 29562615Sitojun int flags; 29662615Sitojun{ 29762615Sitojun int numaddrlen; 29862615Sitojun char numaddr[512]; 29962615Sitojun 30062615Sitojun if (inet_ntop(AF_INET6, addr, numaddr, sizeof(numaddr)) 30162615Sitojun == NULL) 30262615Sitojun return ENI_SYSTEM; 30362615Sitojun 30462615Sitojun numaddrlen = strlen(numaddr); 30562615Sitojun if (numaddrlen + 1 > hostlen) /* don't forget terminator */ 30662615Sitojun return ENI_MEMORY; 30762615Sitojun strcpy(host, numaddr); 30862615Sitojun 30962615Sitojun#ifdef NI_WITHSCOPEID 31062615Sitojun if ( 31162615Sitojun#ifdef DONT_OPAQUE_SCOPEID 31262615Sitojun (IN6_IS_ADDR_LINKLOCAL((struct in6_addr *)addr) || 31362615Sitojun IN6_IS_ADDR_MULTICAST((struct in6_addr *)addr)) && 31462615Sitojun#endif 31562615Sitojun ((const struct sockaddr_in6 *)sa)->sin6_scope_id) { 31662615Sitojun#ifndef ALWAYS_WITHSCOPE 31762615Sitojun if (flags & NI_WITHSCOPEID) 31862615Sitojun#endif /* !ALWAYS_WITHSCOPE */ 31962615Sitojun { 32062615Sitojun char scopebuf[MAXHOSTNAMELEN]; 32162615Sitojun int scopelen; 32262615Sitojun 32362615Sitojun /* ip6_sa2str never fails */ 32462615Sitojun scopelen = ip6_sa2str((const struct sockaddr_in6 *)sa, 32562615Sitojun scopebuf, sizeof(scopebuf), 32662615Sitojun flags); 32762615Sitojun if (scopelen + 1 + numaddrlen + 1 > hostlen) 32862615Sitojun return ENI_MEMORY; 32962615Sitojun /* 33062615Sitojun * construct <numeric-addr><delim><scopeid> 33162615Sitojun */ 33262615Sitojun memcpy(host + numaddrlen + 1, scopebuf, 33362615Sitojun scopelen); 33462615Sitojun host[numaddrlen] = SCOPE_DELIMITER; 33562615Sitojun host[numaddrlen + 1 + scopelen] = '\0'; 33662615Sitojun } 33762615Sitojun } 33862615Sitojun#endif /* NI_WITHSCOPEID */ 33962615Sitojun 34062615Sitojun return 0; 34162615Sitojun} 34262615Sitojun 34362615Sitojun/* ARGSUSED */ 34462615Sitojunstatic int 34562615Sitojunip6_sa2str(sa6, buf, bufsiz, flags) 34662615Sitojun const struct sockaddr_in6 *sa6; 34762615Sitojun char *buf; 34862615Sitojun size_t bufsiz; 34962615Sitojun int flags; 35062615Sitojun{ 35162615Sitojun unsigned int ifindex = (unsigned int)sa6->sin6_scope_id; 35262615Sitojun const struct in6_addr *a6 = &sa6->sin6_addr; 35362615Sitojun 35462615Sitojun#ifdef NI_NUMERICSCOPE 35562615Sitojun if (flags & NI_NUMERICSCOPE) { 35662615Sitojun return(snprintf(buf, bufsiz, "%d", sa6->sin6_scope_id)); 35762615Sitojun } 35862615Sitojun#endif 35962615Sitojun 36062615Sitojun /* if_indextoname() does not take buffer size. not a good api... */ 36162615Sitojun if ((IN6_IS_ADDR_LINKLOCAL(a6) || IN6_IS_ADDR_MC_LINKLOCAL(a6)) && 36262615Sitojun bufsiz >= IF_NAMESIZE) { 36362615Sitojun char *p = if_indextoname(ifindex, buf); 36462615Sitojun if (p) { 36562615Sitojun return(strlen(p)); 36662615Sitojun } 36762615Sitojun } 36862615Sitojun 36962615Sitojun /* last resort */ 37062615Sitojun return(snprintf(buf, bufsiz, "%u", sa6->sin6_scope_id)); 37162615Sitojun} 37262615Sitojun#endif /* INET6 */ 373