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