xref: /freebsd/lib/libc/net/gethostnamadr.c (revision 3d11b6c8f01e1fca5936a11d6996448467851a94)
1 /*-
2  * Copyright (c) 1994, Garrett Wollman
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  */
25 
26 #include <sys/cdefs.h>
27 __FBSDID("$FreeBSD$");
28 
29 #include "namespace.h"
30 #include "reentrant.h"
31 #include <sys/param.h>
32 #include <sys/socket.h>
33 #include <netinet/in.h>
34 #include <arpa/inet.h>
35 #include <netdb.h>
36 #include <stdio.h>
37 #include <ctype.h>
38 #include <errno.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <stdarg.h>
42 #include <nsswitch.h>
43 #include <arpa/nameser.h>		/* XXX hack for _res */
44 #include <resolv.h>			/* XXX hack for _res */
45 #include "un-namespace.h"
46 #include "netdb_private.h"
47 
48 extern int _ht_gethostbyname(void *, void *, va_list);
49 extern int _dns_gethostbyname(void *, void *, va_list);
50 extern int _nis_gethostbyname(void *, void *, va_list);
51 extern int _ht_gethostbyaddr(void *, void *, va_list);
52 extern int _dns_gethostbyaddr(void *, void *, va_list);
53 extern int _nis_gethostbyaddr(void *, void *, va_list);
54 
55 static int gethostbyname_internal(const char *, int, struct hostent *,
56     struct hostent_data *);
57 
58 /* Host lookup order if nsswitch.conf is broken or nonexistant */
59 static const ns_src default_src[] = {
60 	{ NSSRC_FILES, NS_SUCCESS },
61 	{ NSSRC_DNS, NS_SUCCESS },
62 	{ 0 }
63 };
64 
65 static struct hostdata hostdata;
66 static thread_key_t hostdata_key;
67 static once_t hostdata_init_once = ONCE_INITIALIZER;
68 static int hostdata_thr_keycreated = 0;
69 
70 static void
71 hostdata_free(void *ptr)
72 {
73 	struct hostdata *hd = ptr;
74 
75 	if (hd == NULL)
76 		return;
77 	hd->data.stayopen = 0;
78 	_endhosthtent(&hd->data);
79 	free(hd);
80 }
81 
82 static void
83 hostdata_keycreate(void)
84 {
85 	hostdata_thr_keycreated =
86 	    (thr_keycreate(&hostdata_key, hostdata_free) == 0);
87 }
88 
89 struct hostdata *
90 __hostdata_init(void)
91 {
92 	struct hostdata *hd;
93 
94 	if (thr_main() != 0)
95 		return &hostdata;
96 	if (thr_once(&hostdata_init_once, hostdata_keycreate) != 0 ||
97 	    !hostdata_thr_keycreated)
98 		return NULL;
99 	if ((hd = thr_getspecific(hostdata_key)) != NULL)
100 		return hd;
101 	if ((hd = calloc(1, sizeof(*hd))) == NULL)
102 		return NULL;
103 	if (thr_setspecific(hostdata_key, hd) == 0)
104 		return hd;
105 	free(hd);
106 	return NULL;
107 }
108 
109 int
110 gethostbyname_r(const char *name, struct hostent *he, struct hostent_data *hed)
111 {
112 	int error;
113 
114 	hed->res = __res_state();
115 	if ((hed->res->options & RES_INIT) == 0 && res_ninit(hed->res) == -1) {
116 		RES_SET_H_ERRNO(hed->res, NETDB_INTERNAL);
117 		return -1;
118 	}
119 	if (hed->res->options & RES_USE_INET6) {
120 		error = gethostbyname_internal(name, AF_INET6, he, hed);
121 		if (error == 0)
122 			return 0;
123 	}
124 	return gethostbyname_internal(name, AF_INET, he, hed);
125 }
126 
127 int
128 gethostbyname2_r(const char *name, int af, struct hostent *he,
129     struct hostent_data *hed)
130 {
131 	hed->res = __res_state();
132 	if ((hed->res->options & RES_INIT) == 0 && res_ninit(hed->res) == -1) {
133 		RES_SET_H_ERRNO(hed->res, NETDB_INTERNAL);
134 		return -1;
135 	}
136 	return gethostbyname_internal(name, af, he, hed);
137 }
138 
139 static int
140 gethostbyname_internal(const char *name, int af, struct hostent *he,
141     struct hostent_data *hed)
142 {
143 	const char *cp;
144 	char *bp, *ep;
145 	int size, rval;
146 	char abuf[MAXDNAME];
147 
148 	static const ns_dtab dtab[] = {
149 		NS_FILES_CB(_ht_gethostbyname, NULL)
150 		{ NSSRC_DNS, _dns_gethostbyname, NULL },
151 		NS_NIS_CB(_nis_gethostbyname, NULL) /* force -DHESIOD */
152 		{ 0 }
153 	};
154 
155 	switch (af) {
156 	case AF_INET:
157 		size = INADDRSZ;
158 		break;
159 	case AF_INET6:
160 		size = IN6ADDRSZ;
161 		break;
162 	default:
163 		RES_SET_H_ERRNO(hed->res, NETDB_INTERNAL);
164 		errno = EAFNOSUPPORT;
165 		return -1;
166 	}
167 
168 	he->h_addrtype = af;
169 	he->h_length = size;
170 
171 	/*
172 	 * if there aren't any dots, it could be a user-level alias.
173 	 * this is also done in res_query() since we are not the only
174 	 * function that looks up host names.
175 	 */
176 	if (!strchr(name, '.') &&
177 	    (cp = res_hostalias(hed->res, name, abuf, sizeof abuf)))
178 		name = cp;
179 
180 	/*
181 	 * disallow names consisting only of digits/dots, unless
182 	 * they end in a dot.
183 	 */
184 	if (isdigit((u_char)name[0]))
185 		for (cp = name;; ++cp) {
186 			if (!*cp) {
187 				if (*--cp == '.')
188 					break;
189 				/*
190 				 * All-numeric, no dot at the end.
191 				 * Fake up a hostent as if we'd actually
192 				 * done a lookup.
193 				 */
194 				if (inet_pton(af, name, hed->host_addr) <= 0) {
195 					RES_SET_H_ERRNO(hed->res,
196 					    HOST_NOT_FOUND);
197 					return -1;
198 				}
199 				strncpy(hed->hostbuf, name, MAXDNAME);
200 				hed->hostbuf[MAXDNAME] = '\0';
201 				bp = hed->hostbuf + MAXDNAME + 1;
202 				ep = hed->hostbuf + sizeof hed->hostbuf;
203 				he->h_name = hed->hostbuf;
204 				he->h_aliases = hed->host_aliases;
205 				hed->host_aliases[0] = NULL;
206 				hed->h_addr_ptrs[0] = (char *)hed->host_addr;
207 				hed->h_addr_ptrs[1] = NULL;
208 				he->h_addr_list = hed->h_addr_ptrs;
209 				if (hed->res->options & RES_USE_INET6)
210 					_map_v4v6_hostent(he, &bp, ep);
211 				RES_SET_H_ERRNO(hed->res, NETDB_SUCCESS);
212 				return 0;
213 			}
214 			if (!isdigit((u_char)*cp) && *cp != '.')
215 				break;
216 		}
217 	if ((isxdigit((u_char)name[0]) && strchr(name, ':') != NULL) ||
218 	    name[0] == ':')
219 		for (cp = name;; ++cp) {
220 			if (!*cp) {
221 				if (*--cp == '.')
222 					break;
223 				/*
224 				 * All-IPv6-legal, no dot at the end.
225 				 * Fake up a hostent as if we'd actually
226 				 * done a lookup.
227 				 */
228 				if (inet_pton(af, name, hed->host_addr) <= 0) {
229 					RES_SET_H_ERRNO(hed->res,
230 					    HOST_NOT_FOUND);
231 					return -1;
232 				}
233 				strncpy(hed->hostbuf, name, MAXDNAME);
234 				hed->hostbuf[MAXDNAME] = '\0';
235 				he->h_name = hed->hostbuf;
236 				he->h_aliases = hed->host_aliases;
237 				hed->host_aliases[0] = NULL;
238 				hed->h_addr_ptrs[0] = (char *)hed->host_addr;
239 				hed->h_addr_ptrs[1] = NULL;
240 				he->h_addr_list = hed->h_addr_ptrs;
241 				RES_SET_H_ERRNO(hed->res, NETDB_SUCCESS);
242 				return 0;
243 			}
244 			if (!isxdigit((u_char)*cp) && *cp != ':' && *cp != '.')
245 				break;
246 		}
247 
248 	rval = _nsdispatch(NULL, dtab, NSDB_HOSTS, "gethostbyname",
249 	    default_src, name, af, he, hed);
250 
251 	return (rval == NS_SUCCESS) ? 0 : -1;
252 }
253 
254 int
255 gethostbyaddr_r(const char *addr, int len, int af, struct hostent *he,
256     struct hostent_data *hed)
257 {
258 	const u_char *uaddr = (const u_char *)addr;
259 	const struct in6_addr *addr6;
260 	socklen_t size;
261 	int rval;
262 
263 	static const ns_dtab dtab[] = {
264 		NS_FILES_CB(_ht_gethostbyaddr, NULL)
265 		{ NSSRC_DNS, _dns_gethostbyaddr, NULL },
266 		NS_NIS_CB(_nis_gethostbyaddr, NULL) /* force -DHESIOD */
267 		{ 0 }
268 	};
269 
270 	hed->res = __res_state();
271 	if ((hed->res->options & RES_INIT) == 0 && res_ninit(hed->res) == -1) {
272 		RES_SET_H_ERRNO(hed->res, NETDB_INTERNAL);
273 		return -1;
274 	}
275 
276 	if (af == AF_INET6 && len == IN6ADDRSZ) {
277 		addr6 = (const struct in6_addr *)(const void *)uaddr;
278 		if (IN6_IS_ADDR_LINKLOCAL(addr6)) {
279 			RES_SET_H_ERRNO(hed->res, HOST_NOT_FOUND);
280 			return -1;
281 		}
282 		if (IN6_IS_ADDR_V4MAPPED(addr6) ||
283 		    IN6_IS_ADDR_V4COMPAT(addr6)) {
284 			/* Unmap. */
285 			uaddr += IN6ADDRSZ - INADDRSZ;
286 			af = AF_INET;
287 			len = INADDRSZ;
288 		}
289 	}
290 	switch (af) {
291 	case AF_INET:
292 		size = INADDRSZ;
293 		break;
294 	case AF_INET6:
295 		size = IN6ADDRSZ;
296 		break;
297 	default:
298 		errno = EAFNOSUPPORT;
299 		RES_SET_H_ERRNO(hed->res, NETDB_INTERNAL);
300 		return -1;
301 	}
302 	if (size != len) {
303 		errno = EINVAL;
304 		RES_SET_H_ERRNO(hed->res, NETDB_INTERNAL);
305 		return -1;
306 	}
307 
308 	rval = _nsdispatch(NULL, dtab, NSDB_HOSTS, "gethostbyaddr",
309 	    default_src, uaddr, len, af, he, hed);
310 
311 	return (rval == NS_SUCCESS) ? 0 : -1;
312 }
313 
314 void
315 sethostent_r(int stayopen, struct hostent_data *hed)
316 {
317 	_sethosthtent(stayopen, hed);
318 	_sethostdnsent(stayopen);
319 }
320 
321 void
322 endhostent_r(struct hostent_data *hed)
323 {
324 	_endhosthtent(hed);
325 	_endhostdnsent();
326 }
327 
328 struct hostent *
329 gethostbyname(const char *name)
330 {
331 	struct hostdata *hd;
332 
333 	if ((hd = __hostdata_init()) == NULL)
334 		return NULL;
335 	if (gethostbyname_r(name, &hd->host, &hd->data) != 0)
336 		return NULL;
337 	return &hd->host;
338 }
339 
340 struct hostent *
341 gethostbyname2(const char *name, int af)
342 {
343 	struct hostdata *hd;
344 
345 	if ((hd = __hostdata_init()) == NULL)
346 		return NULL;
347 	if (gethostbyname2_r(name, af, &hd->host, &hd->data) != 0)
348 		return NULL;
349 	return &hd->host;
350 }
351 
352 struct hostent *
353 gethostbyaddr(const char *addr, int len, int af)
354 {
355 	struct hostdata *hd;
356 
357 	if ((hd = __hostdata_init()) == NULL)
358 		return NULL;
359 	if (gethostbyaddr_r(addr, len, af, &hd->host, &hd->data) != 0)
360 		return NULL;
361 	return &hd->host;
362 }
363 
364 void
365 sethostent(int stayopen)
366 {
367 	struct hostdata *hd;
368 
369 	if ((hd = __hostdata_init()) == NULL)
370 		return;
371 	sethostent_r(stayopen, &hd->data);
372 }
373 
374 void
375 endhostent(void)
376 {
377 	struct hostdata *hd;
378 
379 	if ((hd = __hostdata_init()) == NULL)
380 		return;
381 	endhostent_r(&hd->data);
382 }
383