xref: /freebsd/lib/libc/net/gethostnamadr.c (revision 87569f75a91f298c52a71823c04d41cf53c88889)
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 extern const char *_res_hostalias(const char *, char *, size_t);
55 
56 static int gethostbyname_internal(const char *, int, struct hostent *,
57     struct hostent_data *);
58 
59 /* Host lookup order if nsswitch.conf is broken or nonexistant */
60 static const ns_src default_src[] = {
61 	{ NSSRC_FILES, NS_SUCCESS },
62 	{ NSSRC_DNS, NS_SUCCESS },
63 	{ 0 }
64 };
65 
66 static struct hostdata hostdata;
67 static thread_key_t hostdata_key;
68 static once_t hostdata_init_once = ONCE_INITIALIZER;
69 static int hostdata_thr_keycreated = 0;
70 
71 static void
72 hostdata_free(void *ptr)
73 {
74 	struct hostdata *hd = ptr;
75 
76 	if (hd == NULL)
77 		return;
78 	hd->data.stayopen = 0;
79 	_endhosthtent(&hd->data);
80 	free(hd);
81 }
82 
83 static void
84 hostdata_keycreate(void)
85 {
86 	hostdata_thr_keycreated =
87 	    (thr_keycreate(&hostdata_key, hostdata_free) == 0);
88 }
89 
90 struct hostdata *
91 __hostdata_init(void)
92 {
93 	struct hostdata *hd;
94 
95 	if (thr_main() != 0)
96 		return &hostdata;
97 	if (thr_once(&hostdata_init_once, hostdata_keycreate) != 0 ||
98 	    !hostdata_thr_keycreated)
99 		return NULL;
100 	if ((hd = thr_getspecific(hostdata_key)) != NULL)
101 		return hd;
102 	if ((hd = calloc(1, sizeof(*hd))) == NULL)
103 		return NULL;
104 	if (thr_setspecific(hostdata_key, hd) == 0)
105 		return hd;
106 	free(hd);
107 	return NULL;
108 }
109 
110 int
111 gethostbyname_r(const char *name, struct hostent *he, struct hostent_data *hed)
112 {
113 	int error;
114 
115 	if ((_res.options & RES_INIT) == 0 && res_init() == -1) {
116 		h_errno = NETDB_INTERNAL;
117 		return -1;
118 	}
119 	if (_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 	if ((_res.options & RES_INIT) == 0 && res_init() == -1) {
132 		h_errno = NETDB_INTERNAL;
133 		return -1;
134 	}
135 	return gethostbyname_internal(name, af, he, hed);
136 }
137 
138 static int
139 gethostbyname_internal(const char *name, int af, struct hostent *he,
140     struct hostent_data *hed)
141 {
142 	const char *cp;
143 	char *bp, *ep;
144 	int size, rval;
145 	char abuf[MAXDNAME];
146 
147 	static const ns_dtab dtab[] = {
148 		NS_FILES_CB(_ht_gethostbyname, NULL)
149 		{ NSSRC_DNS, _dns_gethostbyname, NULL },
150 		NS_NIS_CB(_nis_gethostbyname, NULL) /* force -DHESIOD */
151 		{ 0 }
152 	};
153 
154 	switch (af) {
155 	case AF_INET:
156 		size = INADDRSZ;
157 		break;
158 	case AF_INET6:
159 		size = IN6ADDRSZ;
160 		break;
161 	default:
162 		h_errno = NETDB_INTERNAL;
163 		errno = EAFNOSUPPORT;
164 		return -1;
165 	}
166 
167 	he->h_addrtype = af;
168 	he->h_length = size;
169 
170 	/*
171 	 * if there aren't any dots, it could be a user-level alias.
172 	 * this is also done in res_query() since we are not the only
173 	 * function that looks up host names.
174 	 */
175 	if (!strchr(name, '.') &&
176 	    (cp = _res_hostalias(name, abuf, sizeof abuf)))
177 		name = cp;
178 
179 	/*
180 	 * disallow names consisting only of digits/dots, unless
181 	 * they end in a dot.
182 	 */
183 	if (isdigit((u_char)name[0]))
184 		for (cp = name;; ++cp) {
185 			if (!*cp) {
186 				if (*--cp == '.')
187 					break;
188 				/*
189 				 * All-numeric, no dot at the end.
190 				 * Fake up a hostent as if we'd actually
191 				 * done a lookup.
192 				 */
193 				if (inet_pton(af, name, hed->host_addr) <= 0) {
194 					h_errno = HOST_NOT_FOUND;
195 					return -1;
196 				}
197 				strncpy(hed->hostbuf, name, MAXDNAME);
198 				hed->hostbuf[MAXDNAME] = '\0';
199 				bp = hed->hostbuf + MAXDNAME + 1;
200 				ep = hed->hostbuf + sizeof hed->hostbuf;
201 				he->h_name = hed->hostbuf;
202 				he->h_aliases = hed->host_aliases;
203 				hed->host_aliases[0] = NULL;
204 				hed->h_addr_ptrs[0] = (char *)hed->host_addr;
205 				hed->h_addr_ptrs[1] = NULL;
206 				he->h_addr_list = hed->h_addr_ptrs;
207 				if (_res.options & RES_USE_INET6)
208 					_map_v4v6_hostent(he, &bp, ep);
209 				h_errno = NETDB_SUCCESS;
210 				return 0;
211 			}
212 			if (!isdigit((u_char)*cp) && *cp != '.')
213 				break;
214 		}
215 	if ((isxdigit((u_char)name[0]) && strchr(name, ':') != NULL) ||
216 	    name[0] == ':')
217 		for (cp = name;; ++cp) {
218 			if (!*cp) {
219 				if (*--cp == '.')
220 					break;
221 				/*
222 				 * All-IPv6-legal, no dot at the end.
223 				 * Fake up a hostent as if we'd actually
224 				 * done a lookup.
225 				 */
226 				if (inet_pton(af, name, hed->host_addr) <= 0) {
227 					h_errno = HOST_NOT_FOUND;
228 					return -1;
229 				}
230 				strncpy(hed->hostbuf, name, MAXDNAME);
231 				hed->hostbuf[MAXDNAME] = '\0';
232 				he->h_name = hed->hostbuf;
233 				he->h_aliases = hed->host_aliases;
234 				hed->host_aliases[0] = NULL;
235 				hed->h_addr_ptrs[0] = (char *)hed->host_addr;
236 				hed->h_addr_ptrs[1] = NULL;
237 				he->h_addr_list = hed->h_addr_ptrs;
238 				h_errno = NETDB_SUCCESS;
239 				return 0;
240 			}
241 			if (!isxdigit((u_char)*cp) && *cp != ':' && *cp != '.')
242 				break;
243 		}
244 
245 	rval = _nsdispatch(NULL, dtab, NSDB_HOSTS, "gethostbyname",
246 	    default_src, name, af, he, hed);
247 
248 	return (rval == NS_SUCCESS) ? 0 : -1;
249 }
250 
251 int
252 gethostbyaddr_r(const char *addr, int len, int af, struct hostent *he,
253     struct hostent_data *hed)
254 {
255 	const u_char *uaddr = (const u_char *)addr;
256 	const struct in6_addr *addr6;
257 	socklen_t size;
258 	int rval;
259 
260 	static const ns_dtab dtab[] = {
261 		NS_FILES_CB(_ht_gethostbyaddr, NULL)
262 		{ NSSRC_DNS, _dns_gethostbyaddr, NULL },
263 		NS_NIS_CB(_nis_gethostbyaddr, NULL) /* force -DHESIOD */
264 		{ 0 }
265 	};
266 
267 	if ((_res.options & RES_INIT) == 0 && res_init() == -1) {
268 		h_errno = NETDB_INTERNAL;
269 		return -1;
270 	}
271 
272 	if (af == AF_INET6 && len == IN6ADDRSZ) {
273 		addr6 = (const struct in6_addr *)(const void *)uaddr;
274 		if (IN6_IS_ADDR_LINKLOCAL(addr6)) {
275 			h_errno = HOST_NOT_FOUND;
276 			return -1;
277 		}
278 		if (IN6_IS_ADDR_V4MAPPED(addr6) ||
279 		    IN6_IS_ADDR_V4COMPAT(addr6)) {
280 			/* Unmap. */
281 			uaddr += IN6ADDRSZ - INADDRSZ;
282 			af = AF_INET;
283 			len = INADDRSZ;
284 		}
285 	}
286 	switch (af) {
287 	case AF_INET:
288 		size = INADDRSZ;
289 		break;
290 	case AF_INET6:
291 		size = IN6ADDRSZ;
292 		break;
293 	default:
294 		errno = EAFNOSUPPORT;
295 		h_errno = NETDB_INTERNAL;
296 		return -1;
297 	}
298 	if (size != len) {
299 		errno = EINVAL;
300 		h_errno = NETDB_INTERNAL;
301 		return -1;
302 	}
303 
304 	rval = _nsdispatch(NULL, dtab, NSDB_HOSTS, "gethostbyaddr",
305 	    default_src, uaddr, len, af, he, hed);
306 
307 	return (rval == NS_SUCCESS) ? 0 : -1;
308 }
309 
310 void
311 sethostent_r(int stayopen, struct hostent_data *hed)
312 {
313 	_sethosthtent(stayopen, hed);
314 	_sethostdnsent(stayopen);
315 }
316 
317 void
318 endhostent_r(struct hostent_data *hed)
319 {
320 	_endhosthtent(hed);
321 	_endhostdnsent();
322 }
323 
324 struct hostent *
325 gethostbyname(const char *name)
326 {
327 	struct hostdata *hd;
328 
329 	if ((hd = __hostdata_init()) == NULL)
330 		return NULL;
331 	if (gethostbyname_r(name, &hd->host, &hd->data) != 0)
332 		return NULL;
333 	return &hd->host;
334 }
335 
336 struct hostent *
337 gethostbyname2(const char *name, int af)
338 {
339 	struct hostdata *hd;
340 
341 	if ((hd = __hostdata_init()) == NULL)
342 		return NULL;
343 	if (gethostbyname2_r(name, af, &hd->host, &hd->data) != 0)
344 		return NULL;
345 	return &hd->host;
346 }
347 
348 struct hostent *
349 gethostbyaddr(const char *addr, int len, int af)
350 {
351 	struct hostdata *hd;
352 
353 	if ((hd = __hostdata_init()) == NULL)
354 		return NULL;
355 	if (gethostbyaddr_r(addr, len, af, &hd->host, &hd->data) != 0)
356 		return NULL;
357 	return &hd->host;
358 }
359 
360 void
361 sethostent(int stayopen)
362 {
363 	struct hostdata *hd;
364 
365 	if ((hd = __hostdata_init()) == NULL)
366 		return;
367 	sethostent_r(stayopen, &hd->data);
368 }
369 
370 void
371 endhostent(void)
372 {
373 	struct hostdata *hd;
374 
375 	if ((hd = __hostdata_init()) == NULL)
376 		return;
377 	endhostent_r(&hd->data);
378 }
379