1224090Sdougb/*
2254402Serwin * Copyright (C) 2009, 2011-2013  Internet Systems Consortium, Inc. ("ISC")
3224090Sdougb *
4224090Sdougb * Permission to use, copy, modify, and/or distribute this software for any
5224090Sdougb * purpose with or without fee is hereby granted, provided that the above
6224090Sdougb * copyright notice and this permission notice appear in all copies.
7224090Sdougb *
8224090Sdougb * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
9224090Sdougb * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10224090Sdougb * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
11224090Sdougb * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12224090Sdougb * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
13224090Sdougb * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14224090Sdougb * PERFORMANCE OF THIS SOFTWARE.
15224090Sdougb */
16224090Sdougb
17234010Sdougb/* $Id$ */
18224090Sdougb
19224090Sdougb/*! \file */
20224090Sdougb
21224090Sdougb/*
22224090Sdougb * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
23224090Sdougb * All rights reserved.
24224090Sdougb *
25224090Sdougb * Redistribution and use in source and binary forms, with or without
26224090Sdougb * modification, are permitted provided that the following conditions
27224090Sdougb * are met:
28224090Sdougb * 1. Redistributions of source code must retain the above copyright
29224090Sdougb *    notice, this list of conditions and the following disclaimer.
30224090Sdougb * 2. Redistributions in binary form must reproduce the above copyright
31224090Sdougb *    notice, this list of conditions and the following disclaimer in the
32224090Sdougb *    documentation and/or other materials provided with the distribution.
33224090Sdougb * 3. Neither the name of the project nor the names of its contributors
34224090Sdougb *    may be used to endorse or promote products derived from this software
35224090Sdougb *    without specific prior written permission.
36224090Sdougb *
37224090Sdougb * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
38224090Sdougb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
39224090Sdougb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
40224090Sdougb * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
41224090Sdougb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
42224090Sdougb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
43224090Sdougb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
44224090Sdougb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
45224090Sdougb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
46224090Sdougb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47224090Sdougb * SUCH DAMAGE.
48224090Sdougb */
49224090Sdougb
50224090Sdougb/**
51224090Sdougb *    getnameinfo() returns the hostname for the struct sockaddr sa which is
52224090Sdougb *    salen bytes long. The hostname is of length hostlen and is returned via
53224090Sdougb *    *host. The maximum length of the hostname is 1025 bytes: #NI_MAXHOST.
54224090Sdougb *
55224090Sdougb *    The name of the service associated with the port number in sa is
56224090Sdougb *    returned in *serv. It is servlen bytes long. The maximum length of the
57224090Sdougb *    service name is #NI_MAXSERV - 32 bytes.
58224090Sdougb *
59224090Sdougb *    The flags argument sets the following bits:
60224090Sdougb *
61224090Sdougb * \li   #NI_NOFQDN:
62224090Sdougb *           A fully qualified domain name is not required for local hosts.
63224090Sdougb *           The local part of the fully qualified domain name is returned
64224090Sdougb *           instead.
65224090Sdougb *
66224090Sdougb * \li   #NI_NUMERICHOST
67224090Sdougb *           Return the address in numeric form, as if calling inet_ntop(),
68224090Sdougb *           instead of a host name.
69224090Sdougb *
70224090Sdougb * \li   #NI_NAMEREQD
71224090Sdougb *           A name is required. If the hostname cannot be found in the DNS
72224090Sdougb *           and this flag is set, a non-zero error code is returned. If the
73224090Sdougb *           hostname is not found and the flag is not set, the address is
74224090Sdougb *           returned in numeric form.
75224090Sdougb *
76224090Sdougb * \li   #NI_NUMERICSERV
77224090Sdougb *           The service name is returned as a digit string representing the
78224090Sdougb *           port number.
79224090Sdougb *
80224090Sdougb * \li   #NI_DGRAM
81224090Sdougb *           Specifies that the service being looked up is a datagram
82224090Sdougb *           service, and causes getservbyport() to be called with a second
83224090Sdougb *           argument of "udp" instead of its default of "tcp". This is
84224090Sdougb *           required for the few ports (512-514) that have different
85224090Sdougb *           services for UDP and TCP.
86224090Sdougb *
87224090Sdougb * \section getnameinfo_return Return Values
88224090Sdougb *
89224090Sdougb *    getnameinfo() returns 0 on success or a non-zero error code if
90224090Sdougb *    an error occurs.
91224090Sdougb *
92224090Sdougb * \section getname_see See Also
93224090Sdougb *
94224090Sdougb *    RFC3493, getservbyport(),
95224090Sdougb *    getnamebyaddr(). inet_ntop().
96224090Sdougb */
97224090Sdougb
98224090Sdougb#include <config.h>
99224090Sdougb
100224090Sdougb#include <stdio.h>
101224090Sdougb#include <string.h>
102224090Sdougb
103224090Sdougb#include <isc/netaddr.h>
104224090Sdougb#include <isc/print.h>
105224090Sdougb#include <isc/sockaddr.h>
106224090Sdougb#include <isc/util.h>
107224090Sdougb
108224090Sdougb#include <dns/byaddr.h>
109224090Sdougb#include <dns/client.h>
110224090Sdougb#include <dns/fixedname.h>
111224090Sdougb#include <dns/name.h>
112224090Sdougb#include <dns/rdata.h>
113224090Sdougb#include <dns/rdataset.h>
114224090Sdougb#include <dns/rdatastruct.h>
115224090Sdougb#include <dns/result.h>
116224090Sdougb
117224090Sdougb#include <irs/context.h>
118224090Sdougb#include <irs/netdb.h>
119224090Sdougb
120224090Sdougb#define SUCCESS 0
121224090Sdougb
122224090Sdougb/*% afd structure definition */
123224090Sdougbstatic struct afd {
124224090Sdougb	int a_af;
125224090Sdougb	size_t a_addrlen;
126224090Sdougb	size_t a_socklen;
127224090Sdougb} afdl [] = {
128224090Sdougb	/*!
129224090Sdougb	 * First entry is linked last...
130224090Sdougb	 */
131224090Sdougb	{ AF_INET, sizeof(struct in_addr), sizeof(struct sockaddr_in) },
132224090Sdougb	{ AF_INET6, sizeof(struct in6_addr), sizeof(struct sockaddr_in6) },
133224090Sdougb	{0, 0, 0},
134224090Sdougb};
135224090Sdougb
136224090Sdougb/*!
137224090Sdougb * The test against 0 is there to keep the Solaris compiler
138224090Sdougb * from complaining about "end-of-loop code not reached".
139224090Sdougb */
140224090Sdougb#define ERR(code) \
141224090Sdougb	do { result = (code);			\
142224090Sdougb		if (result != 0) goto cleanup;	\
143224090Sdougb	} while (0)
144224090Sdougb
145224090Sdougbint
146224090Sdougbgetnameinfo(const struct sockaddr *sa, socklen_t salen, char *host,
147224090Sdougb	    IRS_GETNAMEINFO_BUFLEN_T hostlen, char *serv,
148224090Sdougb	    IRS_GETNAMEINFO_BUFLEN_T servlen, IRS_GETNAMEINFO_FLAGS_T flags)
149224090Sdougb{
150254402Serwin	struct afd *afd = NULL;
151224090Sdougb	struct servent *sp;
152225361Sdougb	unsigned short port = 0;
153224090Sdougb#ifdef IRS_PLATFORM_HAVESALEN
154224090Sdougb	size_t len;
155224090Sdougb#endif
156224090Sdougb	int family, i;
157225361Sdougb	const void *addr = NULL;
158224090Sdougb	char *p;
159224090Sdougb#if 0
160224090Sdougb	unsigned long v4a;
161224090Sdougb	unsigned char pfx;
162224090Sdougb#endif
163224090Sdougb	char numserv[sizeof("65000")];
164224090Sdougb	char numaddr[sizeof("abcd:abcd:abcd:abcd:abcd:abcd:255.255.255.255")
165224090Sdougb		    + 1 + sizeof("4294967295")];
166224090Sdougb	const char *proto;
167224090Sdougb	int result = SUCCESS;
168224090Sdougb
169224090Sdougb	if (sa == NULL)
170224090Sdougb		ERR(EAI_FAIL);
171224090Sdougb
172224090Sdougb#ifdef IRS_PLATFORM_HAVESALEN
173224090Sdougb	len = sa->sa_len;
174224090Sdougb	if (len != salen)
175224090Sdougb		ERR(EAI_FAIL);
176224090Sdougb#endif
177224090Sdougb
178224090Sdougb	family = sa->sa_family;
179224090Sdougb	for (i = 0; afdl[i].a_af; i++)
180224090Sdougb		if (afdl[i].a_af == family) {
181224090Sdougb			afd = &afdl[i];
182224090Sdougb			goto found;
183224090Sdougb		}
184224090Sdougb	ERR(EAI_FAMILY);
185224090Sdougb
186224090Sdougb found:
187224090Sdougb	if (salen != afd->a_socklen)
188224090Sdougb		ERR(EAI_FAIL);
189224090Sdougb
190224090Sdougb	switch (family) {
191224090Sdougb	case AF_INET:
192224090Sdougb		port = ((const struct sockaddr_in *)sa)->sin_port;
193224090Sdougb		addr = &((const struct sockaddr_in *)sa)->sin_addr.s_addr;
194224090Sdougb		break;
195224090Sdougb
196224090Sdougb	case AF_INET6:
197224090Sdougb		port = ((const struct sockaddr_in6 *)sa)->sin6_port;
198224090Sdougb		addr = ((const struct sockaddr_in6 *)sa)->sin6_addr.s6_addr;
199224090Sdougb		break;
200224090Sdougb
201224090Sdougb	default:
202224090Sdougb		INSIST(0);
203224090Sdougb	}
204224090Sdougb	proto = (flags & NI_DGRAM) ? "udp" : "tcp";
205224090Sdougb
206224090Sdougb	if (serv == NULL || servlen == 0U) {
207224090Sdougb		/*
208224090Sdougb		 * Caller does not want service.
209224090Sdougb		 */
210224090Sdougb	} else if ((flags & NI_NUMERICSERV) != 0 ||
211224090Sdougb		   (sp = getservbyport(port, proto)) == NULL) {
212224090Sdougb		snprintf(numserv, sizeof(numserv), "%d", ntohs(port));
213224090Sdougb		if ((strlen(numserv) + 1) > servlen)
214224090Sdougb			ERR(EAI_OVERFLOW);
215224090Sdougb		strcpy(serv, numserv);
216224090Sdougb	} else {
217224090Sdougb		if ((strlen(sp->s_name) + 1) > servlen)
218224090Sdougb			ERR(EAI_OVERFLOW);
219224090Sdougb		strcpy(serv, sp->s_name);
220224090Sdougb	}
221224090Sdougb
222224090Sdougb#if 0
223224090Sdougb	switch (sa->sa_family) {
224224090Sdougb	case AF_INET:
225224090Sdougb		v4a = ((struct sockaddr_in *)sa)->sin_addr.s_addr;
226224090Sdougb		if (IN_MULTICAST(v4a) || IN_EXPERIMENTAL(v4a))
227224090Sdougb			flags |= NI_NUMERICHOST;
228224090Sdougb		v4a >>= IN_CLASSA_NSHIFT;
229224090Sdougb		if (v4a == 0 || v4a == IN_LOOPBACKNET)
230224090Sdougb			flags |= NI_NUMERICHOST;
231224090Sdougb		break;
232224090Sdougb
233224090Sdougb	case AF_INET6:
234224090Sdougb		pfx = ((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr[0];
235224090Sdougb		if (pfx == 0 || pfx == 0xfe || pfx == 0xff)
236224090Sdougb			flags |= NI_NUMERICHOST;
237224090Sdougb		break;
238224090Sdougb	}
239224090Sdougb#endif
240224090Sdougb
241224090Sdougb	if (host == NULL || hostlen == 0U) {
242224090Sdougb		/*
243224090Sdougb		 * do nothing in this case.
244224090Sdougb		 * in case you are wondering if "&&" is more correct than
245224090Sdougb		 * "||" here: RFC3493 says that host == NULL or hostlen == 0
246224090Sdougb		 * means that the caller does not want the result.
247224090Sdougb		 */
248224090Sdougb	} else if ((flags & NI_NUMERICHOST) != 0) {
249224090Sdougb		if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr))
250224090Sdougb		    == NULL)
251224090Sdougb			ERR(EAI_SYSTEM);
252224090Sdougb#if defined(IRS_HAVE_SIN6_SCOPE_ID)
253224090Sdougb		if (afd->a_af == AF_INET6 &&
254224090Sdougb		    ((const struct sockaddr_in6 *)sa)->sin6_scope_id) {
255224090Sdougb			char *p = numaddr + strlen(numaddr);
256224090Sdougb			const char *stringscope = NULL;
257224090Sdougb#ifdef VENDOR_SPECIFIC
258224090Sdougb			/*
259224090Sdougb			 * Vendors may want to add support for
260224090Sdougb			 * non-numeric scope identifier.
261224090Sdougb			 */
262224090Sdougb			stringscope = foo;
263224090Sdougb#endif
264224090Sdougb			if (stringscope == NULL) {
265224090Sdougb				snprintf(p, sizeof(numaddr) - (p - numaddr),
266224090Sdougb				    "%%%u",
267224090Sdougb				    ((const struct sockaddr_in6 *)sa)->sin6_scope_id);
268224090Sdougb			} else {
269224090Sdougb				snprintf(p, sizeof(numaddr) - (p - numaddr),
270224090Sdougb				    "%%%s", stringscope);
271224090Sdougb			}
272224090Sdougb		}
273224090Sdougb#endif
274224090Sdougb		if (strlen(numaddr) + 1 > hostlen)
275224090Sdougb			ERR(EAI_OVERFLOW);
276224090Sdougb		strcpy(host, numaddr);
277224090Sdougb	} else {
278224090Sdougb		isc_netaddr_t netaddr;
279224090Sdougb		dns_fixedname_t ptrfname;
280224090Sdougb		dns_name_t *ptrname;
281224090Sdougb		irs_context_t *irsctx = NULL;
282224090Sdougb		dns_client_t *client;
283224090Sdougb		isc_boolean_t found = ISC_FALSE;
284224090Sdougb		dns_namelist_t answerlist;
285224090Sdougb		dns_rdataset_t *rdataset;
286224090Sdougb		isc_region_t hostregion;
287224090Sdougb		char hoststr[1024]; /* is this enough? */
288224090Sdougb		isc_result_t iresult;
289224090Sdougb
290224090Sdougb		/* Get IRS context and the associated DNS client object */
291224090Sdougb		iresult = irs_context_get(&irsctx);
292224090Sdougb		if (iresult != ISC_R_SUCCESS)
293224090Sdougb			ERR(EAI_FAIL);
294224090Sdougb		client = irs_context_getdnsclient(irsctx);
295224090Sdougb
296224090Sdougb		/* Make query name */
297224090Sdougb		isc_netaddr_fromsockaddr(&netaddr, (const isc_sockaddr_t *)sa);
298224090Sdougb		dns_fixedname_init(&ptrfname);
299224090Sdougb		ptrname = dns_fixedname_name(&ptrfname);
300224090Sdougb		iresult = dns_byaddr_createptrname2(&netaddr, 0, ptrname);
301224090Sdougb		if (iresult != ISC_R_SUCCESS)
302224090Sdougb			ERR(EAI_FAIL);
303224090Sdougb
304224090Sdougb		/* Get the PTR RRset */
305224090Sdougb		ISC_LIST_INIT(answerlist);
306224090Sdougb		iresult = dns_client_resolve(client, ptrname,
307224090Sdougb					     dns_rdataclass_in,
308224090Sdougb					     dns_rdatatype_ptr,
309224090Sdougb					     DNS_CLIENTRESOPT_ALLOWRUN,
310224090Sdougb					     &answerlist);
311224090Sdougb		switch (iresult) {
312224090Sdougb		case ISC_R_SUCCESS:
313224090Sdougb		/*
314224090Sdougb		 * a 'non-existent' error is not necessarily fatal for
315224090Sdougb		 * getnameinfo().
316224090Sdougb		 */
317224090Sdougb		case DNS_R_NCACHENXDOMAIN:
318224090Sdougb		case DNS_R_NCACHENXRRSET:
319224090Sdougb			break;
320224090Sdougb		case DNS_R_SIGINVALID:
321224090Sdougb		case DNS_R_SIGEXPIRED:
322224090Sdougb		case DNS_R_SIGFUTURE:
323224090Sdougb		case DNS_R_KEYUNAUTHORIZED:
324224090Sdougb		case DNS_R_MUSTBESECURE:
325224090Sdougb		case DNS_R_COVERINGNSEC:
326224090Sdougb		case DNS_R_NOTAUTHORITATIVE:
327224090Sdougb		case DNS_R_NOVALIDKEY:
328224090Sdougb		case DNS_R_NOVALIDDS:
329224090Sdougb		case DNS_R_NOVALIDSIG:
330224090Sdougb			ERR(EAI_INSECUREDATA);
331254402Serwin			break;
332224090Sdougb		default:
333224090Sdougb			ERR(EAI_FAIL);
334224090Sdougb		}
335224090Sdougb
336224090Sdougb		/* Parse the answer for the hostname */
337224090Sdougb		for (ptrname = ISC_LIST_HEAD(answerlist); ptrname != NULL;
338224090Sdougb		     ptrname = ISC_LIST_NEXT(ptrname, link)) {
339224090Sdougb			for (rdataset = ISC_LIST_HEAD(ptrname->list);
340224090Sdougb			     rdataset != NULL;
341224090Sdougb			     rdataset = ISC_LIST_NEXT(rdataset, link)) {
342224090Sdougb				if (!dns_rdataset_isassociated(rdataset))
343224090Sdougb					continue;
344224090Sdougb				if (rdataset->type != dns_rdatatype_ptr)
345224090Sdougb					continue;
346224090Sdougb
347224090Sdougb				for (iresult = dns_rdataset_first(rdataset);
348224090Sdougb				     iresult == ISC_R_SUCCESS;
349224090Sdougb				     iresult = dns_rdataset_next(rdataset)) {
350224090Sdougb					dns_rdata_t rdata;
351224090Sdougb					dns_rdata_ptr_t rdata_ptr;
352224090Sdougb					isc_buffer_t b;
353224090Sdougb
354224090Sdougb					dns_rdata_init(&rdata);
355224090Sdougb					dns_rdataset_current(rdataset, &rdata);
356224090Sdougb					dns_rdata_tostruct(&rdata, &rdata_ptr,
357224090Sdougb							   NULL);
358224090Sdougb
359224090Sdougb					isc_buffer_init(&b, hoststr,
360224090Sdougb							sizeof(hoststr));
361224090Sdougb					iresult =
362224090Sdougb						dns_name_totext(&rdata_ptr.ptr,
363224090Sdougb								ISC_TRUE, &b);
364224090Sdougb					dns_rdata_freestruct(&rdata_ptr);
365224090Sdougb					if (iresult == ISC_R_SUCCESS) {
366224090Sdougb						/*
367224090Sdougb						 * We ignore the rest of the
368224090Sdougb						 * answer.  After all,
369224090Sdougb						 * getnameinfo() can return
370224090Sdougb						 * at most one hostname.
371224090Sdougb						 */
372224090Sdougb						found = ISC_TRUE;
373224090Sdougb						isc_buffer_usedregion(
374224090Sdougb							&b, &hostregion);
375224090Sdougb						goto ptrfound;
376224090Sdougb					}
377224090Sdougb
378224090Sdougb				}
379224090Sdougb			}
380224090Sdougb		}
381224090Sdougb	ptrfound:
382224090Sdougb		dns_client_freeresanswer(client, &answerlist);
383224090Sdougb		if (found) {
384224090Sdougb			if ((flags & NI_NOFQDN) != 0) {
385224090Sdougb				p = strchr(hoststr, '.');
386224090Sdougb				if (p)
387224090Sdougb					*p = '\0';
388224090Sdougb			}
389224090Sdougb			if (hostregion.length + 1 > hostlen)
390224090Sdougb				ERR(EAI_OVERFLOW);
391224090Sdougb			snprintf(host, hostlen, "%.*s",
392224090Sdougb				 (int)hostregion.length,
393224090Sdougb				 (char *)hostregion.base);
394224090Sdougb		} else {
395224090Sdougb			if ((flags & NI_NAMEREQD) != 0)
396224090Sdougb				ERR(EAI_NONAME);
397224090Sdougb			if (inet_ntop(afd->a_af, addr, numaddr,
398224090Sdougb				      sizeof(numaddr)) == NULL)
399224090Sdougb				ERR(EAI_SYSTEM);
400224090Sdougb			if ((strlen(numaddr) + 1) > hostlen)
401224090Sdougb				ERR(EAI_OVERFLOW);
402224090Sdougb			strcpy(host, numaddr);
403224090Sdougb		}
404224090Sdougb	}
405224090Sdougb	result = SUCCESS;
406224090Sdougb
407224090Sdougb cleanup:
408224090Sdougb	return (result);
409224090Sdougb}
410