/[svn]/hopm/branches/1.0.x/src/firedns.c
ViewVC logotype

Contents of /hopm/branches/1.0.x/src/firedns.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 5953 - (show annotations)
Fri May 8 20:47:29 2015 UTC (7 years, 3 months ago) by michael
File MIME type: text/x-chdr
File size: 17411 byte(s)
- firedns.c: replace PF_INET(6) with AF_INET(6)

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(servers4));
171 memset(servers6, 0, sizeof(servers6));
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(AF_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 memset(&addr6, 0, sizeof(addr6));
474 addr6.sin6_family = AF_INET6;
475
476 if (bind(s->fd, (struct sockaddr *)&addr6, sizeof(addr6)) == 0)
477 s->v6 = 1;
478 else
479 close(s->fd);
480 }
481 }
482
483 if (s->v6 == 0)
484 {
485 s->fd = socket(AF_INET, SOCK_DGRAM, 0);
486
487 if (s->fd != -1)
488 {
489 if (fcntl(s->fd, F_SETFL, O_NONBLOCK))
490 {
491 close(s->fd);
492 s->fd = -1;
493 }
494 }
495
496 if (s->fd != -1)
497 {
498 memset(&addr4, 0, sizeof(addr4));
499 addr4.sin_family = AF_INET;
500 addr4.sin_port = 0;
501 addr4.sin_addr.s_addr = INADDR_ANY;
502
503 if (bind(s->fd, (struct sockaddr *)&addr4, sizeof(addr4)) != 0)
504 {
505 close(s->fd);
506 s->fd = -1;
507 }
508 }
509
510 if (s->fd == -1)
511 {
512 firedns_errno = FDNS_ERR_NETWORK;
513 return -1;
514 }
515 }
516
517 /* if we've got ipv6 support, an ip v6 socket, and ipv6 servers, send to them */
518 if (i6 > 0 && s->v6 == 1)
519 {
520 for (i = 0; i < i6; i++)
521 {
522 memset(&addr6, 0, sizeof(addr6));
523 memcpy(&addr6.sin6_addr, &servers6[i], sizeof(addr6.sin6_addr));
524
525 addr6.sin6_family = AF_INET6;
526 addr6.sin6_port = htons(FDNS_PORT);
527
528 if (sendto(s->fd, h, l + 12, 0, (struct sockaddr *)&addr6, sizeof(addr6)) > 0)
529 sent_ok = 1;
530 }
531 }
532
533 for (i = 0; i < i4; i++)
534 {
535 /* send via ipv4-over-ipv6 if we've got an ipv6 socket */
536 if (s->v6 == 1)
537 {
538 memset(&addr6, 0, sizeof(addr6));
539 memcpy(addr6.sin6_addr.s6_addr, "\0\0\0\0\0\0\0\0\0\0\xff\xff", 12);
540 memcpy(&addr6.sin6_addr.s6_addr[12], &servers4[i].s_addr, 4);
541 addr6.sin6_family = AF_INET6;
542 addr6.sin6_port = htons(FDNS_PORT);
543
544 if (sendto(s->fd, h, l + 12, 0, (struct sockaddr *)&addr6, sizeof(addr6)) > 0)
545 sent_ok = 1;
546
547 continue;
548 }
549
550 /* otherwise send via standard ipv4 boringness */
551 memset(&addr4, 0, sizeof(addr4));
552 memcpy(&addr4.sin_addr, &servers4[i], sizeof(addr4.sin_addr));
553 addr4.sin_family = AF_INET;
554 addr4.sin_port = htons(FDNS_PORT);
555
556 if (sendto(s->fd, h, l + 12, 0, (struct sockaddr *)&addr4, sizeof(addr4)) > 0)
557 sent_ok = 1;
558 }
559
560 if (!sent_ok)
561 {
562 close(s->fd);
563 s->fd = -1;
564 firedns_errno = FDNS_ERR_NETWORK;
565 return -1;
566 }
567
568 time(&s->start);
569 firedns_fdinuse++;
570 firedns_errno = FDNS_ERR_NONE;
571
572 return s->fd;
573 }
574
575 /* retrieve result of DNS query */
576 struct firedns_result *
577 firedns_getresult(const int fd)
578 {
579 static struct firedns_result result;
580 struct s_header h;
581 struct s_connection *c;
582 node_t *node;
583 int l, i, q, curanswer;
584 struct s_rr_middle *rr, rrbacking;
585 char *src, *dst;
586 int bytes;
587
588 firedns_errno = FDNS_ERR_OTHER;
589 result.info = NULL;
590
591 memset(result.text, 0, sizeof(result.text));
592
593 /* Find query in list of dns lookups */
594 LIST_FOREACH(node, CONNECTIONS->head)
595 {
596 c = node->data;
597
598 if (c->fd == fd)
599 break;
600 else
601 c = NULL;
602 }
603
604 /* query not found */
605 if (c == NULL)
606 return &result;
607
608 /* query found -- we remove in cleanup */
609 l = recv(c->fd, &h,sizeof(struct s_header), 0);
610 result.info = c->info;
611 strlcpy(result.lookup, c->lookup, sizeof(result.lookup));
612
613 if (l == -1)
614 {
615 firedns_errno = FDNS_ERR_NETWORK;
616 goto cleanup;
617 }
618
619 if (l < 12)
620 goto cleanup;
621
622 if (c->id[0] != h.id[0] || c->id[1] != h.id[1])
623 /*
624 * ID mismatch: we keep the connection, as this could be an answer to
625 * a previous lookup..
626 */
627 return NULL;
628
629 if ((h.flags1 & FLAGS1_MASK_QR) == 0)
630 goto cleanup;
631
632 if ((h.flags1 & FLAGS1_MASK_OPCODE) != 0)
633 goto cleanup;
634
635 if ((h.flags2 & FLAGS2_MASK_RCODE) != 0)
636 {
637 firedns_errno = (h.flags2 & FLAGS2_MASK_RCODE);
638 goto cleanup;
639 }
640
641 h.ancount = ntohs(h.ancount);
642
643 if (h.ancount < 1)
644 {
645 firedns_errno = FDNS_ERR_NXDOMAIN;
646 /* no sense going on if we don't have any answers */
647 goto cleanup;
648 }
649
650 /* skip queries */
651 i = 0;
652 q = 0;
653 l -= 12;
654 h.qdcount = ntohs(h.qdcount);
655
656 while (q < h.qdcount && i < l)
657 {
658 if (h.payload[i] > 63)
659 {
660 /* pointer */
661 i += 6; /* skip pointer, class and type */
662 q++;
663 }
664 else
665 {
666 /* label */
667 if (h.payload[i] == 0)
668 {
669 q++;
670 i += 5; /* skip nil, class and type */
671 }
672 else
673 i += h.payload[i] + 1; /* skip length and label */
674 }
675 }
676
677 /* &h.payload[i] should now be the start of the first response */
678 curanswer = 0;
679
680 while (curanswer < h.ancount)
681 {
682 q = 0;
683
684 while (q == 0 && i < l)
685 {
686 if (h.payload[i] > 63)
687 {
688 /* pointer */
689 i += 2; /* skip pointer */
690 q = 1;
691 }
692 else
693 {
694 /* label */
695 if (h.payload[i] == 0)
696 {
697 i++;
698 q = 1;
699 }
700 else
701 i += h.payload[i] + 1; /* skip length and label */
702 }
703 }
704
705 if (l - i < 10)
706 goto cleanup;
707
708 rr = (struct s_rr_middle *)&h.payload[i];
709 src = (char *)rr;
710 dst = (char *)&rrbacking;
711
712 for (bytes = sizeof(rrbacking); bytes; bytes--)
713 *dst++ = *src++;
714
715 rr = &rrbacking;
716 i += 10;
717 rr->rdlength = ntohs(rr->rdlength);
718
719 if (ntohs(rr->type) != c->type)
720 {
721 curanswer++;
722 i += rr->rdlength;
723 continue;
724 }
725
726 if (ntohs(rr->class) != c->class)
727 {
728 curanswer++;
729 i += rr->rdlength;
730 continue;
731 }
732
733 break;
734 }
735
736 if (curanswer == h.ancount)
737 goto cleanup;
738 if (i + rr->rdlength > l)
739 goto cleanup;
740 if (rr->rdlength > 1023)
741 goto cleanup;
742
743 firedns_errno = FDNS_ERR_NONE;
744 memcpy(result.text, &h.payload[i], rr->rdlength);
745 result.text[rr->rdlength] = '\0';
746
747 /* Clean-up */
748 cleanup:
749 list_remove(CONNECTIONS, node);
750 node_free(node);
751 close(c->fd);
752 firedns_fdinuse--;
753 xfree(c);
754
755 return &result;
756 }
757
758 void
759 firedns_cycle(void)
760 {
761 node_t *node, *node_next;
762 struct s_connection *p;
763 struct firedns_result *res, new_result;
764 static struct pollfd *ufds = NULL;
765 int fd;
766 unsigned int size, i;
767 time_t timenow;
768
769 if (LIST_SIZE(CONNECTIONS) == 0)
770 return;
771
772 if (ufds == NULL)
773 ufds = xcalloc((sizeof *ufds) * OptionsItem->dns_fdlimit);
774
775 time(&timenow);
776 size = 0;
777
778 LIST_FOREACH_SAFE(node, node_next, CONNECTIONS->head)
779 {
780 if (size >= OptionsItem->dns_fdlimit)
781 break;
782
783 p = node->data;
784
785 if (p->fd < 0)
786 continue;
787
788 if (p->fd > 0 && (p->start + FDNS_TIMEOUT) < timenow)
789 {
790 /* Timed out - remove from list */
791 list_remove(CONNECTIONS, node);
792 node_free(node);
793
794 memset(new_result.text, 0, sizeof(new_result.text));
795 new_result.info = p->info;
796 strlcpy(new_result.lookup, p->lookup, sizeof(new_result.lookup));
797
798 close(p->fd);
799 firedns_fdinuse--;
800 xfree(p);
801
802 firedns_errno = FDNS_ERR_TIMEOUT;
803
804 if (new_result.info)
805 dnsbl_result(&new_result);
806
807 continue;
808 }
809
810 ufds[size].events = 0;
811 ufds[size].revents = 0;
812 ufds[size].fd = p->fd;
813 ufds[size].events = POLLIN;
814
815 ++size;
816 }
817
818 switch (poll(ufds, size, 0))
819 {
820 case -1:
821 case 0:
822 return;
823 }
824
825 LIST_FOREACH_SAFE(node, node_next, CONNECTIONS->head)
826 {
827 p = node->data;
828
829 if (p->fd > 0)
830 {
831 for (i = 0; i < size; ++i)
832 {
833 if ((ufds[i].revents & POLLIN) && ufds[i].fd == p->fd)
834 {
835 fd = p->fd;
836 res = firedns_getresult(fd);
837
838 if (res && res->info)
839 dnsbl_result(res);
840
841 break;
842 }
843 }
844 }
845 else if (firedns_fdinuse < OptionsItem->dns_fdlimit)
846 firedns_doquery(p);
847 }
848 }
849
850 const char *
851 firedns_strerror(int error)
852 {
853 if (error == FDNS_ERR_NETWORK)
854 return strerror(errno);
855
856 return errors[error];
857 }

Properties

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

svnadmin@ircd-hybrid.org
ViewVC Help
Powered by ViewVC 1.1.28