1/*	$NetBSD$	*/
2
3/*
4 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the project nor the names of its contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32/*
33 * Issues to be discussed:
34 * - Thread safe-ness must be checked
35 * - Return values.  There seems to be no standard for return value (RFC2553)
36 *   but INRIA implementation returns EAI_xxx defined for getaddrinfo().
37 */
38
39#include <sys/types.h>
40#include <sys/socket.h>
41#include <netinet/in.h>
42#include <arpa/inet.h>
43#include <arpa/nameser.h>
44#include <netdb.h>
45#include <resolv.h>
46#include <string.h>
47#include <stddef.h>
48
49#include "addrinfo.h"
50
51#define SUCCESS 0
52#define ANY 0
53#define YES 1
54#define NO  0
55
56static struct afd {
57	int a_af;
58	int a_addrlen;
59	int a_socklen;
60	int a_off;
61} afdl [] = {
62#ifdef INET6
63	{PF_INET6, sizeof(struct in6_addr), sizeof(struct sockaddr_in6),
64		offsetof(struct sockaddr_in6, sin6_addr)},
65#endif
66	{PF_INET, sizeof(struct in_addr), sizeof(struct sockaddr_in),
67		offsetof(struct sockaddr_in, sin_addr)},
68	{0, 0, 0},
69};
70
71struct sockinet {
72	u_char	si_len;
73	u_char	si_family;
74	u_short	si_port;
75};
76
77#define ENI_NOSOCKET 	0
78#define ENI_NOSERVNAME	1
79#define ENI_NOHOSTNAME	2
80#define ENI_MEMORY	3
81#define ENI_SYSTEM	4
82#define ENI_FAMILY	5
83#define ENI_SALEN	6
84
85int
86getnameinfo(sa, salen, host, hostlen, serv, servlen, flags)
87	const struct sockaddr *sa;
88	size_t salen;
89	char *host;
90	size_t hostlen;
91	char *serv;
92	size_t servlen;
93	int flags;
94{
95	struct afd *afd;
96	struct servent *sp;
97	struct hostent *hp;
98	u_short port;
99	int family, len, i;
100	char *addr, *p;
101	u_long v4a;
102	int h_error;
103	char numserv[512];
104	char numaddr[512];
105
106	if (sa == NULL)
107		return ENI_NOSOCKET;
108
109#ifdef HAVE_SA_LEN
110	len = sa->sa_len;
111	if (len != salen) return ENI_SALEN;
112#else
113	len = salen;
114#endif
115
116	family = sa->sa_family;
117	for (i = 0; afdl[i].a_af; i++)
118		if (afdl[i].a_af == family) {
119			afd = &afdl[i];
120			goto found;
121		}
122	return ENI_FAMILY;
123
124 found:
125	if (len != afd->a_socklen) return ENI_SALEN;
126
127	port = ((struct sockinet *)sa)->si_port; /* network byte order */
128	addr = (char *)sa + afd->a_off;
129
130	if (serv == NULL || servlen == 0) {
131		/* what we should do? */
132	} else if (flags & NI_NUMERICSERV) {
133		snprintf(numserv, sizeof(numserv), "%d", ntohs(port));
134		if (strlen(numserv) > servlen)
135			return ENI_MEMORY;
136		strcpy(serv, numserv);
137	} else {
138		sp = getservbyport(port, (flags & NI_DGRAM) ? "udp" : "tcp");
139		if (sp) {
140			if (strlen(sp->s_name) > servlen)
141				return ENI_MEMORY;
142			strcpy(serv, sp->s_name);
143		} else
144			return ENI_NOSERVNAME;
145	}
146
147	switch (sa->sa_family) {
148	case AF_INET:
149		v4a = ntohl(((struct sockaddr_in *)sa)->sin_addr.s_addr);
150		if (IN_MULTICAST(v4a) || IN_EXPERIMENTAL(v4a))
151			flags |= NI_NUMERICHOST;
152		v4a >>= IN_CLASSA_NSHIFT;
153		if (v4a == 0 || v4a == IN_LOOPBACKNET)
154			flags |= NI_NUMERICHOST;
155		break;
156#ifdef INET6
157	case AF_INET6:
158	    {
159		struct sockaddr_in6 *sin6;
160		sin6 = (struct sockaddr_in6 *)sa;
161		switch (sin6->sin6_addr.s6_addr[0]) {
162		case 0x00:
163			if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
164				;
165			else if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr))
166				;
167			else
168				flags |= NI_NUMERICHOST;
169			break;
170		default:
171			if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
172				flags |= NI_NUMERICHOST;
173			else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
174				flags |= NI_NUMERICHOST;
175			break;
176		}
177	    }
178		break;
179#endif
180	}
181	if (host == NULL || hostlen == 0) {
182		/* what should we do? */
183	} else if (flags & NI_NUMERICHOST) {
184		/* NUMERICHOST and NAMEREQD conflicts with each other */
185		if (flags & NI_NAMEREQD)
186			return ENI_NOHOSTNAME;
187		if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr))
188		    == NULL)
189			return ENI_SYSTEM;
190		if (strlen(numaddr) > hostlen)
191			return ENI_MEMORY;
192		strcpy(host, numaddr);
193	} else {
194#ifdef USE_GETIPNODEBY
195		hp = getipnodebyaddr(addr, afd->a_addrlen, afd->a_af, &h_error);
196#else
197		hp = gethostbyaddr(addr, afd->a_addrlen, afd->a_af);
198		h_error = h_errno;
199#endif
200
201		if (hp) {
202			if (flags & NI_NOFQDN) {
203				p = strchr(hp->h_name, '.');
204				if (p) *p = '\0';
205			}
206			if (strlen(hp->h_name) > hostlen) {
207#ifdef USE_GETIPNODEBY
208				freehostent(hp);
209#endif
210				return ENI_MEMORY;
211			}
212			strcpy(host, hp->h_name);
213#ifdef USE_GETIPNODEBY
214			freehostent(hp);
215#endif
216		} else {
217			if (flags & NI_NAMEREQD)
218				return ENI_NOHOSTNAME;
219			if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr))
220			    == NULL)
221				return ENI_NOHOSTNAME;
222			if (strlen(numaddr) > hostlen)
223				return ENI_MEMORY;
224			strcpy(host, numaddr);
225		}
226	}
227	return SUCCESS;
228}
229