gethostnamadr.c revision 145728
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 145728 2005-04-30 20:07:01Z 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);
54extern const char *_res_hostalias(const char *, char *, size_t);
55
56static int gethostbyname_internal(const char *, int, struct hostent *,
57    struct hostent_data *);
58
59/* Host lookup order if nsswitch.conf is broken or nonexistant */
60static const ns_src default_src[] = {
61	{ NSSRC_FILES, NS_SUCCESS },
62	{ NSSRC_DNS, NS_SUCCESS },
63	{ 0 }
64};
65
66static struct hostdata hostdata;
67static thread_key_t hostdata_key;
68static once_t hostdata_init_once = ONCE_INITIALIZER;
69static int hostdata_thr_keycreated = 0;
70
71static void
72hostdata_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
83static void
84hostdata_keycreate(void)
85{
86	hostdata_thr_keycreated =
87	    (thr_keycreate(&hostdata_key, hostdata_free) == 0);
88}
89
90struct 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
110int
111gethostbyname_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
127int
128gethostbyname2_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
138static int
139gethostbyname_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
251int
252gethostbyaddr_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
310void
311sethostent_r(int stayopen, struct hostent_data *hed)
312{
313	_sethosthtent(stayopen, hed);
314	_sethostdnsent(stayopen);
315}
316
317void
318endhostent_r(struct hostent_data *hed)
319{
320	_endhosthtent(hed);
321	_endhostdnsent();
322}
323
324struct hostent *
325gethostbyname(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
336struct hostent *
337gethostbyname2(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
348struct hostent *
349gethostbyaddr(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
360void
361sethostent(int stayopen)
362{
363	struct hostdata *hd;
364
365	if ((hd = __hostdata_init()) == NULL)
366		return;
367	sethostent_r(stayopen, &hd->data);
368}
369
370void
371endhostent(void)
372{
373	struct hostdata *hd;
374
375	if ((hd = __hostdata_init()) == NULL)
376		return;
377	endhostent_r(&hd->data);
378}
379