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