getnameinfo.c revision 56671
155163Sshin/* 255163Sshin * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 355163Sshin * All rights reserved. 455163Sshin * 555163Sshin * Redistribution and use in source and binary forms, with or without 655163Sshin * modification, are permitted provided that the following conditions 755163Sshin * are met: 855163Sshin * 1. Redistributions of source code must retain the above copyright 955163Sshin * notice, this list of conditions and the following disclaimer. 1055163Sshin * 2. Redistributions in binary form must reproduce the above copyright 1155163Sshin * notice, this list of conditions and the following disclaimer in the 1255163Sshin * documentation and/or other materials provided with the distribution. 1355163Sshin * 3. Neither the name of the project nor the names of its contributors 1455163Sshin * may be used to endorse or promote products derived from this software 1555163Sshin * without specific prior written permission. 1655163Sshin * 1755163Sshin * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 1855163Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1955163Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2055163Sshin * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 2155163Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2255163Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2355163Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2455163Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2555163Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2655163Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2755163Sshin * SUCH DAMAGE. 2855163Sshin * 2955163Sshin * $FreeBSD: head/lib/libc/net/getnameinfo.c 56671 2000-01-27 13:00:14Z shin $ 3055163Sshin */ 3155163Sshin 3255163Sshin/* 3355163Sshin * Issues to be discussed: 3455163Sshin * - Thread safe-ness must be checked 3555163Sshin * - Return values. There seems to be no standard for return value (RFC2553) 3655163Sshin * but INRIA implementation returns EAI_xxx defined for getaddrinfo(). 3755163Sshin */ 3855163Sshin 3955163Sshin#include <sys/types.h> 4055163Sshin#include <sys/socket.h> 4155163Sshin#include <net/if.h> 4255163Sshin#include <netinet/in.h> 4355163Sshin#include <arpa/inet.h> 4455163Sshin#include <arpa/nameser.h> 4555163Sshin#include <netdb.h> 4655163Sshin#include <resolv.h> 4755163Sshin#include <string.h> 4855163Sshin#include <stddef.h> 4955163Sshin 5055163Sshin#define SUCCESS 0 5155163Sshin#define ANY 0 5255163Sshin#define YES 1 5355163Sshin#define NO 0 5455163Sshin 5555163Sshinstatic struct afd { 5655163Sshin int a_af; 5755163Sshin int a_addrlen; 5855163Sshin int a_socklen; 5955163Sshin int a_off; 6055163Sshin} afdl [] = { 6155163Sshin#ifdef INET6 6255163Sshin {PF_INET6, sizeof(struct in6_addr), sizeof(struct sockaddr_in6), 6355163Sshin offsetof(struct sockaddr_in6, sin6_addr)}, 6455163Sshin#endif 6555163Sshin {PF_INET, sizeof(struct in_addr), sizeof(struct sockaddr_in), 6655163Sshin offsetof(struct sockaddr_in, sin_addr)}, 6755163Sshin {0, 0, 0}, 6855163Sshin}; 6955163Sshin 7055163Sshinstruct sockinet { 7155163Sshin u_char si_len; 7255163Sshin u_char si_family; 7355163Sshin u_short si_port; 7455163Sshin}; 7555163Sshin 7655163Sshin#define ENI_NOSOCKET 0 7755163Sshin#define ENI_NOSERVHOST 1 7855163Sshin#define ENI_NOHOSTNAME 2 7955163Sshin#define ENI_MEMORY 3 8055163Sshin#define ENI_SYSTEM 4 8155163Sshin#define ENI_FAMILY 5 8255163Sshin#define ENI_SALEN 6 8355163Sshin 8455163Sshinint 8555163Sshingetnameinfo(sa, salen, host, hostlen, serv, servlen, flags) 8655163Sshin const struct sockaddr *sa; 8755163Sshin size_t salen; 8855163Sshin char *host; 8955163Sshin size_t hostlen; 9055163Sshin char *serv; 9155163Sshin size_t servlen; 9255163Sshin int flags; 9355163Sshin{ 9455163Sshin struct afd *afd; 9555163Sshin struct servent *sp; 9655163Sshin struct hostent *hp; 9755163Sshin u_short port; 9855163Sshin int family, i; 9955163Sshin char *addr, *p; 10055163Sshin u_long v4a; 10155163Sshin int h_error; 10255163Sshin char numserv[512]; 10355163Sshin char numaddr[512]; 10455163Sshin int noserv = 0; 10555163Sshin 10655163Sshin if (sa == NULL) 10755163Sshin return ENI_NOSOCKET; 10855163Sshin 10955163Sshin if (sa->sa_len != salen) 11055163Sshin return ENI_SALEN; 11155163Sshin 11255163Sshin family = sa->sa_family; 11355163Sshin for (i = 0; afdl[i].a_af; i++) 11455163Sshin if (afdl[i].a_af == family) { 11555163Sshin afd = &afdl[i]; 11655163Sshin goto found; 11755163Sshin } 11855163Sshin return ENI_FAMILY; 11955163Sshin 12055163Sshin found: 12155163Sshin if (salen != afd->a_socklen) 12255163Sshin return ENI_SALEN; 12355163Sshin 12455163Sshin port = ((struct sockinet *)sa)->si_port; /* network byte order */ 12555163Sshin addr = (char *)sa + afd->a_off; 12655163Sshin 12755163Sshin if (serv == NULL || servlen == 0) { 12855163Sshin noserv = 1; 12955163Sshin } else { 13055163Sshin if (flags & NI_NUMERICSERV) 13155163Sshin sp = NULL; 13255163Sshin else { 13355163Sshin sp = getservbyport(port, 13455163Sshin (flags & NI_DGRAM) ? "udp" : "tcp"); 13555163Sshin } 13655163Sshin if (sp) { 13755163Sshin if (strlen(sp->s_name) > servlen) 13855163Sshin return ENI_MEMORY; 13955163Sshin strcpy(serv, sp->s_name); 14055163Sshin } else { 14155163Sshin snprintf(numserv, sizeof(numserv), "%d", ntohs(port)); 14255163Sshin if (strlen(numserv) > servlen) 14355163Sshin return ENI_MEMORY; 14455163Sshin strcpy(serv, numserv); 14555163Sshin } 14655163Sshin } 14755163Sshin 14855163Sshin switch (sa->sa_family) { 14955163Sshin case AF_INET: 15055163Sshin v4a = ntohl(((struct sockaddr_in *)sa)->sin_addr.s_addr); 15155163Sshin if (IN_MULTICAST(v4a) || IN_EXPERIMENTAL(v4a)) 15255163Sshin flags |= NI_NUMERICHOST; 15355163Sshin v4a >>= IN_CLASSA_NSHIFT; 15456671Sshin if (v4a == 0) 15555163Sshin flags |= NI_NUMERICHOST; 15655163Sshin break; 15755163Sshin#ifdef INET6 15855163Sshin case AF_INET6: 15955163Sshin { 16055163Sshin struct sockaddr_in6 *sin6; 16155163Sshin sin6 = (struct sockaddr_in6 *)sa; 16255163Sshin if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) || 16355163Sshin IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) 16455163Sshin flags |= NI_NUMERICHOST; 16555163Sshin } 16655163Sshin break; 16755163Sshin#endif 16855163Sshin } 16955163Sshin if (host == NULL || hostlen == 0) { 17055163Sshin if (noserv == 1) 17155163Sshin return ENI_NOSERVHOST; 17255163Sshin } else if (flags & NI_NUMERICHOST) { 17355163Sshin /* NUMERICHOST and NAMEREQD conflicts with each other */ 17455163Sshin if (flags & NI_NAMEREQD) 17555163Sshin return ENI_NOHOSTNAME; 17655163Sshin if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr)) 17755163Sshin == NULL) 17855163Sshin return ENI_SYSTEM; 17955163Sshin if (strlen(numaddr) > hostlen) 18055163Sshin return ENI_MEMORY; 18155163Sshin strcpy(host, numaddr); 18255163Sshin#ifdef INET6 18355163Sshin if (afd->a_af == AF_INET6 && 18455163Sshin (IN6_IS_ADDR_LINKLOCAL((struct in6_addr *)addr) || 18555163Sshin IN6_IS_ADDR_MULTICAST((struct in6_addr *)addr)) && 18655163Sshin ((struct sockaddr_in6 *)sa)->sin6_scope_id) { 18755163Sshin if (flags & NI_WITHSCOPEID) { 18855163Sshin char *ep = strchr(host, '\0'); 18955163Sshin unsigned int ifindex = 19055163Sshin ((struct sockaddr_in6 *)sa)->sin6_scope_id; 19155163Sshin char ifname[IF_NAMESIZE * 2 /* for safety */]; 19255163Sshin 19355163Sshin if ((if_indextoname(ifindex, ifname)) == NULL) 19455163Sshin return ENI_SYSTEM; 19555163Sshin if (strlen(host) + 1 /* SCOPE_DELIMITER */ 19655163Sshin + strlen(ifname) > hostlen) 19755163Sshin return ENI_MEMORY; 19855163Sshin *ep = SCOPE_DELIMITER; 19955163Sshin strcpy(ep + 1, ifname); 20055163Sshin } 20155163Sshin } 20255163Sshin#endif /* INET6 */ 20355163Sshin } else { 20455163Sshin hp = getipnodebyaddr(addr, afd->a_addrlen, afd->a_af, &h_error); 20555163Sshin if (hp) { 20655163Sshin if (flags & NI_NOFQDN) { 20755163Sshin p = strchr(hp->h_name, '.'); 20855163Sshin if (p) *p = '\0'; 20955163Sshin } 21055163Sshin if (strlen(hp->h_name) > hostlen) { 21155163Sshin freehostent(hp); 21255163Sshin return ENI_MEMORY; 21355163Sshin } 21455163Sshin strcpy(host, hp->h_name); 21555163Sshin freehostent(hp); 21655163Sshin } else { 21755163Sshin if (flags & NI_NAMEREQD) 21855163Sshin return ENI_NOHOSTNAME; 21955163Sshin if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr)) 22055163Sshin == NULL) 22155163Sshin return ENI_NOHOSTNAME; 22255163Sshin if (strlen(numaddr) > hostlen) 22355163Sshin return ENI_MEMORY; 22455163Sshin strcpy(host, numaddr); 22555163Sshin } 22655163Sshin } 22755163Sshin return SUCCESS; 22855163Sshin} 229