/[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 5186 - (show annotations)
Mon Dec 29 13:49:50 2014 UTC (5 years, 8 months ago) by michael
File MIME type: text/x-chdr
File size: 17581 byte(s)
- firedns.c:firedns_init(): unlike inet_aton(), inet_pton(), which we use now, does not
  forgive any unknown trailing characters such as \n

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

Properties

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

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