gethostnamadr.c revision 156960
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: head/lib/libc/net/gethostnamadr.c 156960 2006-03-21 16:11:11Z ume $");
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
48extern int _ht_gethostbyname(void *, void *, va_list);
49extern int _dns_gethostbyname(void *, void *, va_list);
50extern int _nis_gethostbyname(void *, void *, va_list);
51extern int _ht_gethostbyaddr(void *, void *, va_list);
52extern int _dns_gethostbyaddr(void *, void *, va_list);
53extern int _nis_gethostbyaddr(void *, void *, va_list);
54
55static int gethostbyname_internal(const char *, int, struct hostent *,
56    struct hostent_data *);
57
58/* Host lookup order if nsswitch.conf is broken or nonexistant */
59static const ns_src default_src[] = {
60	{ NSSRC_FILES, NS_SUCCESS },
61	{ NSSRC_DNS, NS_SUCCESS },
62	{ 0 }
63};
64
65static struct hostdata hostdata;
66static thread_key_t hostdata_key;
67static once_t hostdata_init_once = ONCE_INITIALIZER;
68static int hostdata_thr_keycreated = 0;
69
70static void
71hostdata_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
82static void
83hostdata_keycreate(void)
84{
85	hostdata_thr_keycreated =
86	    (thr_keycreate(&hostdata_key, hostdata_free) == 0);
87}
88
89struct 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
109int
110gethostbyname_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
127int
128gethostbyname2_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
139static int
140gethostbyname_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
254int
255gethostbyaddr_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
314void
315sethostent_r(int stayopen, struct hostent_data *hed)
316{
317	_sethosthtent(stayopen, hed);
318	_sethostdnsent(stayopen);
319}
320
321void
322endhostent_r(struct hostent_data *hed)
323{
324	_endhosthtent(hed);
325	_endhostdnsent();
326}
327
328struct hostent *
329gethostbyname(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
340struct hostent *
341gethostbyname2(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
352struct hostent *
353gethostbyaddr(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
364void
365sethostent(int stayopen)
366{
367	struct hostdata *hd;
368
369	if ((hd = __hostdata_init()) == NULL)
370		return;
371	sethostent_r(stayopen, &hd->data);
372}
373
374void
375endhostent(void)
376{
377	struct hostdata *hd;
378
379	if ((hd = __hostdata_init()) == NULL)
380		return;
381	endhostent_r(&hd->data);
382}
383