xref: /freebsd/lib/libc/net/gethostbydns.c (revision e627b39baccd1ec9129690167cf5e6d860509655)
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)
55 static char sccsid[] = "@(#)gethostnamadr.c	8.1 (Berkeley) 6/4/93";
56 static char rcsid[] = "$Id: gethostbydns.c,v 1.10 1996/08/29 20:07:50 peter 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 
77 extern void _res_close __P((void));
78 
79 #define SPRINTF(x) ((size_t)sprintf x)
80 
81 #define	MAXALIASES	35
82 #define	MAXADDRS	35
83 
84 static const char AskedForGot[] =
85 		"gethostby*.gethostanswer: asked for \"%s\", got \"%s\"";
86 
87 static char *h_addr_ptrs[MAXADDRS + 1];
88 static struct hostent *gethostbyname_ipv4 __P((const char *));
89 
90 static struct hostent host;
91 static char *host_aliases[MAXALIASES];
92 static char hostbuf[8*1024];
93 static u_char host_addr[16];	/* IPv4 or IPv6 */
94 
95 #ifdef RESOLVSORT
96 static 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 
105 typedef union {
106     HEADER hdr;
107     u_char buf[MAXPACKET];
108 } querybuf;
109 
110 typedef union {
111     int32_t al;
112     char ac;
113 } align;
114 
115 extern int h_errno;
116 
117 #ifdef DEBUG
118 static void
119 dprintf(msg, num)
120 	char *msg;
121 	int num;
122 {
123 	if (_res.options & RES_DEBUG) {
124 		int save = errno;
125 
126 		printf(msg, num);
127 		errno = save;
128 	}
129 }
130 #else
131 # define dprintf(msg, num) /*nada*/
132 #endif
133 
134 static struct hostent *
135 gethostanswer(answer, anslen, qname, qtype)
136 	const querybuf *answer;
137 	int anslen;
138 	const char *qname;
139 	int qtype;
140 {
141 	register const HEADER *hp;
142 	register const u_char *cp;
143 	register int n;
144 	const u_char *eom;
145 	char *bp, **ap, **hap;
146 	int type, class, buflen, ancount, qdcount;
147 	int haveanswer, had_error;
148 	int toobig = 0;
149 	char tbuf[MAXDNAME+1];
150 	const char *tname;
151 	int (*name_ok) __P((const char *));
152 
153 	tname = qname;
154 	host.h_name = NULL;
155 	eom = answer->buf + anslen;
156 	switch (qtype) {
157 	case T_A:
158 	case T_AAAA:
159 		name_ok = res_hnok;
160 		break;
161 	case T_PTR:
162 		name_ok = res_dnok;
163 		break;
164 	default:
165 		h_errno = NO_RECOVERY;
166 		return (NULL);	/* XXX should be abort(); */
167 	}
168 	/*
169 	 * find first satisfactory answer
170 	 */
171 	hp = &answer->hdr;
172 	ancount = ntohs(hp->ancount);
173 	qdcount = ntohs(hp->qdcount);
174 	bp = hostbuf;
175 	buflen = sizeof hostbuf;
176 	cp = answer->buf + HFIXEDSZ;
177 	if (qdcount != 1) {
178 		h_errno = NO_RECOVERY;
179 		return (NULL);
180 	}
181 	n = dn_expand(answer->buf, eom, cp, bp, buflen);
182 	if ((n < 0) || !(*name_ok)(bp)) {
183 		h_errno = NO_RECOVERY;
184 		return (NULL);
185 	}
186 	cp += n + QFIXEDSZ;
187 	if (qtype == T_A || qtype == T_AAAA) {
188 		/* res_send() has already verified that the query name is the
189 		 * same as the one we sent; this just gets the expanded name
190 		 * (i.e., with the succeeding search-domain tacked on).
191 		 */
192 		n = strlen(bp) + 1;		/* for the \0 */
193 		host.h_name = bp;
194 		bp += n;
195 		buflen -= n;
196 		/* The qname can be abbreviated, but h_name is now absolute. */
197 		qname = host.h_name;
198 	}
199 	ap = host_aliases;
200 	*ap = NULL;
201 	host.h_aliases = host_aliases;
202 	hap = h_addr_ptrs;
203 	*hap = NULL;
204 	host.h_addr_list = h_addr_ptrs;
205 	haveanswer = 0;
206 	had_error = 0;
207 	while (ancount-- > 0 && cp < eom && !had_error) {
208 		n = dn_expand(answer->buf, eom, cp, bp, buflen);
209 		if ((n < 0) || !(*name_ok)(bp)) {
210 			had_error++;
211 			continue;
212 		}
213 		cp += n;			/* name */
214 		type = _getshort(cp);
215  		cp += INT16SZ;			/* type */
216 		class = _getshort(cp);
217  		cp += INT16SZ + INT32SZ;	/* class, TTL */
218 		n = _getshort(cp);
219 		cp += INT16SZ;			/* len */
220 		if (class != C_IN) {
221 			/* XXX - debug? syslog? */
222 			cp += n;
223 			continue;		/* XXX - had_error++ ? */
224 		}
225 		if ((qtype == T_A || qtype == T_AAAA) && type == T_CNAME) {
226 			if (ap >= &host_aliases[MAXALIASES-1])
227 				continue;
228 			n = dn_expand(answer->buf, eom, cp, tbuf, sizeof tbuf);
229 			if ((n < 0) || !(*name_ok)(tbuf)) {
230 				had_error++;
231 				continue;
232 			}
233 			cp += n;
234 			/* Store alias. */
235 			*ap++ = bp;
236 			n = strlen(bp) + 1;	/* for the \0 */
237 			bp += n;
238 			buflen -= n;
239 			/* Get canonical name. */
240 			n = strlen(tbuf) + 1;	/* for the \0 */
241 			if (n > buflen) {
242 				had_error++;
243 				continue;
244 			}
245 			strcpy(bp, tbuf);
246 			host.h_name = bp;
247 			bp += n;
248 			buflen -= n;
249 			continue;
250 		}
251 		if (qtype == T_PTR && type == T_CNAME) {
252 			n = dn_expand(answer->buf, eom, cp, tbuf, sizeof tbuf);
253 			if ((n < 0) || !res_hnok(tbuf)) {
254 				had_error++;
255 				continue;
256 			}
257 			cp += n;
258 			/* Get canonical name. */
259 			n = strlen(tbuf) + 1;	/* for the \0 */
260 			if (n > buflen) {
261 				had_error++;
262 				continue;
263 			}
264 			strcpy(bp, tbuf);
265 			tname = bp;
266 			bp += n;
267 			buflen -= n;
268 			continue;
269 		}
270 		if (type != qtype) {
271 			syslog(LOG_NOTICE|LOG_AUTH,
272        "gethostby*.gethostanswer: asked for \"%s %s %s\", got type \"%s\"",
273 			       qname, p_class(C_IN), p_type(qtype),
274 			       p_type(type));
275 			cp += n;
276 			continue;		/* XXX - had_error++ ? */
277 		}
278 		switch (type) {
279 		case T_PTR:
280 			if (strcasecmp(tname, bp) != 0) {
281 				syslog(LOG_NOTICE|LOG_AUTH,
282 				       AskedForGot, qname, bp);
283 				cp += n;
284 				continue;	/* XXX - had_error++ ? */
285 			}
286 			n = dn_expand(answer->buf, eom, cp, bp, buflen);
287 			if ((n < 0) || !res_hnok(bp)) {
288 				had_error++;
289 				break;
290 			}
291 #if MULTI_PTRS_ARE_ALIASES
292 			cp += n;
293 			if (!haveanswer)
294 				host.h_name = bp;
295 			else if (ap < &host_aliases[MAXALIASES-1])
296 				*ap++ = bp;
297 			else
298 				n = -1;
299 			if (n != -1) {
300 				n = strlen(bp) + 1;	/* for the \0 */
301 				bp += n;
302 				buflen -= n;
303 			}
304 			break;
305 #else
306 			host.h_name = bp;
307 			if (_res.options & RES_USE_INET6) {
308 				n = strlen(bp) + 1;	/* for the \0 */
309 				bp += n;
310 				buflen -= n;
311 				_map_v4v6_hostent(&host, &bp, &buflen);
312 			}
313 			h_errno = NETDB_SUCCESS;
314 			return (&host);
315 #endif
316 		case T_A:
317 		case T_AAAA:
318 			if (strcasecmp(host.h_name, bp) != 0) {
319 				syslog(LOG_NOTICE|LOG_AUTH,
320 				       AskedForGot, host.h_name, bp);
321 				cp += n;
322 				continue;	/* XXX - had_error++ ? */
323 			}
324 			if (n != host.h_length) {
325 				cp += n;
326 				continue;
327 			}
328 			if (!haveanswer) {
329 				register int nn;
330 
331 				host.h_name = bp;
332 				nn = strlen(bp) + 1;	/* for the \0 */
333 				bp += nn;
334 				buflen -= nn;
335 			}
336 
337 			bp += sizeof(align) - ((u_long)bp % sizeof(align));
338 
339 			if (bp + n >= &hostbuf[sizeof hostbuf]) {
340 				dprintf("size (%d) too big\n", n);
341 				had_error++;
342 				continue;
343 			}
344 			if (hap >= &h_addr_ptrs[MAXADDRS-1]) {
345 				if (!toobig++)
346 					dprintf("Too many addresses (%d)\n",
347 						MAXADDRS);
348 				cp += n;
349 				continue;
350 			}
351 			bcopy(cp, *hap++ = bp, n);
352 			bp += n;
353 			buflen -= n;
354 			cp += n;
355 			break;
356 		default:
357 			dprintf("Impossible condition (type=%d)\n", type);
358 			h_errno = NO_RECOVERY;
359 			return (NULL);
360 		} /*switch*/
361 		if (!had_error)
362 			haveanswer++;
363 	} /*while*/
364 	if (haveanswer) {
365 		*ap = NULL;
366 		*hap = NULL;
367 # if defined(RESOLVSORT)
368 		/*
369 		 * Note: we sort even if host can take only one address
370 		 * in its return structures - should give it the "best"
371 		 * address in that case, not some random one
372 		 */
373 		if (_res.nsort && haveanswer > 1 && qtype == T_A)
374 			addrsort(h_addr_ptrs, haveanswer);
375 # endif /*RESOLVSORT*/
376 		if (!host.h_name) {
377 			n = strlen(qname) + 1;	/* for the \0 */
378 			if (n > buflen)
379 				goto try_again;
380 			strcpy(bp, qname);
381 			host.h_name = bp;
382 			bp += n;
383 			buflen -= n;
384 		}
385 		if (_res.options & RES_USE_INET6)
386 			_map_v4v6_hostent(&host, &bp, &buflen);
387 		h_errno = NETDB_SUCCESS;
388 		return (&host);
389 	}
390  try_again:
391 	h_errno = TRY_AGAIN;
392 	return (NULL);
393 }
394 
395 struct hostent *
396 _gethostbydnsname(name, af)
397 	const char *name;
398 	int af;
399 {
400 	querybuf buf;
401 	register const char *cp;
402 	char *bp;
403 	int n, size, type, len;
404 
405 	if ((_res.options & RES_INIT) == 0 && res_init() == -1) {
406 		h_errno = NETDB_INTERNAL;
407 		return (NULL);
408 	}
409 
410 	switch (af) {
411 	case AF_INET:
412 		size = INADDRSZ;
413 		type = T_A;
414 		break;
415 	case AF_INET6:
416 		size = IN6ADDRSZ;
417 		type = T_AAAA;
418 		break;
419 	default:
420 		h_errno = NETDB_INTERNAL;
421 		errno = EAFNOSUPPORT;
422 		return (NULL);
423 	}
424 
425 	host.h_addrtype = af;
426 	host.h_length = size;
427 
428 	/*
429 	 * if there aren't any dots, it could be a user-level alias.
430 	 * this is also done in res_query() since we are not the only
431 	 * function that looks up host names.
432 	 */
433 	if (!strchr(name, '.') && (cp = __hostalias(name)))
434 		name = cp;
435 
436 	/*
437 	 * disallow names consisting only of digits/dots, unless
438 	 * they end in a dot.
439 	 */
440 	if (isdigit(name[0]))
441 		for (cp = name;; ++cp) {
442 			if (!*cp) {
443 				if (*--cp == '.')
444 					break;
445 				/*
446 				 * All-numeric, no dot at the end.
447 				 * Fake up a hostent as if we'd actually
448 				 * done a lookup.
449 				 */
450 				if (inet_pton(af, name, host_addr) <= 0) {
451 					h_errno = HOST_NOT_FOUND;
452 					return (NULL);
453 				}
454 				strncpy(hostbuf, name, MAXDNAME);
455 				hostbuf[MAXDNAME] = '\0';
456 				bp = hostbuf + MAXDNAME;
457 				len = sizeof hostbuf - MAXDNAME;
458 				host.h_name = hostbuf;
459 				host.h_aliases = host_aliases;
460 				host_aliases[0] = NULL;
461 				h_addr_ptrs[0] = (char *)host_addr;
462 				h_addr_ptrs[1] = NULL;
463 				host.h_addr_list = h_addr_ptrs;
464 				if (_res.options & RES_USE_INET6)
465 					_map_v4v6_hostent(&host, &bp, &len);
466 				h_errno = NETDB_SUCCESS;
467 				return (&host);
468 			}
469 			if (!isdigit(*cp) && *cp != '.')
470 				break;
471 		}
472 
473 	if ((n = res_search(name, C_IN, type, buf.buf, sizeof(buf))) < 0) {
474 		dprintf("res_search failed (%d)\n", n);
475 		return (NULL);
476 	}
477 	return (gethostanswer(&buf, n, name, type));
478 }
479 
480 struct hostent *
481 _gethostbydnsaddr(addr, len, af)
482 	const char *addr;	/* XXX should have been def'd as u_char! */
483 	int len, af;
484 {
485 	const u_char *uaddr = (const u_char *)addr;
486 	static const u_char mapped[] = { 0,0, 0,0, 0,0, 0,0, 0,0, 0xff,0xff };
487 	static const u_char tunnelled[] = { 0,0, 0,0, 0,0, 0,0, 0,0, 0,0 };
488 	int n, size;
489 	querybuf buf;
490 	register struct hostent *hp;
491 	char qbuf[MAXDNAME+1], *qp;
492 #ifdef SUNSECURITY
493 	register struct hostent *rhp;
494 	char **haddr;
495 	u_long old_options;
496 	char hname2[MAXDNAME+1];
497 #endif /*SUNSECURITY*/
498 
499 	if ((_res.options & RES_INIT) == 0 && res_init() == -1) {
500 		h_errno = NETDB_INTERNAL;
501 		return (NULL);
502 	}
503 	if (af == AF_INET6 && len == IN6ADDRSZ &&
504 	    (!bcmp(uaddr, mapped, sizeof mapped) ||
505 	     !bcmp(uaddr, tunnelled, sizeof tunnelled))) {
506 		/* Unmap. */
507 		addr += sizeof mapped;
508 		uaddr += sizeof mapped;
509 		af = AF_INET;
510 		len = INADDRSZ;
511 	}
512 	switch (af) {
513 	case AF_INET:
514 		size = INADDRSZ;
515 		break;
516 	case AF_INET6:
517 		size = IN6ADDRSZ;
518 		break;
519 	default:
520 		errno = EAFNOSUPPORT;
521 		h_errno = NETDB_INTERNAL;
522 		return (NULL);
523 	}
524 	if (size != len) {
525 		errno = EINVAL;
526 		h_errno = NETDB_INTERNAL;
527 		return (NULL);
528 	}
529 	switch (af) {
530 	case AF_INET:
531 		(void) sprintf(qbuf, "%u.%u.%u.%u.in-addr.arpa",
532 			       (uaddr[3] & 0xff),
533 			       (uaddr[2] & 0xff),
534 			       (uaddr[1] & 0xff),
535 			       (uaddr[0] & 0xff));
536 		break;
537 	case AF_INET6:
538 		qp = qbuf;
539 		for (n = IN6ADDRSZ - 1; n >= 0; n--) {
540 			qp += SPRINTF((qp, "%x.%x.",
541 				       uaddr[n] & 0xf,
542 				       (uaddr[n] >> 4) & 0xf));
543 		}
544 		strcpy(qp, "ip6.int");
545 		break;
546 	default:
547 		abort();
548 	}
549 	n = res_query(qbuf, C_IN, T_PTR, (u_char *)buf.buf, sizeof buf.buf);
550 	if (n < 0) {
551 		dprintf("res_query failed (%d)\n", n);
552 		return (NULL);
553 	}
554 	if (!(hp = gethostanswer(&buf, n, qbuf, T_PTR)))
555 		return (NULL);	/* h_errno was set by gethostanswer() */
556 #ifdef SUNSECURITY
557 	if (af == AF_INET) {
558 	    /*
559 	     * turn off search as the name should be absolute,
560 	     * 'localhost' should be matched by defnames
561 	     */
562 	    strncpy(hname2, hp->h_name, MAXDNAME);
563 	    hname2[MAXDNAME] = '\0';
564 	    old_options = _res.options;
565 	    _res.options &= ~RES_DNSRCH;
566 	    _res.options |= RES_DEFNAMES;
567 	    if (!(rhp = gethostbyname(hname2))) {
568 		syslog(LOG_NOTICE|LOG_AUTH,
569 		       "gethostbyaddr: No A record for %s (verifying [%s])",
570 		       hname2, inet_ntoa(*((struct in_addr *)addr)));
571 		_res.options = old_options;
572 		h_errno = HOST_NOT_FOUND;
573 		return (NULL);
574 	    }
575 	    _res.options = old_options;
576 	    for (haddr = rhp->h_addr_list; *haddr; haddr++)
577 		if (!memcmp(*haddr, addr, INADDRSZ))
578 			break;
579 	    if (!*haddr) {
580 		syslog(LOG_NOTICE|LOG_AUTH,
581 		       "gethostbyaddr: A record of %s != PTR record [%s]",
582 		       hname2, inet_ntoa(*((struct in_addr *)addr)));
583 		h_errno = HOST_NOT_FOUND;
584 		return (NULL);
585 	    }
586 	}
587 #endif /*SUNSECURITY*/
588 	hp->h_addrtype = af;
589 	hp->h_length = len;
590 	bcopy(addr, host_addr, len);
591 	h_addr_ptrs[0] = (char *)host_addr;
592 	h_addr_ptrs[1] = NULL;
593 	if (af == AF_INET && (_res.options & RES_USE_INET6)) {
594 		_map_v4v6_address((char*)host_addr, (char*)host_addr);
595 		hp->h_addrtype = AF_INET6;
596 		hp->h_length = IN6ADDRSZ;
597 	}
598 	h_errno = NETDB_SUCCESS;
599 	return (hp);
600 }
601 
602 #ifdef RESOLVSORT
603 static void
604 addrsort(ap, num)
605 	char **ap;
606 	int num;
607 {
608 	int i, j;
609 	char **p;
610 	short aval[MAXADDRS];
611 	int needsort = 0;
612 
613 	p = ap;
614 	for (i = 0; i < num; i++, p++) {
615 	    for (j = 0 ; (unsigned)j < _res.nsort; j++)
616 		if (_res.sort_list[j].addr.s_addr ==
617 		    (((struct in_addr *)(*p))->s_addr & _res.sort_list[j].mask))
618 			break;
619 	    aval[i] = j;
620 	    if (needsort == 0 && i > 0 && j < aval[i-1])
621 		needsort = i;
622 	}
623 	if (!needsort)
624 	    return;
625 
626 	while (needsort < num) {
627 	    for (j = needsort - 1; j >= 0; j--) {
628 		if (aval[j] > aval[j+1]) {
629 		    char *hp;
630 
631 		    i = aval[j];
632 		    aval[j] = aval[j+1];
633 		    aval[j+1] = i;
634 
635 		    hp = ap[j];
636 		    ap[j] = ap[j+1];
637 		    ap[j+1] = hp;
638 
639 		} else
640 		    break;
641 	    }
642 	    needsort++;
643 	}
644 }
645 #endif
646 void
647 _sethostdnsent(stayopen)
648 	int stayopen;
649 {
650 	if ((_res.options & RES_INIT) == 0 && res_init() == -1)
651 		return;
652 	if (stayopen)
653 		_res.options |= RES_STAYOPEN | RES_USEVC;
654 }
655 
656 void
657 _endhostdnsent()
658 {
659 	_res.options &= ~(RES_STAYOPEN | RES_USEVC);
660 	_res_close();
661 }
662