getnameinfo.c revision 75115
155682Smarkm/*
255682Smarkm * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
355682Smarkm * All rights reserved.
455682Smarkm *
555682Smarkm * Redistribution and use in source and binary forms, with or without
655682Smarkm * modification, are permitted provided that the following conditions
755682Smarkm * are met:
855682Smarkm * 1. Redistributions of source code must retain the above copyright
955682Smarkm *    notice, this list of conditions and the following disclaimer.
1055682Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1155682Smarkm *    notice, this list of conditions and the following disclaimer in the
1255682Smarkm *    documentation and/or other materials provided with the distribution.
1355682Smarkm * 3. Neither the name of the project nor the names of its contributors
1455682Smarkm *    may be used to endorse or promote products derived from this software
1555682Smarkm *    without specific prior written permission.
1655682Smarkm *
1755682Smarkm * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
1855682Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1955682Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2055682Smarkm * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
2155682Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2255682Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2355682Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2455682Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2555682Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2655682Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2755682Smarkm * SUCH DAMAGE.
2855682Smarkm */
2955682Smarkm
3055682Smarkm/*
3155682Smarkm * Issues to be discussed:
3255682Smarkm * - Thread safe-ness must be checked
3355682Smarkm * - Return values.  There seems to be no standard for return value (RFC2553)
3455682Smarkm *   but INRIA implementation returns EAI_xxx defined for getaddrinfo().
3555682Smarkm * - RFC2553 says that we should raise error on short buffer.  X/Open says
3655682Smarkm *   we need to truncate the result.  We obey RFC2553 (and X/Open should be
3755682Smarkm *   modified).
3855682Smarkm */
39233294Sstas
4055682Smarkm#ifdef HAVE_CONFIG_H
4155682Smarkm#include <config.h>
4255682Smarkm#endif
4355682Smarkm
4455682Smarkm#ifndef lint
4555682Smarkmstatic const char rcsid[] =
4655682Smarkm     "@(#) $Header: /tcpdump/master/tcpdump/missing/getnameinfo.c,v 1.8 2000/10/24 00:56:53 fenner Exp $";
4755682Smarkm#endif
4855682Smarkm
4955682Smarkm#include <sys/types.h>
5055682Smarkm#include <sys/socket.h>
5155682Smarkm#include <net/if.h>
5255682Smarkm#include <netinet/in.h>
5355682Smarkm#include <arpa/inet.h>
5455682Smarkm#include <arpa/nameser.h>
5555682Smarkm#include <netdb.h>
5655682Smarkm#include <resolv.h>
5755682Smarkm#include <string.h>
5855682Smarkm#include <stddef.h>
5955682Smarkm#include <errno.h>
6055682Smarkm
6155682Smarkm#ifdef NEED_ADDRINFO_H
6255682Smarkm#include "addrinfo.h"
6355682Smarkm#endif
6455682Smarkm
6555682Smarkm#define SUCCESS 0
6655682Smarkm#define ANY 0
6755682Smarkm#define YES 1
6855682Smarkm#define NO  0
6955682Smarkm
7055682Smarkmstatic struct afd {
7155682Smarkm	int a_af;
7255682Smarkm	int a_addrlen;
7355682Smarkm	int a_socklen;
7455682Smarkm	int a_off;
7555682Smarkm} afdl [] = {
7655682Smarkm#ifdef INET6
7755682Smarkm	{PF_INET6, sizeof(struct in6_addr), sizeof(struct sockaddr_in6),
7855682Smarkm		offsetof(struct sockaddr_in6, sin6_addr)},
7955682Smarkm#endif
8055682Smarkm	{PF_INET, sizeof(struct in_addr), sizeof(struct sockaddr_in),
8155682Smarkm		offsetof(struct sockaddr_in, sin_addr)},
8255682Smarkm	{0, 0, 0},
8355682Smarkm};
8478527Sassar
8555682Smarkmstruct sockinet {
8655682Smarkm	u_char	si_len;
8755682Smarkm	u_char	si_family;
8855682Smarkm	u_short	si_port;
8955682Smarkm};
9055682Smarkm
9155682Smarkm#define ENI_NOSOCKET 	0
9255682Smarkm#define ENI_NOSERVNAME	1
9355682Smarkm#define ENI_NOHOSTNAME	2
9455682Smarkm#define ENI_MEMORY	3
9555682Smarkm#define ENI_SYSTEM	4
9655682Smarkm#define ENI_FAMILY	5
9755682Smarkm#define ENI_SALEN	6
9855682Smarkm
9955682Smarkmint
10055682Smarkmgetnameinfo(sa, salen, host, hostlen, serv, servlen, flags)
10155682Smarkm	const struct sockaddr *sa;
10255682Smarkm	size_t salen;
10355682Smarkm	char *host;
10455682Smarkm	size_t hostlen;
10578527Sassar	char *serv;
10678527Sassar	size_t servlen;
10778527Sassar	int flags;
10878527Sassar{
10978527Sassar	struct afd *afd;
11078527Sassar	struct servent *sp;
11178527Sassar	struct hostent *hp;
11278527Sassar	u_short port;
11378527Sassar	int family, i;
11478527Sassar	char *addr, *p;
11578527Sassar	u_int32_t v4a;
11678527Sassar	int h_error;
11778527Sassar	char numserv[512];
11878527Sassar	char numaddr[512];
11978527Sassar
12078527Sassar	if (sa == NULL)
12155682Smarkm		return ENI_NOSOCKET;
12255682Smarkm
12355682Smarkm#ifdef HAVE_SA_LEN	/*XXX*/
12455682Smarkm	if (sa->sa_len != salen)
12555682Smarkm		return ENI_SALEN;
12655682Smarkm#endif
12755682Smarkm
12855682Smarkm	family = sa->sa_family;
12955682Smarkm	for (i = 0; afdl[i].a_af; i++)
13055682Smarkm		if (afdl[i].a_af == family) {
13155682Smarkm			afd = &afdl[i];
13255682Smarkm			goto found;
13355682Smarkm		}
13455682Smarkm	return ENI_FAMILY;
13555682Smarkm
13655682Smarkm found:
13755682Smarkm	if (salen != afd->a_socklen)
13855682Smarkm		return ENI_SALEN;
13955682Smarkm
14055682Smarkm	port = ((struct sockinet *)sa)->si_port; /* network byte order */
14155682Smarkm	addr = (char *)sa + afd->a_off;
14255682Smarkm
14355682Smarkm	if (serv == NULL || servlen == 0) {
14455682Smarkm		/*
145178825Sdfr		 * do nothing in this case.
14655682Smarkm		 * in case you are wondering if "&&" is more correct than
14755682Smarkm		 * "||" here: RFC2553 says that serv == NULL OR servlen == 0
14855682Smarkm		 * means that the caller does not want the result.
14955682Smarkm		 */
15055682Smarkm	} else {
15155682Smarkm		if (flags & NI_NUMERICSERV)
15255682Smarkm			sp = NULL;
153178825Sdfr		else {
154178825Sdfr			sp = getservbyport(port,
155178825Sdfr				(flags & NI_DGRAM) ? "udp" : "tcp");
156178825Sdfr		}
15755682Smarkm		if (sp) {
158178825Sdfr			if (strlen(sp->s_name) + 1 > servlen)
159178825Sdfr				return ENI_MEMORY;
160178825Sdfr			strcpy(serv, sp->s_name);
161178825Sdfr		} else {
162178825Sdfr			snprintf(numserv, sizeof(numserv), "%d", ntohs(port));
163178825Sdfr			if (strlen(numserv) + 1 > servlen)
164178825Sdfr				return ENI_MEMORY;
165178825Sdfr			strcpy(serv, numserv);
16655682Smarkm		}
16755682Smarkm	}
168178825Sdfr
169178825Sdfr	switch (sa->sa_family) {
17055682Smarkm	case AF_INET:
17155682Smarkm		v4a = (u_int32_t)
17255682Smarkm			ntohl(((struct sockaddr_in *)sa)->sin_addr.s_addr);
17355682Smarkm		if (IN_MULTICAST(v4a) || IN_EXPERIMENTAL(v4a))
17455682Smarkm			flags |= NI_NUMERICHOST;
17555682Smarkm		v4a >>= IN_CLASSA_NSHIFT;
17655682Smarkm		if (v4a == 0)
17755682Smarkm			flags |= NI_NUMERICHOST;
17855682Smarkm		break;
17955682Smarkm#ifdef INET6
18055682Smarkm	case AF_INET6:
18155682Smarkm	    {
18255682Smarkm		struct sockaddr_in6 *sin6;
18355682Smarkm		sin6 = (struct sockaddr_in6 *)sa;
18455682Smarkm		switch (sin6->sin6_addr.s6_addr[0]) {
18555682Smarkm		case 0x00:
18655682Smarkm			if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
18755682Smarkm				;
18855682Smarkm			else if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr))
18955682Smarkm				;
19055682Smarkm			else
19155682Smarkm				flags |= NI_NUMERICHOST;
192233294Sstas			break;
19355682Smarkm		default:
19455682Smarkm			if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
19555682Smarkm				flags |= NI_NUMERICHOST;
19655682Smarkm			}
19755682Smarkm			else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
19855682Smarkm				flags |= NI_NUMERICHOST;
19955682Smarkm			break;
20055682Smarkm		}
20155682Smarkm	    }
20255682Smarkm		break;
20355682Smarkm#endif
20455682Smarkm	}
20555682Smarkm	if (host == NULL || hostlen == 0) {
20655682Smarkm		/*
20755682Smarkm		 * do nothing in this case.
20855682Smarkm		 * in case you are wondering if "&&" is more correct than
20955682Smarkm		 * "||" here: RFC2553 says that host == NULL OR hostlen == 0
21055682Smarkm		 * means that the caller does not want the result.
21155682Smarkm		 */
21255682Smarkm	} else if (flags & NI_NUMERICHOST) {
21355682Smarkm		/* NUMERICHOST and NAMEREQD conflicts with each other */
21455682Smarkm		if (flags & NI_NAMEREQD)
21555682Smarkm			return ENI_NOHOSTNAME;
21655682Smarkm		if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr))
21755682Smarkm		    == NULL)
21855682Smarkm			return ENI_SYSTEM;
21955682Smarkm		if (strlen(numaddr) + 1 > hostlen)
22055682Smarkm			return ENI_MEMORY;
22155682Smarkm		strcpy(host, numaddr);
22255682Smarkm#if defined(INET6) && defined(NI_WITHSCOPEID)
22355682Smarkm		if (afd->a_af == AF_INET6 &&
22455682Smarkm		    (IN6_IS_ADDR_LINKLOCAL((struct in6_addr *)addr) ||
22555682Smarkm		     IN6_IS_ADDR_MULTICAST((struct in6_addr *)addr)) &&
22655682Smarkm		    ((struct sockaddr_in6 *)sa)->sin6_scope_id) {
22755682Smarkm#ifndef ALWAYS_WITHSCOPE
22855682Smarkm			if (flags & NI_WITHSCOPEID)
22955682Smarkm#endif /* !ALWAYS_WITHSCOPE */
23055682Smarkm			{
23155682Smarkm				char *ep = strchr(host, '\0');
23255682Smarkm				unsigned int ifindex =
23355682Smarkm					((struct sockaddr_in6 *)sa)->sin6_scope_id;
23455682Smarkm
23555682Smarkm				*ep = SCOPE_DELIMITER;
23655682Smarkm				if ((if_indextoname(ifindex, ep + 1)) == NULL)
23755682Smarkm					/* XXX what should we do? */
23855682Smarkm					strncpy(ep + 1, "???", 3);
23955682Smarkm			}
24055682Smarkm		}
24155682Smarkm#endif /* INET6 */
24255682Smarkm	} else {
24355682Smarkm#ifdef USE_GETIPNODEBY
24455682Smarkm		hp = getipnodebyaddr(addr, afd->a_addrlen, afd->a_af, &h_error);
24555682Smarkm#else
24655682Smarkm		hp = gethostbyaddr(addr, afd->a_addrlen, afd->a_af);
24755682Smarkm#ifdef HAVE_H_ERRNO
24855682Smarkm		h_error = h_errno;
24955682Smarkm#else
25055682Smarkm		h_error = EINVAL;
25155682Smarkm#endif
25255682Smarkm#endif
25355682Smarkm
25455682Smarkm		if (hp) {
25555682Smarkm			if (flags & NI_NOFQDN) {
25655682Smarkm				p = strchr(hp->h_name, '.');
25755682Smarkm				if (p) *p = '\0';
25855682Smarkm			}
25955682Smarkm			if (strlen(hp->h_name) + 1 > hostlen) {
26055682Smarkm#ifdef USE_GETIPNODEBY
26155682Smarkm				freehostent(hp);
26255682Smarkm#endif
26355682Smarkm				return ENI_MEMORY;
26455682Smarkm			}
26555682Smarkm			strcpy(host, hp->h_name);
26655682Smarkm#ifdef USE_GETIPNODEBY
26755682Smarkm			freehostent(hp);
26855682Smarkm#endif
26955682Smarkm		} else {
27055682Smarkm			if (flags & NI_NAMEREQD)
27155682Smarkm				return ENI_NOHOSTNAME;
27255682Smarkm			if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr))
27355682Smarkm			    == NULL)
27455682Smarkm				return ENI_NOHOSTNAME;
27555682Smarkm			if (strlen(numaddr) + 1 > hostlen)
27655682Smarkm				return ENI_MEMORY;
27755682Smarkm			strcpy(host, numaddr);
27855682Smarkm		}
27955682Smarkm	}
28055682Smarkm	return SUCCESS;
28155682Smarkm}
28255682Smarkm