ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/hopm/releases/1.0.4/src/firedns.c
Revision: 5717
Committed: Tue Mar 17 19:12:28 2015 UTC (9 years ago) by michael
Content type: text/x-csrc
File size: 17509 byte(s)
Log Message:
RELEASE TAG 1.0.4

File Contents

# Content
1 /*
2 firedns.c - firedns library
3 Copyright (C) 2002 Ian Gulliver
4
5 This file has been gutted and mucked with for use in BOPM - see the
6 real library at http://ares.penguinhosting.net/~ian/ before you judge
7 firedns based on this..
8
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of version 2 of the GNU General Public License as
11 published by the Free Software Foundation.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22
23 #include "setup.h"
24
25 #include <stdlib.h>
26 #include <time.h>
27 #include <sys/types.h>
28 #include <sys/socket.h>
29 #include <poll.h>
30 #include <sys/time.h>
31 #include <netinet/in.h>
32 #include <arpa/inet.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <stdio.h>
36 #include <errno.h>
37 #include <fcntl.h>
38
39 #include "compat.h"
40 #include "memory.h"
41 #include "firedns.h"
42 #include "config.h"
43 #include "list.h"
44 #include "log.h"
45 #include "dnsbl.h"
46
47 #define FIREDNS_TRIES 3
48
49 int firedns_errno = FDNS_ERR_NONE;
50
51 /* Variables local to this file */
52 static unsigned int firedns_fdinuse;
53
54 /* up to FDNS_MAX nameservers; populated by firedns_init() */
55 static struct in_addr servers4[FDNS_MAX];
56 static struct in6_addr servers6[FDNS_MAX];
57 /* actual count of nameservers; set by firedns_init() */
58 static int i4;
59 static int i6;
60
61 /*
62 * Linked list of open DNS queries; populated by firedns_add_query(),
63 * decimated by firedns_getresult()
64 */
65 static list_t *CONNECTIONS;
66
67 /*
68 * List of errors, in order of values used in FDNS_ERR_*, returned by
69 * firedns_strerror
70 */
71 static const char *errors[] =
72 {
73 "Success",
74 "Format error",
75 "Server failure",
76 "Name error",
77 "Not implemented",
78 "Refused",
79 "Timeout",
80 "Network error",
81 "FD Limit reached",
82 "Unknown error"
83 };
84
85 /* Structures */
86
87 /* open DNS query */
88 struct s_connection
89 {
90 /*
91 * unique ID (random number), matches header ID; both set by
92 * firedns_add_query()
93 */
94 unsigned char id[2];
95 uint16_t class;
96 uint16_t type;
97
98 /* file descriptor returned from sockets */
99 int fd;
100 void *info;
101 time_t start;
102 char lookup[256];
103 int v6;
104 };
105
106 struct s_rr_middle
107 {
108 uint16_t type;
109 uint16_t class;
110
111 /* XXX - firedns depends on this being 4 bytes */
112 uint32_t ttl;
113 uint16_t rdlength;
114 };
115
116 /* DNS query header */
117 struct s_header
118 {
119 unsigned char id[2];
120 unsigned char flags1;
121 #define FLAGS1_MASK_QR 0x80
122 /* bitshift right 3 */
123 #define FLAGS1_MASK_OPCODE 0x78
124 #define FLAGS1_MASK_AA 0x04
125 #define FLAGS1_MASK_TC 0x02
126 #define FLAGS1_MASK_RD 0x01
127
128 unsigned char flags2;
129 #define FLAGS2_MASK_RA 0x80
130 #define FLAGS2_MASK_Z 0x70
131 #define FLAGS2_MASK_RCODE 0x0f
132
133 uint16_t qdcount;
134 uint16_t ancount;
135 uint16_t nscount;
136 uint16_t arcount;
137
138 /* DNS question, populated by firedns_build_query_payload() */
139 unsigned char payload[512];
140 };
141
142 /* Function prototypes */
143 static struct s_connection *firedns_add_query(void);
144 static int firedns_doquery(struct s_connection *);
145 static int firedns_build_query_payload(const char *const, uint16_t, uint16_t, unsigned char *);
146 static int firedns_send_requests(struct s_header *, struct s_connection *, int);
147
148
149 void
150 firedns_init(void)
151 {
152 /*
153 * populates servers4 (or -6) struct with up to FDNS_MAX nameserver IP
154 * addresses from /etc/firedns.conf (or /etc/resolv.conf)
155 */
156 FILE *f;
157 int i;
158 struct in_addr addr4;
159 struct in6_addr addr6;
160 char buf[1024];
161 char *p = NULL;
162
163 i6 = 0;
164 i4 = 0;
165
166 /* Initialize connections list */
167 CONNECTIONS = list_create();
168
169 srand((unsigned int)time(NULL));
170 memset(servers4, 0, sizeof(struct in_addr) * FDNS_MAX);
171 memset(servers6, 0, sizeof(struct in6_addr) * FDNS_MAX);
172
173 /* read etc/firedns.conf if we've got it, otherwise parse /etc/resolv.conf */
174 f = fopen(FDNS_CONFIG_PREF, "r");
175
176 if (f == NULL)
177 {
178 f = fopen(FDNS_CONFIG_FBCK, "r");
179
180 if (f == NULL)
181 {
182 log_printf("Unable to open %s", FDNS_CONFIG_FBCK);
183 return;
184 }
185
186 while (fgets(buf, sizeof(buf), f))
187 {
188 if ((p = strchr(buf, '\n')))
189 *p = '\0';
190
191 if (strncmp(buf, "nameserver", 10) == 0)
192 {
193 i = 10;
194
195 while (buf[i] == ' ' || buf[i] == '\t')
196 ++i;
197
198 if (i6 < FDNS_MAX)
199 {
200 if (inet_pton(AF_INET6, &buf[i], &addr6) > 0)
201 {
202 memcpy(&servers6[i6++], &addr6, sizeof(struct in6_addr));
203 continue;
204 }
205 }
206
207 if (i4 < FDNS_MAX)
208 {
209 if (inet_pton(AF_INET, &buf[i], &addr4) > 0)
210 memcpy(&servers4[i4++], &addr4, sizeof(struct in_addr));
211 }
212 }
213 }
214 }
215 else
216 {
217 while (fgets(buf, sizeof(buf), f))
218 {
219 if ((p = strchr(buf, '\n')))
220 *p = '\0';
221
222 if (i6 < FDNS_MAX)
223 {
224 if (inet_pton(AF_INET6, buf, &addr6) > 0)
225 {
226 memcpy(&servers6[i6++], &addr6, sizeof(struct in6_addr));
227 continue;
228 }
229 }
230
231 if (i4 < FDNS_MAX)
232 {
233 if (inet_pton(AF_INET, buf, &addr4) > 0)
234 memcpy(&servers4[i4++], &addr4, sizeof(struct in_addr));
235 }
236 }
237 }
238
239 fclose(f);
240 }
241
242 /* immediate A query */
243 struct in_addr *
244 firedns_resolveip4(const char *const name)
245 {
246 static struct in_addr addr;
247
248 if (inet_pton(AF_INET, name, &addr) > 0)
249 return &addr;
250
251 return (struct in_addr *)firedns_resolveip(FDNS_QRY_A, name);
252 }
253
254 /* immediate AAAA query */
255 struct in6_addr *
256 firedns_resolveip6(const char * const name)
257 {
258 static struct in6_addr addr;
259
260 if (inet_pton(AF_INET6, name, &addr) > 0)
261 return &addr;
262
263 return (struct in6_addr *)firedns_resolveip(FDNS_QRY_AAAA, name);
264 }
265
266 /* resolve a query of a given type */
267 char *
268 firedns_resolveip(int type, const char *const name)
269 {
270 struct firedns_result *result;
271 struct timeval tv;
272 fd_set s;
273
274 for (int t = 0; t < FIREDNS_TRIES; ++t)
275 {
276 int fd = firedns_getip(type, name, NULL);
277
278 if (fd == -1)
279 return NULL;
280
281 tv.tv_sec = 5;
282 tv.tv_usec = 0;
283 FD_ZERO(&s);
284 FD_SET(fd, &s);
285 select(fd + 1, &s, NULL, NULL, &tv);
286
287 result = firedns_getresult(fd);
288
289 if (firedns_errno == FDNS_ERR_NONE)
290 /*
291 * Return is from static memory in getresult, so there is no need to
292 * copy it until the next call to firedns.
293 */
294 return result->text;
295 else if (firedns_errno == FDNS_ERR_NXDOMAIN)
296 return NULL;
297 }
298
299 if (firedns_errno == FDNS_ERR_NONE)
300 firedns_errno = FDNS_ERR_TIMEOUT;
301
302 return NULL;
303 }
304
305 /*
306 * build, add and send specified query; retrieve result with
307 * firedns_getresult()
308 */
309 int
310 firedns_getip(int type, const char * const name, void *info)
311 {
312 struct s_connection *s;
313 node_t *node;
314 int fd;
315
316 s = firedns_add_query();
317 s->class = 1;
318 s->type = type;
319 s->info = info;
320 strlcpy(s->lookup, name, sizeof(s->lookup));
321
322 if (firedns_fdinuse >= OptionsItem->dns_fdlimit)
323 {
324 firedns_errno = FDNS_ERR_FDLIMIT;
325
326 /* Don't add to queue if there is no info */
327 if (info == NULL)
328 xfree(s);
329 else
330 {
331 node = node_create(s);
332 list_add(CONNECTIONS, node);
333 }
334
335 return -1;
336 }
337
338 fd = firedns_doquery(s);
339
340 if (fd == -1)
341 {
342 xfree(s);
343 return -1;
344 }
345
346 node = node_create(s);
347 list_add(CONNECTIONS, node);
348
349 return fd;
350 }
351
352 /* build DNS query, add to list */
353 static struct s_connection *
354 firedns_add_query(void)
355 {
356 struct s_connection *s;
357
358 /* create new connection object */
359 s = xcalloc(sizeof *s);
360
361 /* verified by firedns_getresult() */
362 s->id[0] = rand() % 255;
363 s->id[1] = rand() % 255;
364 s->fd = -1;
365
366 return s;
367 }
368
369 static int
370 firedns_doquery(struct s_connection *s)
371 {
372 int len;
373 struct s_header h;
374
375 len = firedns_build_query_payload(s->lookup, s->type, 1, (unsigned char *)&h.payload);
376
377 if (len == -1)
378 {
379 firedns_errno = FDNS_ERR_FORMAT;
380 return -1;
381 }
382
383 return firedns_send_requests(&h, s, len);
384 }
385
386 /*
387 * populate payload with query: name= question, rr= record type
388 */
389 static int
390 firedns_build_query_payload(const char *const name, uint16_t rr, uint16_t class,
391 unsigned char *payload)
392 {
393 int16_t payloadpos = 0;
394 const char *tempchr, *tempchr2;
395 uint16_t l;
396
397 tempchr2 = name;
398
399 /* split name up into labels, create query */
400 while ((tempchr = strchr(tempchr2, '.')))
401 {
402 l = tempchr - tempchr2;
403
404 if (payloadpos + l + 1 > 507)
405 return -1;
406
407 payload[payloadpos++] = l;
408 memcpy(&payload[payloadpos], tempchr2, l);
409 payloadpos += l;
410 tempchr2 = &tempchr[1];
411 }
412
413 l = strlen(tempchr2);
414
415 if (l)
416 {
417 if (payloadpos + l + 2 > 507)
418 return -1;
419
420 payload[payloadpos++] = l;
421 memcpy(&payload[payloadpos], tempchr2, l);
422 payloadpos += l;
423 payload[payloadpos++] = '\0';
424 }
425
426 if (payloadpos > 508)
427 return -1;
428
429 l = htons(rr);
430 memcpy(&payload[payloadpos], &l, 2);
431
432 l = htons(class);
433 memcpy(&payload[payloadpos + 2], &l, 2);
434
435 return payloadpos + 4;
436 }
437
438 /* send DNS query */
439 static int
440 firedns_send_requests(struct s_header *h, struct s_connection *s, int l)
441 {
442 int i, sent_ok = 0;
443 struct sockaddr_in addr4;
444 struct sockaddr_in6 addr6;
445
446 /* set header flags */
447 h->flags1 = 0 | FLAGS1_MASK_RD;
448 h->flags2 = 0;
449 h->qdcount = htons(1);
450 h->ancount = htons(0);
451 h->nscount = htons(0);
452 h->arcount = htons(0);
453 memcpy(h->id, s->id, 2);
454
455 /* try to create ipv6 or ipv4 socket */
456 s->v6 = 0;
457
458 if (i6 > 0)
459 {
460 s->fd = socket(PF_INET6, SOCK_DGRAM, 0);
461
462 if (s->fd != -1)
463 {
464 if (fcntl(s->fd, F_SETFL, O_NONBLOCK))
465 {
466 close(s->fd);
467 s->fd = -1;
468 }
469 }
470
471 if (s->fd != -1)
472 {
473 struct sockaddr_in6 addr6;
474
475 memset(&addr6, 0, sizeof(addr6));
476 addr6.sin6_family = AF_INET6;
477
478 if (bind(s->fd, (struct sockaddr *)&addr6, sizeof(addr6)) == 0)
479 s->v6 = 1;
480 else
481 close(s->fd);
482 }
483 }
484
485 if (s->v6 == 0)
486 {
487 s->fd = socket(PF_INET, SOCK_DGRAM, 0);
488
489 if (s->fd != -1)
490 {
491 if (fcntl(s->fd, F_SETFL, O_NONBLOCK))
492 {
493 close(s->fd);
494 s->fd = -1;
495 }
496 }
497
498 if (s->fd != -1)
499 {
500 struct sockaddr_in addr;
501
502 memset(&addr, 0, sizeof(addr));
503 addr.sin_family = AF_INET;
504 addr.sin_port = 0;
505 addr.sin_addr.s_addr = INADDR_ANY;
506
507 if (bind(s->fd, (struct sockaddr *)&addr, sizeof(addr)) != 0)
508 {
509 close(s->fd);
510 s->fd = -1;
511 }
512 }
513
514 if (s->fd == -1)
515 {
516 firedns_errno = FDNS_ERR_NETWORK;
517 return -1;
518 }
519 }
520
521 /* if we've got ipv6 support, an ip v6 socket, and ipv6 servers, send to them */
522 if (i6 > 0 && s->v6 == 1)
523 {
524 for (i = 0; i < i6; i++)
525 {
526 memset(&addr6, 0, sizeof(addr6));
527 memcpy(&addr6.sin6_addr, &servers6[i], sizeof(addr6.sin6_addr));
528
529 addr6.sin6_family = AF_INET6;
530 addr6.sin6_port = htons(FDNS_PORT);
531
532 if (sendto(s->fd, h, l + 12, 0, (struct sockaddr *)&addr6, sizeof(addr6)) > 0)
533 sent_ok = 1;
534 }
535 }
536
537 for (i = 0; i < i4; i++)
538 {
539 /* send via ipv4-over-ipv6 if we've got an ipv6 socket */
540 if (s->v6 == 1)
541 {
542 memset(&addr6, 0, sizeof(addr6));
543 memcpy(addr6.sin6_addr.s6_addr, "\0\0\0\0\0\0\0\0\0\0\xff\xff", 12);
544 memcpy(&addr6.sin6_addr.s6_addr[12], &servers4[i].s_addr, 4);
545 addr6.sin6_family = AF_INET6;
546 addr6.sin6_port = htons(FDNS_PORT);
547
548 if (sendto(s->fd, h, l + 12, 0, (struct sockaddr *)&addr6, sizeof(addr6)) > 0)
549 sent_ok = 1;
550
551 continue;
552 }
553
554 /* otherwise send via standard ipv4 boringness */
555 memset(&addr4, 0, sizeof(addr4));
556 memcpy(&addr4.sin_addr, &servers4[i], sizeof(addr4.sin_addr));
557 addr4.sin_family = AF_INET;
558 addr4.sin_port = htons(FDNS_PORT);
559
560 if (sendto(s->fd, h, l + 12, 0, (struct sockaddr *)&addr4, sizeof(addr4)) > 0)
561 sent_ok = 1;
562 }
563
564 if (!sent_ok)
565 {
566 close(s->fd);
567 s->fd = -1;
568 firedns_errno = FDNS_ERR_NETWORK;
569 return -1;
570 }
571
572 time(&s->start);
573 firedns_fdinuse++;
574 firedns_errno = FDNS_ERR_NONE;
575
576 return s->fd;
577 }
578
579 /* retrieve result of DNS query */
580 struct firedns_result *
581 firedns_getresult(const int fd)
582 {
583 static struct firedns_result result;
584 struct s_header h;
585 struct s_connection *c;
586 node_t *node;
587 int l, i, q, curanswer;
588 struct s_rr_middle *rr, rrbacking;
589 char *src, *dst;
590 int bytes;
591
592 firedns_errno = FDNS_ERR_OTHER;
593 result.info = NULL;
594
595 memset(result.text, 0, sizeof(result.text));
596
597 /* Find query in list of dns lookups */
598 LIST_FOREACH(node, CONNECTIONS->head)
599 {
600 c = node->data;
601
602 if (c->fd == fd)
603 break;
604 else
605 c = NULL;
606 }
607
608 /* query not found */
609 if (c == NULL)
610 return &result;
611
612 /* query found -- we remove in cleanup */
613 l = recv(c->fd, &h,sizeof(struct s_header), 0);
614 result.info = c->info;
615 strlcpy(result.lookup, c->lookup, sizeof(result.lookup));
616
617 if (l == -1)
618 {
619 firedns_errno = FDNS_ERR_NETWORK;
620 goto cleanup;
621 }
622
623 if (l < 12)
624 goto cleanup;
625
626 if (c->id[0] != h.id[0] || c->id[1] != h.id[1])
627 /*
628 * ID mismatch: we keep the connection, as this could be an answer to
629 * a previous lookup..
630 */
631 return NULL;
632
633 if ((h.flags1 & FLAGS1_MASK_QR) == 0)
634 goto cleanup;
635
636 if ((h.flags1 & FLAGS1_MASK_OPCODE) != 0)
637 goto cleanup;
638
639 if ((h.flags2 & FLAGS2_MASK_RCODE) != 0)
640 {
641 firedns_errno = (h.flags2 & FLAGS2_MASK_RCODE);
642 goto cleanup;
643 }
644
645 h.ancount = ntohs(h.ancount);
646
647 if (h.ancount < 1)
648 {
649 firedns_errno = FDNS_ERR_NXDOMAIN;
650 /* no sense going on if we don't have any answers */
651 goto cleanup;
652 }
653
654 /* skip queries */
655 i = 0;
656 q = 0;
657 l -= 12;
658 h.qdcount = ntohs(h.qdcount);
659
660 while (q < h.qdcount && i < l)
661 {
662 if (h.payload[i] > 63)
663 {
664 /* pointer */
665 i += 6; /* skip pointer, class and type */
666 q++;
667 }
668 else
669 {
670 /* label */
671 if (h.payload[i] == 0)
672 {
673 q++;
674 i += 5; /* skip nil, class and type */
675 }
676 else
677 i += h.payload[i] + 1; /* skip length and label */
678 }
679 }
680
681 /* &h.payload[i] should now be the start of the first response */
682 curanswer = 0;
683
684 while (curanswer < h.ancount)
685 {
686 q = 0;
687
688 while (q == 0 && i < l)
689 {
690 if (h.payload[i] > 63)
691 {
692 /* pointer */
693 i += 2; /* skip pointer */
694 q = 1;
695 }
696 else
697 {
698 /* label */
699 if (h.payload[i] == 0)
700 {
701 i++;
702 q = 1;
703 }
704 else
705 i += h.payload[i] + 1; /* skip length and label */
706 }
707 }
708
709 if (l - i < 10)
710 goto cleanup;
711
712 rr = (struct s_rr_middle *)&h.payload[i];
713 src = (char *)rr;
714 dst = (char *)&rrbacking;
715
716 for (bytes = sizeof(rrbacking); bytes; bytes--)
717 *dst++ = *src++;
718
719 rr = &rrbacking;
720 i += 10;
721 rr->rdlength = ntohs(rr->rdlength);
722
723 if (ntohs(rr->type) != c->type)
724 {
725 curanswer++;
726 i += rr->rdlength;
727 continue;
728 }
729
730 if (ntohs(rr->class) != c->class)
731 {
732 curanswer++;
733 i += rr->rdlength;
734 continue;
735 }
736
737 break;
738 }
739
740 if (curanswer == h.ancount)
741 goto cleanup;
742 if (i + rr->rdlength > l)
743 goto cleanup;
744 if (rr->rdlength > 1023)
745 goto cleanup;
746
747 firedns_errno = FDNS_ERR_NONE;
748 memcpy(result.text, &h.payload[i], rr->rdlength);
749 result.text[rr->rdlength] = '\0';
750
751 /* Clean-up */
752 cleanup:
753 list_remove(CONNECTIONS, node);
754 node_free(node);
755 close(c->fd);
756 firedns_fdinuse--;
757 xfree(c);
758
759 return &result;
760 }
761
762 void
763 firedns_cycle(void)
764 {
765 node_t *node, *node_next;
766 struct s_connection *p;
767 struct firedns_result *res, new_result;
768 static struct pollfd *ufds = NULL;
769 int fd;
770 unsigned int size, i;
771 time_t timenow;
772
773 if (LIST_SIZE(CONNECTIONS) == 0)
774 return;
775
776 if (ufds == NULL)
777 ufds = xcalloc((sizeof *ufds) * OptionsItem->dns_fdlimit);
778
779 time(&timenow);
780 size = 0;
781
782 LIST_FOREACH_SAFE(node, node_next, CONNECTIONS->head)
783 {
784 if (size >= OptionsItem->dns_fdlimit)
785 break;
786
787 p = node->data;
788
789 if (p->fd < 0)
790 continue;
791
792 if (p->fd > 0 && (p->start + FDNS_TIMEOUT) < timenow)
793 {
794 /* Timed out - remove from list */
795 list_remove(CONNECTIONS, node);
796 node_free(node);
797
798 memset(new_result.text, 0, sizeof(new_result.text));
799 new_result.info = p->info;
800 strlcpy(new_result.lookup, p->lookup, sizeof(new_result.lookup));
801
802 close(p->fd);
803 firedns_fdinuse--;
804 xfree(p);
805
806 firedns_errno = FDNS_ERR_TIMEOUT;
807
808 if (new_result.info)
809 dnsbl_result(&new_result);
810
811 continue;
812 }
813
814 ufds[size].events = 0;
815 ufds[size].revents = 0;
816 ufds[size].fd = p->fd;
817 ufds[size].events = POLLIN;
818
819 ++size;
820 }
821
822 switch (poll(ufds, size, 0))
823 {
824 case -1:
825 case 0:
826 return;
827 }
828
829 LIST_FOREACH_SAFE(node, node_next, CONNECTIONS->head)
830 {
831 p = node->data;
832
833 if (p->fd > 0)
834 {
835 for (i = 0; i < size; ++i)
836 {
837 if ((ufds[i].revents & POLLIN) && ufds[i].fd == p->fd)
838 {
839 fd = p->fd;
840 res = firedns_getresult(fd);
841
842 if (res && res->info)
843 dnsbl_result(res);
844
845 break;
846 }
847 }
848 }
849 else if (firedns_fdinuse < OptionsItem->dns_fdlimit)
850 firedns_doquery(p);
851 }
852 }
853
854 const char *
855 firedns_strerror(int error)
856 {
857 if (error == FDNS_ERR_NETWORK)
858 return strerror(errno);
859
860 return errors[error];
861 }

Properties

Name Value
svn:eol-style native
svn:keywords Id