getnameinfo.c revision 55163
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 55163 1999-12-28 02:37: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;
15455163Sshin		if (v4a == 0 || v4a == IN_LOOPBACKNET)
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