/[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 5174 - (show annotations)
Fri Dec 26 21:08:42 2014 UTC (4 years, 7 months ago) by michael
File MIME type: text/x-chdr
File size: 18895 byte(s)
- Removed now unused inet.c and inet.h

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

Properties

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

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