gethostbydns.c revision 19301
1/*-
2 * Copyright (c) 1985, 1988, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by the University of
16 *	California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 * -
33 * Portions Copyright (c) 1993 by Digital Equipment Corporation.
34 *
35 * Permission to use, copy, modify, and distribute this software for any
36 * purpose with or without fee is hereby granted, provided that the above
37 * copyright notice and this permission notice appear in all copies, and that
38 * the name of Digital Equipment Corporation not be used in advertising or
39 * publicity pertaining to distribution of the document or software without
40 * specific, written prior permission.
41 *
42 * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
43 * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
44 * OF MERCHANTABILITY AND FITNESS.   IN NO EVENT SHALL DIGITAL EQUIPMENT
45 * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
46 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
47 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
48 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
49 * SOFTWARE.
50 * -
51 * --Copyright--
52 */
53
54#if defined(LIBC_SCCS) && !defined(lint)
55static char sccsid[] = "@(#)gethostnamadr.c	8.1 (Berkeley) 6/4/93";
56static char rcsid[] = "$Id: gethostbydns.c,v 1.11 1996/10/01 03:45:06 pst Exp $";
57#endif /* LIBC_SCCS and not lint */
58
59#include <sys/types.h>
60#include <sys/param.h>
61#include <sys/socket.h>
62#include <netinet/in.h>
63#include <arpa/inet.h>
64#include <arpa/nameser.h>
65
66#include <stdio.h>
67#include <unistd.h>
68#include <string.h>
69#include <netdb.h>
70#include <resolv.h>
71#include <ctype.h>
72#include <errno.h>
73#include <syslog.h>
74
75#include "res_config.h"
76
77extern void _res_close __P((void));
78
79#define SPRINTF(x) ((size_t)sprintf x)
80
81#define	MAXALIASES	35
82#define	MAXADDRS	35
83
84static const char AskedForGot[] =
85		"gethostby*.gethostanswer: asked for \"%s\", got \"%s\"";
86
87static char *h_addr_ptrs[MAXADDRS + 1];
88static struct hostent *gethostbyname_ipv4 __P((const char *));
89
90static struct hostent host;
91static char *host_aliases[MAXALIASES];
92static char hostbuf[8*1024];
93static u_char host_addr[16];	/* IPv4 or IPv6 */
94
95#ifdef RESOLVSORT
96static void addrsort __P((char **, int));
97#endif
98
99#if PACKETSZ > 1024
100#define	MAXPACKET	PACKETSZ
101#else
102#define	MAXPACKET	1024
103#endif
104
105typedef union {
106    HEADER hdr;
107    u_char buf[MAXPACKET];
108} querybuf;
109
110typedef union {
111    int32_t al;
112    char ac;
113} align;
114
115extern int h_errno;
116int _dns_ttl_;
117
118#ifdef DEBUG
119static void
120dprintf(msg, num)
121	char *msg;
122	int num;
123{
124	if (_res.options & RES_DEBUG) {
125		int save = errno;
126
127		printf(msg, num);
128		errno = save;
129	}
130}
131#else
132# define dprintf(msg, num) /*nada*/
133#endif
134
135static struct hostent *
136gethostanswer(answer, anslen, qname, qtype)
137	const querybuf *answer;
138	int anslen;
139	const char *qname;
140	int qtype;
141{
142	register const HEADER *hp;
143	register const u_char *cp;
144	register int n;
145	const u_char *eom;
146	char *bp, **ap, **hap;
147	int type, class, buflen, ancount, qdcount;
148	int haveanswer, had_error;
149	int toobig = 0;
150	char tbuf[MAXDNAME+1];
151	const char *tname;
152	int (*name_ok) __P((const char *));
153
154	tname = qname;
155	host.h_name = NULL;
156	eom = answer->buf + anslen;
157	switch (qtype) {
158	case T_A:
159	case T_AAAA:
160		name_ok = res_hnok;
161		break;
162	case T_PTR:
163		name_ok = res_dnok;
164		break;
165	default:
166		h_errno = NO_RECOVERY;
167		return (NULL);	/* XXX should be abort(); */
168	}
169	/*
170	 * find first satisfactory answer
171	 */
172	hp = &answer->hdr;
173	ancount = ntohs(hp->ancount);
174	qdcount = ntohs(hp->qdcount);
175	bp = hostbuf;
176	buflen = sizeof hostbuf;
177	cp = answer->buf + HFIXEDSZ;
178	if (qdcount != 1) {
179		h_errno = NO_RECOVERY;
180		return (NULL);
181	}
182	n = dn_expand(answer->buf, eom, cp, bp, buflen);
183	if ((n < 0) || !(*name_ok)(bp)) {
184		h_errno = NO_RECOVERY;
185		return (NULL);
186	}
187	cp += n + QFIXEDSZ;
188	if (qtype == T_A || qtype == T_AAAA) {
189		/* res_send() has already verified that the query name is the
190		 * same as the one we sent; this just gets the expanded name
191		 * (i.e., with the succeeding search-domain tacked on).
192		 */
193		n = strlen(bp) + 1;		/* for the \0 */
194		host.h_name = bp;
195		bp += n;
196		buflen -= n;
197		/* The qname can be abbreviated, but h_name is now absolute. */
198		qname = host.h_name;
199	}
200	ap = host_aliases;
201	*ap = NULL;
202	host.h_aliases = host_aliases;
203	hap = h_addr_ptrs;
204	*hap = NULL;
205	host.h_addr_list = h_addr_ptrs;
206	haveanswer = 0;
207	had_error = 0;
208	_dns_ttl_ = -1;
209	while (ancount-- > 0 && cp < eom && !had_error) {
210		n = dn_expand(answer->buf, eom, cp, bp, buflen);
211		if ((n < 0) || !(*name_ok)(bp)) {
212			had_error++;
213			continue;
214		}
215		cp += n;			/* name */
216		type = _getshort(cp);
217 		cp += INT16SZ;			/* type */
218		class = _getshort(cp);
219 		cp += INT16SZ;			/* class */
220		if (qtype == T_A  && type == T_A)
221			_dns_ttl_ = _getlong(cp);
222		cp += INT32SZ;			/* TTL */
223		n = _getshort(cp);
224		cp += INT16SZ;			/* len */
225		if (class != C_IN) {
226			/* XXX - debug? syslog? */
227			cp += n;
228			continue;		/* XXX - had_error++ ? */
229		}
230		if ((qtype == T_A || qtype == T_AAAA) && type == T_CNAME) {
231			if (ap >= &host_aliases[MAXALIASES-1])
232				continue;
233			n = dn_expand(answer->buf, eom, cp, tbuf, sizeof tbuf);
234			if ((n < 0) || !(*name_ok)(tbuf)) {
235				had_error++;
236				continue;
237			}
238			cp += n;
239			/* Store alias. */
240			*ap++ = bp;
241			n = strlen(bp) + 1;	/* for the \0 */
242			bp += n;
243			buflen -= n;
244			/* Get canonical name. */
245			n = strlen(tbuf) + 1;	/* for the \0 */
246			if (n > buflen) {
247				had_error++;
248				continue;
249			}
250			strcpy(bp, tbuf);
251			host.h_name = bp;
252			bp += n;
253			buflen -= n;
254			continue;
255		}
256		if (qtype == T_PTR && type == T_CNAME) {
257			n = dn_expand(answer->buf, eom, cp, tbuf, sizeof tbuf);
258			if ((n < 0) || !res_hnok(tbuf)) {
259				had_error++;
260				continue;
261			}
262			cp += n;
263			/* Get canonical name. */
264			n = strlen(tbuf) + 1;	/* for the \0 */
265			if (n > buflen) {
266				had_error++;
267				continue;
268			}
269			strcpy(bp, tbuf);
270			tname = bp;
271			bp += n;
272			buflen -= n;
273			continue;
274		}
275		if (type != qtype) {
276			syslog(LOG_NOTICE|LOG_AUTH,
277       "gethostby*.gethostanswer: asked for \"%s %s %s\", got type \"%s\"",
278			       qname, p_class(C_IN), p_type(qtype),
279			       p_type(type));
280			cp += n;
281			continue;		/* XXX - had_error++ ? */
282		}
283		switch (type) {
284		case T_PTR:
285			if (strcasecmp(tname, bp) != 0) {
286				syslog(LOG_NOTICE|LOG_AUTH,
287				       AskedForGot, qname, bp);
288				cp += n;
289				continue;	/* XXX - had_error++ ? */
290			}
291			n = dn_expand(answer->buf, eom, cp, bp, buflen);
292			if ((n < 0) || !res_hnok(bp)) {
293				had_error++;
294				break;
295			}
296#if MULTI_PTRS_ARE_ALIASES
297			cp += n;
298			if (!haveanswer)
299				host.h_name = bp;
300			else if (ap < &host_aliases[MAXALIASES-1])
301				*ap++ = bp;
302			else
303				n = -1;
304			if (n != -1) {
305				n = strlen(bp) + 1;	/* for the \0 */
306				bp += n;
307				buflen -= n;
308			}
309			break;
310#else
311			host.h_name = bp;
312			if (_res.options & RES_USE_INET6) {
313				n = strlen(bp) + 1;	/* for the \0 */
314				bp += n;
315				buflen -= n;
316				_map_v4v6_hostent(&host, &bp, &buflen);
317			}
318			h_errno = NETDB_SUCCESS;
319			return (&host);
320#endif
321		case T_A:
322		case T_AAAA:
323			if (strcasecmp(host.h_name, bp) != 0) {
324				syslog(LOG_NOTICE|LOG_AUTH,
325				       AskedForGot, host.h_name, bp);
326				cp += n;
327				continue;	/* XXX - had_error++ ? */
328			}
329			if (n != host.h_length) {
330				cp += n;
331				continue;
332			}
333			if (!haveanswer) {
334				register int nn;
335
336				host.h_name = bp;
337				nn = strlen(bp) + 1;	/* for the \0 */
338				bp += nn;
339				buflen -= nn;
340			}
341
342			bp += sizeof(align) - ((u_long)bp % sizeof(align));
343
344			if (bp + n >= &hostbuf[sizeof hostbuf]) {
345				dprintf("size (%d) too big\n", n);
346				had_error++;
347				continue;
348			}
349			if (hap >= &h_addr_ptrs[MAXADDRS-1]) {
350				if (!toobig++)
351					dprintf("Too many addresses (%d)\n",
352						MAXADDRS);
353				cp += n;
354				continue;
355			}
356			bcopy(cp, *hap++ = bp, n);
357			bp += n;
358			buflen -= n;
359			cp += n;
360			break;
361		default:
362			dprintf("Impossible condition (type=%d)\n", type);
363			h_errno = NO_RECOVERY;
364			return (NULL);
365		} /*switch*/
366		if (!had_error)
367			haveanswer++;
368	} /*while*/
369	if (haveanswer) {
370		*ap = NULL;
371		*hap = NULL;
372# if defined(RESOLVSORT)
373		/*
374		 * Note: we sort even if host can take only one address
375		 * in its return structures - should give it the "best"
376		 * address in that case, not some random one
377		 */
378		if (_res.nsort && haveanswer > 1 && qtype == T_A)
379			addrsort(h_addr_ptrs, haveanswer);
380# endif /*RESOLVSORT*/
381		if (!host.h_name) {
382			n = strlen(qname) + 1;	/* for the \0 */
383			if (n > buflen)
384				goto try_again;
385			strcpy(bp, qname);
386			host.h_name = bp;
387			bp += n;
388			buflen -= n;
389		}
390		if (_res.options & RES_USE_INET6)
391			_map_v4v6_hostent(&host, &bp, &buflen);
392		h_errno = NETDB_SUCCESS;
393		return (&host);
394	}
395 try_again:
396	h_errno = TRY_AGAIN;
397	return (NULL);
398}
399
400struct hostent *
401_gethostbydnsname(name, af)
402	const char *name;
403	int af;
404{
405	querybuf buf;
406	register const char *cp;
407	char *bp;
408	int n, size, type, len;
409
410	if ((_res.options & RES_INIT) == 0 && res_init() == -1) {
411		h_errno = NETDB_INTERNAL;
412		return (NULL);
413	}
414
415	switch (af) {
416	case AF_INET:
417		size = INADDRSZ;
418		type = T_A;
419		break;
420	case AF_INET6:
421		size = IN6ADDRSZ;
422		type = T_AAAA;
423		break;
424	default:
425		h_errno = NETDB_INTERNAL;
426		errno = EAFNOSUPPORT;
427		return (NULL);
428	}
429
430	host.h_addrtype = af;
431	host.h_length = size;
432
433	/*
434	 * if there aren't any dots, it could be a user-level alias.
435	 * this is also done in res_query() since we are not the only
436	 * function that looks up host names.
437	 */
438	if (!strchr(name, '.') && (cp = __hostalias(name)))
439		name = cp;
440
441	/*
442	 * disallow names consisting only of digits/dots, unless
443	 * they end in a dot.
444	 */
445	if (isdigit(name[0]))
446		for (cp = name;; ++cp) {
447			if (!*cp) {
448				if (*--cp == '.')
449					break;
450				/*
451				 * All-numeric, no dot at the end.
452				 * Fake up a hostent as if we'd actually
453				 * done a lookup.
454				 */
455				if (inet_pton(af, name, host_addr) <= 0) {
456					h_errno = HOST_NOT_FOUND;
457					return (NULL);
458				}
459				strncpy(hostbuf, name, MAXDNAME);
460				hostbuf[MAXDNAME] = '\0';
461				bp = hostbuf + MAXDNAME;
462				len = sizeof hostbuf - MAXDNAME;
463				host.h_name = hostbuf;
464				host.h_aliases = host_aliases;
465				host_aliases[0] = NULL;
466				h_addr_ptrs[0] = (char *)host_addr;
467				h_addr_ptrs[1] = NULL;
468				host.h_addr_list = h_addr_ptrs;
469				if (_res.options & RES_USE_INET6)
470					_map_v4v6_hostent(&host, &bp, &len);
471				h_errno = NETDB_SUCCESS;
472				return (&host);
473			}
474			if (!isdigit(*cp) && *cp != '.')
475				break;
476		}
477
478	if ((n = res_search(name, C_IN, type, buf.buf, sizeof(buf))) < 0) {
479		dprintf("res_search failed (%d)\n", n);
480		return (NULL);
481	}
482	return (gethostanswer(&buf, n, name, type));
483}
484
485struct hostent *
486_gethostbydnsaddr(addr, len, af)
487	const char *addr;	/* XXX should have been def'd as u_char! */
488	int len, af;
489{
490	const u_char *uaddr = (const u_char *)addr;
491	static const u_char mapped[] = { 0,0, 0,0, 0,0, 0,0, 0,0, 0xff,0xff };
492	static const u_char tunnelled[] = { 0,0, 0,0, 0,0, 0,0, 0,0, 0,0 };
493	int n, size;
494	querybuf buf;
495	register struct hostent *hp;
496	char qbuf[MAXDNAME+1], *qp;
497#ifdef SUNSECURITY
498	register struct hostent *rhp;
499	char **haddr;
500	u_long old_options;
501	char hname2[MAXDNAME+1];
502#endif /*SUNSECURITY*/
503
504	if ((_res.options & RES_INIT) == 0 && res_init() == -1) {
505		h_errno = NETDB_INTERNAL;
506		return (NULL);
507	}
508	if (af == AF_INET6 && len == IN6ADDRSZ &&
509	    (!bcmp(uaddr, mapped, sizeof mapped) ||
510	     !bcmp(uaddr, tunnelled, sizeof tunnelled))) {
511		/* Unmap. */
512		addr += sizeof mapped;
513		uaddr += sizeof mapped;
514		af = AF_INET;
515		len = INADDRSZ;
516	}
517	switch (af) {
518	case AF_INET:
519		size = INADDRSZ;
520		break;
521	case AF_INET6:
522		size = IN6ADDRSZ;
523		break;
524	default:
525		errno = EAFNOSUPPORT;
526		h_errno = NETDB_INTERNAL;
527		return (NULL);
528	}
529	if (size != len) {
530		errno = EINVAL;
531		h_errno = NETDB_INTERNAL;
532		return (NULL);
533	}
534	switch (af) {
535	case AF_INET:
536		(void) sprintf(qbuf, "%u.%u.%u.%u.in-addr.arpa",
537			       (uaddr[3] & 0xff),
538			       (uaddr[2] & 0xff),
539			       (uaddr[1] & 0xff),
540			       (uaddr[0] & 0xff));
541		break;
542	case AF_INET6:
543		qp = qbuf;
544		for (n = IN6ADDRSZ - 1; n >= 0; n--) {
545			qp += SPRINTF((qp, "%x.%x.",
546				       uaddr[n] & 0xf,
547				       (uaddr[n] >> 4) & 0xf));
548		}
549		strcpy(qp, "ip6.int");
550		break;
551	default:
552		abort();
553	}
554	n = res_query(qbuf, C_IN, T_PTR, (u_char *)buf.buf, sizeof buf.buf);
555	if (n < 0) {
556		dprintf("res_query failed (%d)\n", n);
557		return (NULL);
558	}
559	if (!(hp = gethostanswer(&buf, n, qbuf, T_PTR)))
560		return (NULL);	/* h_errno was set by gethostanswer() */
561#ifdef SUNSECURITY
562	if (af == AF_INET) {
563	    /*
564	     * turn off search as the name should be absolute,
565	     * 'localhost' should be matched by defnames
566	     */
567	    strncpy(hname2, hp->h_name, MAXDNAME);
568	    hname2[MAXDNAME] = '\0';
569	    old_options = _res.options;
570	    _res.options &= ~RES_DNSRCH;
571	    _res.options |= RES_DEFNAMES;
572	    if (!(rhp = gethostbyname(hname2))) {
573		syslog(LOG_NOTICE|LOG_AUTH,
574		       "gethostbyaddr: No A record for %s (verifying [%s])",
575		       hname2, inet_ntoa(*((struct in_addr *)addr)));
576		_res.options = old_options;
577		h_errno = HOST_NOT_FOUND;
578		return (NULL);
579	    }
580	    _res.options = old_options;
581	    for (haddr = rhp->h_addr_list; *haddr; haddr++)
582		if (!memcmp(*haddr, addr, INADDRSZ))
583			break;
584	    if (!*haddr) {
585		syslog(LOG_NOTICE|LOG_AUTH,
586		       "gethostbyaddr: A record of %s != PTR record [%s]",
587		       hname2, inet_ntoa(*((struct in_addr *)addr)));
588		h_errno = HOST_NOT_FOUND;
589		return (NULL);
590	    }
591	}
592#endif /*SUNSECURITY*/
593	hp->h_addrtype = af;
594	hp->h_length = len;
595	bcopy(addr, host_addr, len);
596	h_addr_ptrs[0] = (char *)host_addr;
597	h_addr_ptrs[1] = NULL;
598	if (af == AF_INET && (_res.options & RES_USE_INET6)) {
599		_map_v4v6_address((char*)host_addr, (char*)host_addr);
600		hp->h_addrtype = AF_INET6;
601		hp->h_length = IN6ADDRSZ;
602	}
603	h_errno = NETDB_SUCCESS;
604	return (hp);
605}
606
607#ifdef RESOLVSORT
608static void
609addrsort(ap, num)
610	char **ap;
611	int num;
612{
613	int i, j;
614	char **p;
615	short aval[MAXADDRS];
616	int needsort = 0;
617
618	p = ap;
619	for (i = 0; i < num; i++, p++) {
620	    for (j = 0 ; (unsigned)j < _res.nsort; j++)
621		if (_res.sort_list[j].addr.s_addr ==
622		    (((struct in_addr *)(*p))->s_addr & _res.sort_list[j].mask))
623			break;
624	    aval[i] = j;
625	    if (needsort == 0 && i > 0 && j < aval[i-1])
626		needsort = i;
627	}
628	if (!needsort)
629	    return;
630
631	while (needsort < num) {
632	    for (j = needsort - 1; j >= 0; j--) {
633		if (aval[j] > aval[j+1]) {
634		    char *hp;
635
636		    i = aval[j];
637		    aval[j] = aval[j+1];
638		    aval[j+1] = i;
639
640		    hp = ap[j];
641		    ap[j] = ap[j+1];
642		    ap[j+1] = hp;
643
644		} else
645		    break;
646	    }
647	    needsort++;
648	}
649}
650#endif
651void
652_sethostdnsent(stayopen)
653	int stayopen;
654{
655	if ((_res.options & RES_INIT) == 0 && res_init() == -1)
656		return;
657	if (stayopen)
658		_res.options |= RES_STAYOPEN | RES_USEVC;
659}
660
661void
662_endhostdnsent()
663{
664	_res.options &= ~(RES_STAYOPEN | RES_USEVC);
665	_res_close();
666}
667