1/*
2 * Copyright (C) 2009, 2011-2013  Internet Systems Consortium, Inc. ("ISC")
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
9 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
11 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
13 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14 * PERFORMANCE OF THIS SOFTWARE.
15 */
16
17/* $Id$ */
18
19/*! \file */
20
21/*
22 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
23 * All rights reserved.
24 *
25 * Redistribution and use in source and binary forms, with or without
26 * modification, are permitted provided that the following conditions
27 * are met:
28 * 1. Redistributions of source code must retain the above copyright
29 *    notice, this list of conditions and the following disclaimer.
30 * 2. Redistributions in binary form must reproduce the above copyright
31 *    notice, this list of conditions and the following disclaimer in the
32 *    documentation and/or other materials provided with the distribution.
33 * 3. Neither the name of the project nor the names of its contributors
34 *    may be used to endorse or promote products derived from this software
35 *    without specific prior written permission.
36 *
37 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
38 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
39 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
40 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
41 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
42 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
43 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
44 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
45 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
46 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47 * SUCH DAMAGE.
48 */
49
50/**
51 *    getnameinfo() returns the hostname for the struct sockaddr sa which is
52 *    salen bytes long. The hostname is of length hostlen and is returned via
53 *    *host. The maximum length of the hostname is 1025 bytes: #NI_MAXHOST.
54 *
55 *    The name of the service associated with the port number in sa is
56 *    returned in *serv. It is servlen bytes long. The maximum length of the
57 *    service name is #NI_MAXSERV - 32 bytes.
58 *
59 *    The flags argument sets the following bits:
60 *
61 * \li   #NI_NOFQDN:
62 *           A fully qualified domain name is not required for local hosts.
63 *           The local part of the fully qualified domain name is returned
64 *           instead.
65 *
66 * \li   #NI_NUMERICHOST
67 *           Return the address in numeric form, as if calling inet_ntop(),
68 *           instead of a host name.
69 *
70 * \li   #NI_NAMEREQD
71 *           A name is required. If the hostname cannot be found in the DNS
72 *           and this flag is set, a non-zero error code is returned. If the
73 *           hostname is not found and the flag is not set, the address is
74 *           returned in numeric form.
75 *
76 * \li   #NI_NUMERICSERV
77 *           The service name is returned as a digit string representing the
78 *           port number.
79 *
80 * \li   #NI_DGRAM
81 *           Specifies that the service being looked up is a datagram
82 *           service, and causes getservbyport() to be called with a second
83 *           argument of "udp" instead of its default of "tcp". This is
84 *           required for the few ports (512-514) that have different
85 *           services for UDP and TCP.
86 *
87 * \section getnameinfo_return Return Values
88 *
89 *    getnameinfo() returns 0 on success or a non-zero error code if
90 *    an error occurs.
91 *
92 * \section getname_see See Also
93 *
94 *    RFC3493, getservbyport(),
95 *    getnamebyaddr(). inet_ntop().
96 */
97
98#include <config.h>
99
100#include <stdio.h>
101#include <string.h>
102
103#include <isc/netaddr.h>
104#include <isc/print.h>
105#include <isc/sockaddr.h>
106#include <isc/util.h>
107
108#include <dns/byaddr.h>
109#include <dns/client.h>
110#include <dns/fixedname.h>
111#include <dns/name.h>
112#include <dns/rdata.h>
113#include <dns/rdataset.h>
114#include <dns/rdatastruct.h>
115#include <dns/result.h>
116
117#include <irs/context.h>
118#include <irs/netdb.h>
119
120#define SUCCESS 0
121
122/*% afd structure definition */
123static struct afd {
124	int a_af;
125	size_t a_addrlen;
126	size_t a_socklen;
127} afdl [] = {
128	/*!
129	 * First entry is linked last...
130	 */
131	{ AF_INET, sizeof(struct in_addr), sizeof(struct sockaddr_in) },
132	{ AF_INET6, sizeof(struct in6_addr), sizeof(struct sockaddr_in6) },
133	{0, 0, 0},
134};
135
136/*!
137 * The test against 0 is there to keep the Solaris compiler
138 * from complaining about "end-of-loop code not reached".
139 */
140#define ERR(code) \
141	do { result = (code);			\
142		if (result != 0) goto cleanup;	\
143	} while (0)
144
145int
146getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host,
147	    IRS_GETNAMEINFO_BUFLEN_T hostlen, char *serv,
148	    IRS_GETNAMEINFO_BUFLEN_T servlen, IRS_GETNAMEINFO_FLAGS_T flags)
149{
150	struct afd *afd = NULL;
151	struct servent *sp;
152	unsigned short port = 0;
153#ifdef IRS_PLATFORM_HAVESALEN
154	size_t len;
155#endif
156	int family, i;
157	const void *addr = NULL;
158	char *p;
159#if 0
160	unsigned long v4a;
161	unsigned char pfx;
162#endif
163	char numserv[sizeof("65000")];
164	char numaddr[sizeof("abcd:abcd:abcd:abcd:abcd:abcd:255.255.255.255")
165		    + 1 + sizeof("4294967295")];
166	const char *proto;
167	int result = SUCCESS;
168
169	if (sa == NULL)
170		ERR(EAI_FAIL);
171
172#ifdef IRS_PLATFORM_HAVESALEN
173	len = sa->sa_len;
174	if (len != salen)
175		ERR(EAI_FAIL);
176#endif
177
178	family = sa->sa_family;
179	for (i = 0; afdl[i].a_af; i++)
180		if (afdl[i].a_af == family) {
181			afd = &afdl[i];
182			goto found;
183		}
184	ERR(EAI_FAMILY);
185
186 found:
187	if (salen != afd->a_socklen)
188		ERR(EAI_FAIL);
189
190	switch (family) {
191	case AF_INET:
192		port = ((const struct sockaddr_in *)sa)->sin_port;
193		addr = &((const struct sockaddr_in *)sa)->sin_addr.s_addr;
194		break;
195
196	case AF_INET6:
197		port = ((const struct sockaddr_in6 *)sa)->sin6_port;
198		addr = ((const struct sockaddr_in6 *)sa)->sin6_addr.s6_addr;
199		break;
200
201	default:
202		INSIST(0);
203	}
204	proto = (flags & NI_DGRAM) ? "udp" : "tcp";
205
206	if (serv == NULL || servlen == 0U) {
207		/*
208		 * Caller does not want service.
209		 */
210	} else if ((flags & NI_NUMERICSERV) != 0 ||
211		   (sp = getservbyport(port, proto)) == NULL) {
212		snprintf(numserv, sizeof(numserv), "%d", ntohs(port));
213		if ((strlen(numserv) + 1) > servlen)
214			ERR(EAI_OVERFLOW);
215		strcpy(serv, numserv);
216	} else {
217		if ((strlen(sp->s_name) + 1) > servlen)
218			ERR(EAI_OVERFLOW);
219		strcpy(serv, sp->s_name);
220	}
221
222#if 0
223	switch (sa->sa_family) {
224	case AF_INET:
225		v4a = ((struct sockaddr_in *)sa)->sin_addr.s_addr;
226		if (IN_MULTICAST(v4a) || IN_EXPERIMENTAL(v4a))
227			flags |= NI_NUMERICHOST;
228		v4a >>= IN_CLASSA_NSHIFT;
229		if (v4a == 0 || v4a == IN_LOOPBACKNET)
230			flags |= NI_NUMERICHOST;
231		break;
232
233	case AF_INET6:
234		pfx = ((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr[0];
235		if (pfx == 0 || pfx == 0xfe || pfx == 0xff)
236			flags |= NI_NUMERICHOST;
237		break;
238	}
239#endif
240
241	if (host == NULL || hostlen == 0U) {
242		/*
243		 * do nothing in this case.
244		 * in case you are wondering if "&&" is more correct than
245		 * "||" here: RFC3493 says that host == NULL or hostlen == 0
246		 * means that the caller does not want the result.
247		 */
248	} else if ((flags & NI_NUMERICHOST) != 0) {
249		if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr))
250		    == NULL)
251			ERR(EAI_SYSTEM);
252#if defined(IRS_HAVE_SIN6_SCOPE_ID)
253		if (afd->a_af == AF_INET6 &&
254		    ((const struct sockaddr_in6 *)sa)->sin6_scope_id) {
255			char *p = numaddr + strlen(numaddr);
256			const char *stringscope = NULL;
257#ifdef VENDOR_SPECIFIC
258			/*
259			 * Vendors may want to add support for
260			 * non-numeric scope identifier.
261			 */
262			stringscope = foo;
263#endif
264			if (stringscope == NULL) {
265				snprintf(p, sizeof(numaddr) - (p - numaddr),
266				    "%%%u",
267				    ((const struct sockaddr_in6 *)sa)->sin6_scope_id);
268			} else {
269				snprintf(p, sizeof(numaddr) - (p - numaddr),
270				    "%%%s", stringscope);
271			}
272		}
273#endif
274		if (strlen(numaddr) + 1 > hostlen)
275			ERR(EAI_OVERFLOW);
276		strcpy(host, numaddr);
277	} else {
278		isc_netaddr_t netaddr;
279		dns_fixedname_t ptrfname;
280		dns_name_t *ptrname;
281		irs_context_t *irsctx = NULL;
282		dns_client_t *client;
283		isc_boolean_t found = ISC_FALSE;
284		dns_namelist_t answerlist;
285		dns_rdataset_t *rdataset;
286		isc_region_t hostregion;
287		char hoststr[1024]; /* is this enough? */
288		isc_result_t iresult;
289
290		/* Get IRS context and the associated DNS client object */
291		iresult = irs_context_get(&irsctx);
292		if (iresult != ISC_R_SUCCESS)
293			ERR(EAI_FAIL);
294		client = irs_context_getdnsclient(irsctx);
295
296		/* Make query name */
297		isc_netaddr_fromsockaddr(&netaddr, (const isc_sockaddr_t *)sa);
298		dns_fixedname_init(&ptrfname);
299		ptrname = dns_fixedname_name(&ptrfname);
300		iresult = dns_byaddr_createptrname2(&netaddr, 0, ptrname);
301		if (iresult != ISC_R_SUCCESS)
302			ERR(EAI_FAIL);
303
304		/* Get the PTR RRset */
305		ISC_LIST_INIT(answerlist);
306		iresult = dns_client_resolve(client, ptrname,
307					     dns_rdataclass_in,
308					     dns_rdatatype_ptr,
309					     DNS_CLIENTRESOPT_ALLOWRUN,
310					     &answerlist);
311		switch (iresult) {
312		case ISC_R_SUCCESS:
313		/*
314		 * a 'non-existent' error is not necessarily fatal for
315		 * getnameinfo().
316		 */
317		case DNS_R_NCACHENXDOMAIN:
318		case DNS_R_NCACHENXRRSET:
319			break;
320		case DNS_R_SIGINVALID:
321		case DNS_R_SIGEXPIRED:
322		case DNS_R_SIGFUTURE:
323		case DNS_R_KEYUNAUTHORIZED:
324		case DNS_R_MUSTBESECURE:
325		case DNS_R_COVERINGNSEC:
326		case DNS_R_NOTAUTHORITATIVE:
327		case DNS_R_NOVALIDKEY:
328		case DNS_R_NOVALIDDS:
329		case DNS_R_NOVALIDSIG:
330			ERR(EAI_INSECUREDATA);
331			break;
332		default:
333			ERR(EAI_FAIL);
334		}
335
336		/* Parse the answer for the hostname */
337		for (ptrname = ISC_LIST_HEAD(answerlist); ptrname != NULL;
338		     ptrname = ISC_LIST_NEXT(ptrname, link)) {
339			for (rdataset = ISC_LIST_HEAD(ptrname->list);
340			     rdataset != NULL;
341			     rdataset = ISC_LIST_NEXT(rdataset, link)) {
342				if (!dns_rdataset_isassociated(rdataset))
343					continue;
344				if (rdataset->type != dns_rdatatype_ptr)
345					continue;
346
347				for (iresult = dns_rdataset_first(rdataset);
348				     iresult == ISC_R_SUCCESS;
349				     iresult = dns_rdataset_next(rdataset)) {
350					dns_rdata_t rdata;
351					dns_rdata_ptr_t rdata_ptr;
352					isc_buffer_t b;
353
354					dns_rdata_init(&rdata);
355					dns_rdataset_current(rdataset, &rdata);
356					dns_rdata_tostruct(&rdata, &rdata_ptr,
357							   NULL);
358
359					isc_buffer_init(&b, hoststr,
360							sizeof(hoststr));
361					iresult =
362						dns_name_totext(&rdata_ptr.ptr,
363								ISC_TRUE, &b);
364					dns_rdata_freestruct(&rdata_ptr);
365					if (iresult == ISC_R_SUCCESS) {
366						/*
367						 * We ignore the rest of the
368						 * answer.  After all,
369						 * getnameinfo() can return
370						 * at most one hostname.
371						 */
372						found = ISC_TRUE;
373						isc_buffer_usedregion(
374							&b, &hostregion);
375						goto ptrfound;
376					}
377
378				}
379			}
380		}
381	ptrfound:
382		dns_client_freeresanswer(client, &answerlist);
383		if (found) {
384			if ((flags & NI_NOFQDN) != 0) {
385				p = strchr(hoststr, '.');
386				if (p)
387					*p = '\0';
388			}
389			if (hostregion.length + 1 > hostlen)
390				ERR(EAI_OVERFLOW);
391			snprintf(host, hostlen, "%.*s",
392				 (int)hostregion.length,
393				 (char *)hostregion.base);
394		} else {
395			if ((flags & NI_NAMEREQD) != 0)
396				ERR(EAI_NONAME);
397			if (inet_ntop(afd->a_af, addr, numaddr,
398				      sizeof(numaddr)) == NULL)
399				ERR(EAI_SYSTEM);
400			if ((strlen(numaddr) + 1) > hostlen)
401				ERR(EAI_OVERFLOW);
402			strcpy(host, numaddr);
403		}
404	}
405	result = SUCCESS;
406
407 cleanup:
408	return (result);
409}
410