1 /*
2  * Copyright (c) 2000, 2001, 2002, 2009, 2010, 2011, 2013, 2014, 2017
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 are met:
7  *     * Redistributions of source code must retain the above copyright
8  *       notice, this list of conditions and the following disclaimer.
9  *     * 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  *     * Neither the name of the University nor the names of its contributors
13  *       may be used to endorse or promote products derived from this software
14  *       without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 #ifndef lint
29 static const char rcsid[] __attribute__ ((unused)) =
30     "@(#) $Id: nb_dns.c 210 2017-10-20 23:30:56Z leres $ (LBL)";
31 #endif
32 /*
33  * nb_dns - non-blocking dns routines
34  *
35  * This version works with BIND 9
36  *
37  * Note: The code here is way more complicated than it should be but
38  * although the interface to send requests is public, the routine to
39  * crack reply buffers is private.
40  */
41 
42 #ifdef notdef
43 #include "config.h"			/* must appear before first ifdef */
44 #endif
45 
46 #include <sys/types.h>
47 #include <sys/socket.h>
48 
49 #include <netinet/in.h>
50 
51 #include <arpa/inet.h>
52 #include <arpa/nameser.h>
53 
54 #include <errno.h>
55 #ifdef HAVE_MEMORY_H
56 #include <memory.h>
57 #endif
58 #include <netdb.h>
59 #include <resolv.h>
60 #include <stdio.h>
61 #include <stdlib.h>
62 #include <string.h>
63 #include <unistd.h>
64 
65 #include "gnuc.h"
66 #ifdef HAVE_OS_PROTO_H
67 #include "os-proto.h"
68 #endif
69 
70 #include "nb_dns.h"
71 
72 #if PACKETSZ > 1024
73 #define MAXPACKET	PACKETSZ
74 #else
75 #define MAXPACKET	1024
76 #endif
77 
78 #ifdef DO_SOCK_DECL
79 extern int socket(int, int, int);
80 extern int connect(int, const struct sockaddr *, int);
81 extern int send(int, const void *, int, int);
82 extern int recvfrom(int, void *, int, int, struct sockaddr *, int *);
83 #endif
84 
85 /* Private data */
86 struct nb_dns_entry {
87 	struct nb_dns_entry *next;
88 	char name[NS_MAXDNAME + 1];
89 	int qtype;			/* query type */
90 	int atype;			/* address family */
91 	int asize;			/* address size */
92 	u_short id;
93 	void *cookie;
94 };
95 
96 #ifndef MAXALIASES
97 #define MAXALIASES	35
98 #endif
99 #ifndef MAXADDRS
100 #define MAXADDRS	35
101 #endif
102 
103 struct nb_dns_hostent {
104 	struct hostent hostent;
105 	int numaliases;
106 	int numaddrs;
107 	char *host_aliases[MAXALIASES + 1];
108 	char *h_addr_ptrs[MAXADDRS + 1];
109 	char hostbuf[8 * 1024];
110 };
111 
112 struct nb_dns_info {
113 	int s;				/* Resolver file descriptor */
114 	struct sockaddr_in server;	/* server address to bind to */
115 	struct nb_dns_entry *list;	/* outstanding requests */
116 	struct nb_dns_hostent dns_hostent;
117 };
118 
119 /* Forwards */
120 static int _nb_dns_mkquery(struct nb_dns_info *, const char *, int, int,
121     void *, char *);
122 static int _nb_dns_cmpsockaddr(struct sockaddr *, struct sockaddr *, char *);
123 
124 static char *
my_strerror(int errnum)125 my_strerror(int errnum)
126 {
127 #if HAVE_STRERROR
128 	extern char *strerror(int);
129 	return strerror(errnum);
130 #else
131 	static char errnum_buf[32];
132 	snprintf(errnum_buf, sizeof(errnum_buf), "errno %d", errnum);
133 	return errnum_buf;
134 #endif
135 }
136 
137 struct nb_dns_info *
nb_dns_init(char * errstr)138 nb_dns_init(char *errstr)
139 {
140 	struct nb_dns_info *nd;
141 
142 	nd = (struct nb_dns_info *)malloc(sizeof(*nd));
143 	if (nd == NULL) {
144 		snprintf(errstr, NB_DNS_ERRSIZE, "nb_dns_init: malloc(): %s",
145 		    my_strerror(errno));
146 		return (NULL);
147 	}
148 	memset(nd, 0, sizeof(*nd));
149 	nd->s = -1;
150 
151 	/* XXX should be able to init static hostent struct some other way */
152 	(void)gethostbyname("localhost.");
153 
154 	if ((_res.options & RES_INIT) == 0 && res_init() == -1) {
155 		snprintf(errstr, NB_DNS_ERRSIZE, "res_init() failed");
156 		free(nd);
157 		return (NULL);
158 	}
159 	nd->s = socket(PF_INET, SOCK_DGRAM, 0);
160 	if (nd->s < 0) {
161 		snprintf(errstr, NB_DNS_ERRSIZE, "socket(): %s",
162 		    my_strerror(errno));
163 		free(nd);
164 		return (NULL);
165 	}
166 
167 	/* XXX should use resolver config */
168 	nd->server = _res.nsaddr_list[0];
169 
170 	if (connect(nd->s, (struct sockaddr *)&nd->server,
171 	    sizeof(struct sockaddr)) < 0) {
172 		snprintf(errstr, NB_DNS_ERRSIZE, "connect(%s): %s",
173 		    inet_ntoa(nd->server.sin_addr), my_strerror(errno));
174 		close(nd->s);
175 		free(nd);
176 		return (NULL);
177 	}
178 
179 	return (nd);
180 }
181 
182 void
nb_dns_finish(struct nb_dns_info * nd)183 nb_dns_finish(struct nb_dns_info *nd)
184 {
185 	struct nb_dns_entry *ne, *ne2;
186 
187 	ne = nd->list;
188 	while (ne != NULL) {
189 		ne2 = ne;
190 		ne = ne->next;
191 		free(ne2);
192 	}
193 	close(nd->s);
194 	free(nd);
195 }
196 
197 int
nb_dns_fd(struct nb_dns_info * nd)198 nb_dns_fd(struct nb_dns_info *nd)
199 {
200 
201 	return (nd->s);
202 }
203 
204 static int
_nb_dns_cmpsockaddr(struct sockaddr * sa1,struct sockaddr * sa2,char * errstr)205 _nb_dns_cmpsockaddr(struct sockaddr *sa1, struct sockaddr *sa2, char *errstr)
206 {
207 	struct sockaddr_in *sin1, *sin2;
208 #ifdef AF_INET6
209 	struct sockaddr_in6 *sin6a, *sin6b;
210 #endif
211 	const static char serr[] = "answer from wrong nameserver (%d)";
212 
213 	if (sa1->sa_family != sa2->sa_family) {
214 		snprintf(errstr, NB_DNS_ERRSIZE, serr, 1);
215 		return (-1);
216 	}
217 	switch (sa1->sa_family) {
218 
219 	case AF_INET:
220 		sin1 = (struct sockaddr_in *)sa1;
221 		sin2 = (struct sockaddr_in *)sa2;
222 		if (sin1->sin_port != sin2->sin_port) {
223 			snprintf(errstr, NB_DNS_ERRSIZE, serr, 2);
224 			return (-1);
225 		}
226 		if (sin1->sin_addr.s_addr != sin2->sin_addr.s_addr) {
227 			snprintf(errstr, NB_DNS_ERRSIZE, serr, 3);
228 			return (-1);
229 		}
230 		break;
231 
232 #ifdef AF_INET6
233 	case AF_INET6:
234 		sin6a = (struct sockaddr_in6 *)sa1;
235 		sin6b = (struct sockaddr_in6 *)sa2;
236 		if (sin6a->sin6_port != sin6b->sin6_port) {
237 			snprintf(errstr, NB_DNS_ERRSIZE, serr, 2);
238 			return (-1);
239 		}
240 		if (memcmp(&sin6a->sin6_addr, &sin6b->sin6_addr,
241 		    sizeof(sin6a->sin6_addr)) != 0) {
242 			snprintf(errstr, NB_DNS_ERRSIZE, serr, 3);
243 			return (-1);
244 		}
245 		break;
246 #endif
247 
248 	default:
249 		snprintf(errstr, NB_DNS_ERRSIZE, serr, 4);
250 		return (-1);
251 	}
252 	return (0);
253 }
254 
255 static int
_nb_dns_mkquery(struct nb_dns_info * nd,const char * name,int atype,int qtype,void * cookie,char * errstr)256 _nb_dns_mkquery(struct nb_dns_info *nd, const char *name, int atype,
257     int qtype, void * cookie, char *errstr)
258 {
259 	HEADER *hp;
260 	int n;
261 	u_char *msg;
262 	struct nb_dns_entry *ne;
263 
264 	/* Allocate an entry */
265 	ne = (struct nb_dns_entry *)calloc(1, sizeof(*ne));
266 	if (ne == NULL) {
267 		snprintf(errstr, NB_DNS_ERRSIZE, "calloc(): %s",
268 		    my_strerror(errno));
269 		return (-1);
270 	}
271 	strncpy(ne->name, name, sizeof(ne->name));
272 	ne->name[sizeof(ne->name) - 1] = '\0';
273 	ne->qtype = qtype;
274 	ne->atype = atype;
275 	switch (atype) {
276 
277 	case AF_INET:
278 		ne->asize = NS_INADDRSZ;
279 		break;
280 
281 #ifdef AF_INET6
282 	case AF_INET6:
283 		ne->asize = NS_IN6ADDRSZ;
284 		break;
285 #endif
286 
287 	default:
288 		snprintf(errstr, NB_DNS_ERRSIZE,
289 		    "_nb_dns_mkquery: bad family %d", atype);
290 		free(ne);
291 		return (-1);
292 	}
293 
294 	/* Allocate msg buffer */
295 	msg = calloc(1, MAXPACKET);
296 	if (msg == NULL) {
297 		snprintf(errstr, NB_DNS_ERRSIZE, "calloc() msg: %s",
298 		    my_strerror(errno));
299 		free(ne);
300 		return (-1);
301 	}
302 
303 	/* Build the request */
304 	n = res_mkquery(
305 	    ns_o_query,			/* op code (query) */
306 	    name,			/* domain name */
307 	    ns_c_in,			/* query class (internet) */
308 	    qtype,			/* query type */
309 	    NULL,			/* data */
310 	    0,				/* length of data */
311 	    NULL,			/* new rr */
312 	    (u_char *)msg,		/* buffer */
313 	    MAXPACKET);			/* size of buffer */
314 	if (n < 0) {
315 		snprintf(errstr, NB_DNS_ERRSIZE, "res_mkquery() failed (%d)",
316 		    MAXPACKET);
317 		free(ne);
318 		free(msg);
319 		return (-1);
320 	}
321 
322 	hp = (HEADER *)msg;
323 	ne->id = htons(hp->id);
324 
325 	if (send(nd->s, (char *)msg, n, 0) != n) {
326 		snprintf(errstr, NB_DNS_ERRSIZE, "send(): %s",
327 		    my_strerror(errno));
328 		free(ne);
329 		free(msg);
330 		return (-1);
331 	}
332 
333 	ne->next = nd->list;
334 	ne->cookie = cookie;
335 	nd->list = ne;
336 	free(msg);
337 
338 	return(0);
339 }
340 
341 int
nb_dns_host_request(struct nb_dns_info * nd,const char * name,void * cookie,char * errstr)342 nb_dns_host_request(struct nb_dns_info *nd, const char *name,
343     void *cookie, char *errstr)
344 {
345 
346 	return (nb_dns_host_request2(nd, name, AF_INET, cookie, errstr));
347 }
348 
349 int
nb_dns_host_request2(struct nb_dns_info * nd,const char * name,int af,void * cookie,char * errstr)350 nb_dns_host_request2(struct nb_dns_info *nd, const char *name,
351     int af, void *cookie, char *errstr)
352 {
353 	int qtype;
354 
355 	switch (af) {
356 
357 	case AF_INET:
358 		qtype = T_A;
359 		break;
360 
361 #ifdef AF_INET6
362 	case AF_INET6:
363 		qtype = T_AAAA;
364 		break;
365 #endif
366 
367 	default:
368 		snprintf(errstr, NB_DNS_ERRSIZE,
369 		    "nb_dns_host_request2(): uknown address family %d", af);
370 		return (-1);
371 	}
372 	return (_nb_dns_mkquery(nd, name, af, qtype, cookie, errstr));
373 }
374 
375 int
nb_dns_addr_request(struct nb_dns_info * nd,nb_uint32_t addr,void * cookie,char * errstr)376 nb_dns_addr_request(struct nb_dns_info *nd, nb_uint32_t addr,
377     void *cookie, char *errstr)
378 {
379 
380 	return (nb_dns_addr_request2(nd, (char *)&addr, AF_INET,
381 	    cookie, errstr));
382 }
383 
384 int
nb_dns_addr_request2(struct nb_dns_info * nd,char * addrp,int af,void * cookie,char * errstr)385 nb_dns_addr_request2(struct nb_dns_info *nd, char *addrp,
386     int af, void *cookie, char *errstr)
387 {
388 #ifdef AF_INET6
389 	char *cp;
390 	int n, i;
391 	size_t size;
392 #endif
393 	u_char *uaddr;
394 	char name[NS_MAXDNAME + 1];
395 
396 	switch (af) {
397 
398 	case AF_INET:
399 		uaddr = (u_char *)addrp;
400 		snprintf(name, sizeof(name), "%u.%u.%u.%u.in-addr.arpa",
401 		    (uaddr[3] & 0xff),
402 		    (uaddr[2] & 0xff),
403 		    (uaddr[1] & 0xff),
404 		    (uaddr[0] & 0xff));
405 		break;
406 
407 #ifdef AF_INET6
408 	case AF_INET6:
409 		uaddr = (u_char *)addrp;
410 		cp = name;
411 		size = sizeof(name);
412 		for (n = NS_IN6ADDRSZ - 1; n >= 0; --n) {
413 			snprintf(cp, size, "%x.%x.",
414 			    (uaddr[n] & 0xf),
415 			    (uaddr[n] >> 4) & 0xf);
416 			i = strlen(cp);
417 			size -= i;
418 			cp += i;
419 		}
420 		snprintf(cp, size, "ip6.arpa");
421 		break;
422 #endif
423 
424 	default:
425 		snprintf(errstr, NB_DNS_ERRSIZE,
426 		    "nb_dns_addr_request2(): uknown address family %d", af);
427 		return (-1);
428 	}
429 
430 	return (_nb_dns_mkquery(nd, name, af, T_PTR, cookie, errstr));
431 }
432 
433 int
nb_dns_abort_request(struct nb_dns_info * nd,void * cookie)434 nb_dns_abort_request(struct nb_dns_info *nd, void *cookie)
435 {
436 	struct nb_dns_entry *ne, *lastne;
437 
438 	/* Try to find this request on the outstanding request list */
439 	lastne = NULL;
440 	for (ne = nd->list; ne != NULL; ne = ne->next) {
441 		if (ne->cookie == cookie)
442 			break;
443 		lastne = ne;
444 	}
445 
446 	/* Not a currently pending request */
447 	if (ne == NULL)
448 		return (-1);
449 
450 	/* Unlink this entry */
451 	if (lastne == NULL)
452 		nd->list = ne->next;
453 	else
454 		lastne->next = ne->next;
455 	ne->next = NULL;
456 
457 	return (0);
458 }
459 
460 /* Returns 1 with an answer, 0 when reply was old, -1 on fatal errors */
461 int
nb_dns_activity(struct nb_dns_info * nd,struct nb_dns_result * nr,char * errstr)462 nb_dns_activity(struct nb_dns_info *nd, struct nb_dns_result *nr, char *errstr)
463 {
464 	int msglen, qtype, atype, n, i;
465 	struct nb_dns_entry *ne, *lastne;
466 	socklen_t fromlen;
467 	struct sockaddr from;
468 	u_long msg[MAXPACKET / sizeof(u_long)];
469 	char *bp, *ep;
470 	char **ap, **hap;
471 	u_int16_t id;
472 	const u_char *rdata;
473 	struct hostent *he;
474 	size_t rdlen;
475 	ns_msg handle;
476 	ns_rr rr;
477 
478 	/* This comes from the second half of do_query() */
479 	fromlen = sizeof(from);
480 	msglen = recvfrom(nd->s, (char *)msg, sizeof(msg), 0, &from, &fromlen);
481 	if (msglen <= 0) {
482 		snprintf(errstr, NB_DNS_ERRSIZE, "recvfrom(): %s",
483 		    my_strerror(errno));
484 		return (-1);
485 	}
486 	if (msglen < HFIXEDSZ) {
487 		snprintf(errstr, NB_DNS_ERRSIZE, "recvfrom(): undersized: %d",
488 		    msglen);
489 		return (-1);
490 	}
491 	if (ns_initparse((u_char *)msg, msglen, &handle) < 0) {
492 		snprintf(errstr, NB_DNS_ERRSIZE, "ns_initparse(): %s",
493 		    my_strerror(errno));
494 		nr->host_errno = NO_RECOVERY;
495 		return (-1);
496 	}
497 
498 	/* RES_INSECURE1 style check */
499 	if (_nb_dns_cmpsockaddr((struct sockaddr *)&nd->server, &from,
500 	    errstr) < 0) {
501 		nr->host_errno = NO_RECOVERY;
502 		return (-1);
503 	}
504 
505 	/* Search for this request */
506 	lastne = NULL;
507 	id = ns_msg_id(handle);
508 	for (ne = nd->list; ne != NULL; ne = ne->next) {
509 		if (ne->id == id)
510 			break;
511 		lastne = ne;
512 	}
513 
514 	/* Not an answer to a question we care about anymore */
515 	if (ne == NULL)
516 		return (0);
517 
518 	/* Unlink this entry */
519 	if (lastne == NULL)
520 		nd->list = ne->next;
521 	else
522 		lastne->next = ne->next;
523 	ne->next = NULL;
524 
525 	/* RES_INSECURE2 style check */
526 	/* XXX not implemented */
527 
528 	/* Initialize result struct */
529 	memset(nr, 0, sizeof(*nr));
530 	nr->cookie = ne->cookie;
531 	qtype = ne->qtype;
532 
533 	/* Deal with various errors */
534 	switch (ns_msg_getflag(handle, ns_f_rcode)) {
535 
536 	case ns_r_nxdomain:
537 		nr->host_errno = HOST_NOT_FOUND;
538 		free(ne);
539 		return (1);
540 
541 	case ns_r_servfail:
542 		nr->host_errno = TRY_AGAIN;
543 		free(ne);
544 		return (1);
545 
546 	case ns_r_noerror:
547 		break;
548 
549 	case ns_r_formerr:
550 	case ns_r_notimpl:
551 	case ns_r_refused:
552 	default:
553 		nr->host_errno = NO_RECOVERY;
554 		free(ne);
555 		return (1);
556 	}
557 
558 	/* Loop through records in packet */
559 	memset(&rr, 0, sizeof(rr));
560 	memset(&nd->dns_hostent, 0, sizeof(nd->dns_hostent));
561 	he = &nd->dns_hostent.hostent;
562 	/* XXX no support for aliases */
563 	he->h_aliases = nd->dns_hostent.host_aliases;
564 	he->h_addr_list = nd->dns_hostent.h_addr_ptrs;
565 	he->h_addrtype = ne->atype;
566 	he->h_length = ne->asize;
567 	free(ne);
568 
569 	bp = nd->dns_hostent.hostbuf;
570 	ep = bp + sizeof(nd->dns_hostent.hostbuf);
571 	hap = he->h_addr_list;
572 	ap = he->h_aliases;
573 
574 	for (i = 0; i < ns_msg_count(handle, ns_s_an); i++) {
575 		/* Parse next record */
576 		if (ns_parserr(&handle, ns_s_an, i, &rr) < 0) {
577 			if (errno != ENODEV) {
578 				nr->host_errno = NO_RECOVERY;
579 				return (1);
580 			}
581 			/* All done */
582 			break;
583 		}
584 
585 		/* Ignore records that don't answer our query (e.g. CNAMEs) */
586 		atype = ns_rr_type(rr);
587 		if (atype != qtype)
588 			continue;
589 
590 		rdata = ns_rr_rdata(rr);
591 		rdlen = ns_rr_rdlen(rr);
592 		switch (atype) {
593 
594 		case T_A:
595 		case T_AAAA:
596 			if (rdlen != he->h_length) {
597 				snprintf(errstr, NB_DNS_ERRSIZE,
598 				    "nb_dns_activity(): bad rdlen %d",
599 				    (int)rdlen);
600 				nr->host_errno = NO_RECOVERY;
601 				return (-1);
602 			}
603 
604 			if (bp + rdlen >= ep) {
605 				snprintf(errstr, NB_DNS_ERRSIZE,
606 				    "nb_dns_activity(): overflow 1");
607 				nr->host_errno = NO_RECOVERY;
608 				return (-1);
609 			}
610 			if (nd->dns_hostent.numaddrs + 1 >= MAXADDRS) {
611 				snprintf(errstr, NB_DNS_ERRSIZE,
612 				    "nb_dns_activity(): overflow 2");
613 				nr->host_errno = NO_RECOVERY;
614 				return (-1);
615 			}
616 			memcpy(bp, rdata, rdlen);
617 			*hap++ = bp;
618 			bp += rdlen;
619 			++nd->dns_hostent.numaddrs;
620 
621 			/* Keep looking for more A records */
622 			break;
623 
624 		case T_PTR:
625 			n = dn_expand((const u_char *)msg,
626 			    (const u_char *)msg + msglen, rdata, bp, ep - bp);
627 			if (n < 0) {
628 				/* XXX return -1 here ??? */
629 				nr->host_errno = NO_RECOVERY;
630 				return (1);
631 			}
632 			he->h_name = bp;
633 			/* XXX check for overflow */
634 			bp += n;		/* returned len includes EOS */
635 
636 			/* "Find first satisfactory answer" */
637 			nr->hostent = he;
638 			return (1);
639 		}
640 	}
641 
642 	nr->hostent = he;
643 	return (1);
644 }
645