1/*
2 * Portions Copyright (C) 2004, 2005, 2007, 2011, 2012  Internet Systems Consortium, Inc. ("ISC")
3 * Portions Copyright (C) 1999-2001, 2003  Internet Software Consortium.
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15 * PERFORMANCE OF THIS SOFTWARE.
16 */
17
18/* $Id$ */
19
20/*! \file */
21
22/*
23 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
24 * All rights reserved.
25 *
26 * Redistribution and use in source and binary forms, with or without
27 * modification, are permitted provided that the following conditions
28 * are met:
29 * 1. Redistributions of source code must retain the above copyright
30 *    notice, this list of conditions and the following disclaimer.
31 * 2. Redistributions in binary form must reproduce the above copyright
32 *    notice, this list of conditions and the following disclaimer in the
33 *    documentation and/or other materials provided with the distribution.
34 * 3. Neither the name of the project nor the names of its contributors
35 *    may be used to endorse or promote products derived from this software
36 *    without specific prior written permission.
37 *
38 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
39 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
40 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
41 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
42 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
43 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
44 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
45 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
46 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
47 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48 * SUCH DAMAGE.
49 */
50
51/*
52 * XXX
53 * Issues to be discussed:
54 * - Return values.  There seems to be no standard for return value (RFC2553)
55 *   but INRIA implementation returns EAI_xxx defined for getaddrinfo().
56 */
57
58
59/**
60 *    This function is equivalent to the getnameinfo(3) function defined in
61 *    RFC2133. lwres_getnameinfo() returns the hostname for the struct
62 *    sockaddr sa which is salen bytes long. The hostname is of length
63 *    hostlen and is returned via *host. The maximum length of the hostname
64 *    is 1025 bytes: #NI_MAXHOST.
65 *
66 *    The name of the service associated with the port number in sa is
67 *    returned in *serv. It is servlen bytes long. The maximum length of the
68 *    service name is #NI_MAXSERV - 32 bytes.
69 *
70 *    The flags argument sets the following bits:
71 *
72 * \li   #NI_NOFQDN:
73 *           A fully qualified domain name is not required for local hosts.
74 *           The local part of the fully qualified domain name is returned
75 *           instead.
76 *
77 * \li   #NI_NUMERICHOST
78 *           Return the address in numeric form, as if calling inet_ntop(),
79 *           instead of a host name.
80 *
81 * \li   #NI_NAMEREQD
82 *           A name is required. If the hostname cannot be found in the DNS
83 *           and this flag is set, a non-zero error code is returned. If the
84 *           hostname is not found and the flag is not set, the address is
85 *           returned in numeric form.
86 *
87 * \li   #NI_NUMERICSERV
88 *           The service name is returned as a digit string representing the
89 *           port number.
90 *
91 * \li   #NI_DGRAM
92 *           Specifies that the service being looked up is a datagram
93 *           service, and causes getservbyport() to be called with a second
94 *           argument of "udp" instead of its default of "tcp". This is
95 *           required for the few ports (512-514) that have different
96 *           services for UDP and TCP.
97 *
98 * \section getnameinfo_return Return Values
99 *
100 *    lwres_getnameinfo() returns 0 on success or a non-zero error code if
101 *    an error occurs.
102 *
103 * \section getname_see See Also
104 *
105 *    RFC2133, getservbyport(),
106 *    lwres_getnamebyaddr(). lwres_net_ntop().
107 *
108 * \section getnameinfo_bugs Bugs
109 *
110 *    RFC2133 fails to define what the nonzero return values of
111 *    getnameinfo() are.
112 */
113
114#include <config.h>
115
116#include <stdio.h>
117#include <string.h>
118
119#include <lwres/lwres.h>
120#include <lwres/net.h>
121#include <lwres/netdb.h>
122#include "print_p.h"
123
124#include "assert_p.h"
125
126#define SUCCESS 0
127
128/*% afd structure definition */
129static struct afd {
130	int a_af;
131	size_t a_addrlen;
132	size_t a_socklen;
133} afdl [] = {
134	/*!
135	 * First entry is linked last...
136	 */
137	{ AF_INET, sizeof(struct in_addr), sizeof(struct sockaddr_in) },
138	{ AF_INET6, sizeof(struct in6_addr), sizeof(struct sockaddr_in6) },
139	{0, 0, 0},
140};
141
142#define ENI_NOSERVNAME	1
143#define ENI_NOHOSTNAME	2
144#define ENI_MEMORY	3
145#define ENI_SYSTEM	4
146#define ENI_FAMILY	5
147#define ENI_SALEN	6
148#define ENI_NOSOCKET 	7
149
150/*!
151 * The test against 0 is there to keep the Solaris compiler
152 * from complaining about "end-of-loop code not reached".
153 */
154#define ERR(code) \
155	do { result = (code);			\
156		if (result != 0) goto cleanup;	\
157	} while (0)
158
159/*% lightweight resolver socket address structure to hostname and service name */
160int
161lwres_getnameinfo(const struct sockaddr *sa, size_t salen, char *host,
162		  size_t hostlen, char *serv, size_t servlen, int flags)
163{
164	struct afd *afd;
165	struct servent *sp;
166	unsigned short port;
167#ifdef LWRES_PLATFORM_HAVESALEN
168	size_t len;
169#endif
170	int family, i;
171	const void *addr;
172	char *p;
173#if 0
174	unsigned long v4a;
175	unsigned char pfx;
176#endif
177	char numserv[sizeof("65000")];
178	char numaddr[sizeof("abcd:abcd:abcd:abcd:abcd:abcd:255.255.255.255")
179		    + 1 + sizeof("4294967295")];
180	const char *proto;
181	lwres_uint32_t lwf = 0;
182	lwres_context_t *lwrctx = NULL;
183	lwres_gnbaresponse_t *by = NULL;
184	int result = SUCCESS;
185	int n;
186
187	if (sa == NULL)
188		ERR(ENI_NOSOCKET);
189
190#ifdef LWRES_PLATFORM_HAVESALEN
191	len = sa->sa_len;
192	if (len != salen)
193		ERR(ENI_SALEN);
194#endif
195
196	family = sa->sa_family;
197	for (i = 0; afdl[i].a_af; i++)
198		if (afdl[i].a_af == family) {
199			afd = &afdl[i];
200			goto found;
201		}
202	ERR(ENI_FAMILY);
203
204 found:
205	if (salen != afd->a_socklen)
206		ERR(ENI_SALEN);
207
208	switch (family) {
209	case AF_INET:
210		port = ((const struct sockaddr_in *)sa)->sin_port;
211		addr = &((const struct sockaddr_in *)sa)->sin_addr.s_addr;
212		break;
213
214	case AF_INET6:
215		port = ((const struct sockaddr_in6 *)sa)->sin6_port;
216		addr = ((const struct sockaddr_in6 *)sa)->sin6_addr.s6_addr;
217		break;
218
219	default:
220		port = 0;
221		addr = NULL;
222		POST(port); POST(addr);
223		INSIST(0);
224	}
225	proto = (flags & NI_DGRAM) ? "udp" : "tcp";
226
227	if (serv == NULL || servlen == 0U) {
228		/*
229		 * Caller does not want service.
230		 */
231	} else if ((flags & NI_NUMERICSERV) != 0 ||
232		   (sp = getservbyport(port, proto)) == NULL) {
233		snprintf(numserv, sizeof(numserv), "%d", ntohs(port));
234		if ((strlen(numserv) + 1) > servlen)
235			ERR(ENI_MEMORY);
236		strcpy(serv, numserv);
237	} else {
238		if ((strlen(sp->s_name) + 1) > servlen)
239			ERR(ENI_MEMORY);
240		strcpy(serv, sp->s_name);
241	}
242
243#if 0
244	switch (sa->sa_family) {
245	case AF_INET:
246		v4a = ((struct sockaddr_in *)sa)->sin_addr.s_addr;
247		if (IN_MULTICAST(v4a) || IN_EXPERIMENTAL(v4a))
248			flags |= NI_NUMERICHOST;
249		v4a >>= IN_CLASSA_NSHIFT;
250		if (v4a == 0 || v4a == IN_LOOPBACKNET)
251			flags |= NI_NUMERICHOST;
252		break;
253
254	case AF_INET6:
255		pfx = ((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr[0];
256		if (pfx == 0 || pfx == 0xfe || pfx == 0xff)
257			flags |= NI_NUMERICHOST;
258		break;
259	}
260#endif
261
262	if (host == NULL || hostlen == 0U) {
263		/*
264		 * What should we do?
265		 */
266	} else if (flags & NI_NUMERICHOST) {
267		if (lwres_net_ntop(afd->a_af, addr, numaddr, sizeof(numaddr))
268		    == NULL)
269			ERR(ENI_SYSTEM);
270#if defined(LWRES_HAVE_SIN6_SCOPE_ID)
271		if (afd->a_af == AF_INET6 &&
272		    ((const struct sockaddr_in6 *)sa)->sin6_scope_id) {
273			char *p = numaddr + strlen(numaddr);
274			const char *stringscope = NULL;
275#if 0
276			if ((flags & NI_NUMERICSCOPE) == 0) {
277				/*
278				 * Vendors may want to add support for
279				 * non-numeric scope identifier.
280				 */
281				stringscope = foo;
282			}
283#endif
284			if (stringscope == NULL) {
285				snprintf(p, sizeof(numaddr) - (p - numaddr),
286				    "%%%u",
287				    ((const struct sockaddr_in6 *)sa)->sin6_scope_id);
288			} else {
289				snprintf(p, sizeof(numaddr) - (p - numaddr),
290				    "%%%s", stringscope);
291			}
292		}
293#endif
294		if (strlen(numaddr) + 1 > hostlen)
295			ERR(ENI_MEMORY);
296		strcpy(host, numaddr);
297	} else {
298		switch (family) {
299		case AF_INET:
300			lwf = LWRES_ADDRTYPE_V4;
301			break;
302		case AF_INET6:
303			lwf = LWRES_ADDRTYPE_V6;
304			break;
305		default:
306			INSIST(0);
307		}
308
309		n = lwres_context_create(&lwrctx, NULL, NULL, NULL, 0);
310		if (n == 0)
311			(void) lwres_conf_parse(lwrctx, lwres_resolv_conf);
312
313		if (n == 0)
314			n = lwres_getnamebyaddr(lwrctx, lwf,
315						(lwres_uint16_t)afd->a_addrlen,
316						addr, &by);
317		if (n == 0) {
318			if (flags & NI_NOFQDN) {
319				p = strchr(by->realname, '.');
320				if (p)
321					*p = '\0';
322			}
323			if ((strlen(by->realname) + 1) > hostlen)
324				ERR(ENI_MEMORY);
325			strcpy(host, by->realname);
326		} else {
327			if (flags & NI_NAMEREQD)
328				ERR(ENI_NOHOSTNAME);
329			if (lwres_net_ntop(afd->a_af, addr, numaddr,
330					   sizeof(numaddr))
331			    == NULL)
332				ERR(ENI_NOHOSTNAME);
333			if ((strlen(numaddr) + 1) > hostlen)
334				ERR(ENI_MEMORY);
335			strcpy(host, numaddr);
336		}
337	}
338	result = SUCCESS;
339 cleanup:
340	if (by != NULL)
341		lwres_gnbaresponse_free(lwrctx, &by);
342	if (lwrctx != NULL) {
343		lwres_conf_clear(lwrctx);
344		lwres_context_destroy(&lwrctx);
345	}
346	return (result);
347}
348