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