ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/hopm/trunk/src/firedns.c
Revision: 5097
Committed: Tue Dec 23 20:57:35 2014 UTC (9 years, 3 months ago) by michael
Content type: text/x-csrc
File size: 18998 byte(s)
Log Message:
- firedns.c: replaced strncpy() with strlcpy()

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 <sys/poll.h>
30 #include <sys/time.h>
31 #include <netinet/in.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <stdio.h>
35 #include <errno.h>
36 #include <fcntl.h>
37
38 #include "compat.h"
39 #include "inet.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 unsigned short class;
101 unsigned short 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 unsigned short type;
115 unsigned short class;
116 /* XXX - firedns depends on this being 4 bytes */
117 uint32 ttl;
118 unsigned short 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 unsigned short qdcount;
139 unsigned short ancount;
140 unsigned short nscount;
141 unsigned short 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 unsigned short rr, unsigned short class, 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_pton6(&buf[i], (char *)&addr6) != NULL)
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_aton(&buf[i], &addr4))
217 {
218 memcpy(&servers4[i4++],&addr4,sizeof(struct in_addr));
219 }
220 }
221 }
222 }
223 }
224 else
225 {
226 file = FDNS_CONFIG_PREF;
227 while (fgets(buf,1024,f) != NULL)
228 {
229 buf[strspn(buf, "0123456789.")] = '\0';
230 #ifdef IPV6
231 if (i6 < FDNS_MAX)
232 {
233 if (inet_pton(AF_INET6, buf, (char *)&addr6))
234 {
235 memcpy(&servers6[i6++], &addr6, sizeof(struct in6_addr));
236 continue;
237 }
238 }
239 #endif
240 if (i4 < FDNS_MAX)
241 {
242 if (inet_pton(AF_INET, buf, (char *)&addr4))
243 memcpy(&servers4[i4++],&addr4,sizeof(struct in_addr));
244 }
245 }
246 }
247 fclose(f);
248
249 if(i4 == 0
250 #ifdef IPV6 /* (yuck) */
251 && i6
252 #endif
253 )
254 {
255 log_printf("FIREDNS -> No nameservers found in %s", file);
256 exit(EXIT_FAILURE);
257 }
258 }
259
260 struct in_addr *firedns_resolveip4(const char * const name)
261 { /* immediate A query */
262 static struct in_addr addr;
263
264 if(inet_aton(name, &addr))
265 return &addr;
266
267 return (struct in_addr *) firedns_resolveip(FDNS_QRY_A, name);
268 }
269
270 struct in6_addr *firedns_resolveip6(const char * const name)
271 { /* immediate AAAA query */
272 return (struct in6_addr *) firedns_resolveip(FDNS_QRY_AAAA, name);
273 }
274
275 char *firedns_resolveip(int type, const char * const name)
276 { /* resolve a query of a given type */
277 int fd, t;
278 struct firedns_result *result;
279 struct timeval tv;
280 fd_set s;
281
282 for (t = 0; t < FIREDNS_TRIES; t++)
283 {
284 fd = firedns_getip(type, name, NULL);
285 if (fd == -1)
286 return NULL;
287
288 tv.tv_sec = 5;
289 tv.tv_usec = 0;
290 FD_ZERO(&s);
291 FD_SET(fd, &s);
292 select(fd + 1, &s, NULL, NULL, &tv);
293
294 result = firedns_getresult(fd);
295
296 if (fdns_errno == FDNS_ERR_NONE)
297 /* Return is from static memory in getresult, so there is no need to
298 copy it until the next call to firedns. */
299 return result->text;
300 else if(fdns_errno == FDNS_ERR_NXDOMAIN)
301 return NULL;
302 }
303 if(fdns_errno == FDNS_ERR_NONE)
304 fdns_errno = FDNS_ERR_TIMEOUT;
305 return NULL;
306 }
307
308 /*
309 * build, add and send specified query; retrieve result with
310 * firedns_getresult()
311 */
312 int firedns_getip(int type, const char * const name, void *info)
313 {
314 struct s_connection *s;
315 node_t *node;
316 int fd;
317
318 s = firedns_add_query();
319
320 s->class = 1;
321 s->type = type;
322 strlcpy(s->lookup, name, sizeof(s->lookup));
323 s->info = info;
324
325 if(fdns_fdinuse >= OptionsItem->dns_fdlimit)
326 {
327 fdns_errno = FDNS_ERR_FDLIMIT;
328 /* Don't add to queue if there is no info */
329 if(info == NULL)
330 {
331 MyFree(s);
332 }else{
333 node = node_create(s);
334 list_add(CONNECTIONS, node);
335 }
336 return -1;
337 }
338
339 fd = firedns_doquery(s);
340
341 if(fd == -1)
342 {
343 MyFree(s);
344 return -1;
345 }
346
347 node = node_create(s);
348 list_add(CONNECTIONS, node);
349 return fd;
350 }
351
352 static struct s_connection *firedns_add_query(void)
353 { /* build DNS query, add to list */
354 struct s_connection *s;
355
356 /* create new connection object */
357 s = MyMalloc(sizeof *s);
358
359 /* verified by firedns_getresult() */
360 s->id[0] = rand() % 255;
361 s->id[1] = rand() % 255;
362
363 s->fd = -1;
364
365 return s;
366 }
367
368 static int firedns_doquery(struct s_connection *s)
369 {
370 int len;
371 struct s_header h;
372
373 len = firedns_build_query_payload(s->lookup, s->type, 1,
374 (unsigned char *)&h.payload);
375
376 if(len == -1)
377 {
378 fdns_errno = FDNS_ERR_FORMAT;
379 return -1;
380 }
381
382 return firedns_send_requests(&h, s, len);
383 }
384
385 /*
386 * populate payload with query: name= question, rr= record type
387 */
388 static int firedns_build_query_payload(const char * const name,
389 unsigned short rr, unsigned short class, unsigned char * payload)
390 {
391 short payloadpos;
392 const char * tempchr, * tempchr2;
393 unsigned short l;
394
395 payloadpos = 0;
396 tempchr2 = name;
397
398 /* split name up into labels, create query */
399 while ((tempchr = strchr(tempchr2,'.')) != NULL)
400 {
401 l = tempchr - tempchr2;
402 if (payloadpos + l + 1 > 507)
403 return -1;
404 payload[payloadpos++] = l;
405 memcpy(&payload[payloadpos],tempchr2,l);
406 payloadpos += l;
407 tempchr2 = &tempchr[1];
408 }
409 l = strlen(tempchr2);
410 if (l)
411 {
412 if (payloadpos + l + 2 > 507)
413 return -1;
414 payload[payloadpos++] = l;
415 memcpy(&payload[payloadpos],tempchr2,l);
416 payloadpos += l;
417 payload[payloadpos++] = '\0';
418 }
419 if (payloadpos > 508)
420 return -1;
421 l = htons(rr);
422 memcpy(&payload[payloadpos],&l,2);
423 l = htons(class);
424 memcpy(&payload[payloadpos + 2],&l,2);
425 return payloadpos + 4;
426 }
427
428 /* send DNS query */
429 static int firedns_send_requests(struct s_header *h, struct s_connection *s,
430 int l)
431 {
432 int i, sent_ok = 0;
433 struct sockaddr_in addr4;
434
435 #ifdef IPV6
436 struct sockaddr_in6 addr6;
437 #endif
438
439 /* set header flags */
440 h->flags1 = 0 | FLAGS1_MASK_RD;
441 h->flags2 = 0;
442 h->qdcount = htons(1);
443 h->ancount = htons(0);
444 h->nscount = htons(0);
445 h->arcount = htons(0);
446 memcpy(h->id, s->id, 2);
447
448 /* try to create ipv6 or ipv4 socket */
449 #ifdef IPV6
450
451 s->v6 = 0;
452 if (i6 > 0)
453 {
454 s->fd = socket(PF_INET6, SOCK_DGRAM, 0);
455 if (s->fd != -1)
456 {
457 if (fcntl(s->fd, F_SETFL, O_NONBLOCK) != 0)
458 {
459 close(s->fd);
460 s->fd = -1;
461 }
462 }
463 if (s->fd != -1)
464 {
465 struct sockaddr_in6 addr6;
466 memset(&addr6,0,sizeof(addr6));
467 addr6.sin6_family = AF_INET6;
468 if (bind(s->fd,(struct sockaddr *)&addr6,sizeof(addr6)) == 0)
469 s->v6 = 1;
470 else
471 {
472 close(s->fd);
473 }
474 }
475 }
476 if (s->v6 == 0)
477 {
478 #endif
479 s->fd = socket(PF_INET, SOCK_DGRAM, 0);
480 if (s->fd != -1)
481 {
482 if (fcntl(s->fd, F_SETFL, O_NONBLOCK) != 0)
483 {
484 close(s->fd);
485 s->fd = -1;
486 }
487 }
488 if (s->fd != -1)
489 {
490 struct sockaddr_in addr;
491 memset(&addr,0,sizeof(addr));
492 addr.sin_family = AF_INET;
493 addr.sin_port = 0;
494 addr.sin_addr.s_addr = INADDR_ANY;
495 if (bind(s->fd,(struct sockaddr *)&addr,sizeof(addr)) != 0)
496 {
497 close(s->fd);
498 s->fd = -1;
499 }
500 }
501 if (s->fd == -1)
502 {
503 fdns_errno = FDNS_ERR_NETWORK;
504 return -1;
505 }
506 #ifdef IPV6
507
508 }
509 #endif
510
511
512 #ifdef IPV6
513 /* if we've got ipv6 support, an ip v6 socket, and ipv6 servers, send to them */
514 if (i6 > 0 && s->v6 == 1)
515 {
516 for (i = 0; i < i6; i++)
517 {
518 memset(&addr6,0,sizeof(addr6));
519 memcpy(&addr6.sin6_addr,&servers6[i],sizeof(addr6.sin6_addr));
520 addr6.sin6_family = AF_INET6;
521 addr6.sin6_port = htons(FDNS_PORT);
522 if(sendto(s->fd, h, l + 12, 0, (struct sockaddr *) &addr6, sizeof(addr6)) > 0)
523 sent_ok = 1;
524 }
525 }
526 #endif
527
528 for (i = 0; i < i4; i++)
529 {
530 #ifdef IPV6
531 /* send via ipv4-over-ipv6 if we've got an ipv6 socket */
532 if (s->v6 == 1)
533 {
534 memset(&addr6,0,sizeof(addr6));
535 memcpy(addr6.sin6_addr.s6_addr,"\0\0\0\0\0\0\0\0\0\0\xff\xff",12);
536 memcpy(&addr6.sin6_addr.s6_addr[12],&servers4[i].s_addr,4);
537 addr6.sin6_family = AF_INET6;
538 addr6.sin6_port = htons(FDNS_PORT);
539 if(sendto(s->fd, h, l + 12, 0, (struct sockaddr *) &addr6, sizeof(addr6)) > 0)
540 sent_ok = 1;
541 continue;
542 }
543 #endif
544 /* otherwise send via standard ipv4 boringness */
545 memset(&addr4,0,sizeof(addr4));
546 memcpy(&addr4.sin_addr,&servers4[i],sizeof(addr4.sin_addr));
547 addr4.sin_family = AF_INET;
548 addr4.sin_port = htons(FDNS_PORT);
549 if(sendto(s->fd, h, l + 12, 0, (struct sockaddr *) &addr4, sizeof(addr4)) > 0)
550 sent_ok = 1;
551 }
552
553 if(!sent_ok)
554 {
555 close(s->fd);
556 s->fd = -1;
557 fdns_errno = FDNS_ERR_NETWORK;
558 return -1;
559 }
560
561 time(&s->start);
562 fdns_fdinuse++;
563 fdns_errno = FDNS_ERR_NONE;
564 return s->fd;
565 }
566
567 struct firedns_result *firedns_getresult(const int fd)
568 { /* retrieve result of DNS query */
569 static struct firedns_result result;
570 struct s_header h;
571 struct s_connection *c;
572 node_t *node;
573 int l,i,q,curanswer;
574 struct s_rr_middle *rr, rrbacking;
575 char *src, *dst;
576 int bytes;
577
578 fdns_errno = FDNS_ERR_OTHER;
579 result.info = (void *) NULL;
580 memset(result.text, 0, sizeof(result.text));
581
582 /* Find query in list of dns lookups */
583 LIST_FOREACH(node, CONNECTIONS->head)
584 {
585 c = node->data;
586 if(c->fd == fd)
587 break;
588 else
589 c = NULL;
590 }
591
592 /* query not found */
593 if(c == NULL)
594 return &result;
595
596 /* query found -- we remove in cleanup */
597
598 l = recv(c->fd,&h,sizeof(struct s_header),0);
599 result.info = (void *) c->info;
600 strlcpy(result.lookup, c->lookup, sizeof(result.lookup));
601
602 if(l == -1)
603 {
604 fdns_errno = FDNS_ERR_NETWORK;
605 goto cleanup;
606 }
607
608 if (l < 12)
609 goto cleanup;
610 if (c->id[0] != h.id[0] || c->id[1] != h.id[1])
611 /* ID mismatch: we keep the connection, as this could be an answer to
612 a previous lookup.. */
613 return NULL;
614 if ((h.flags1 & FLAGS1_MASK_QR) == 0)
615 goto cleanup;
616 if ((h.flags1 & FLAGS1_MASK_OPCODE) != 0)
617 goto cleanup;
618 if ((h.flags2 & FLAGS2_MASK_RCODE) != 0)
619 {
620 fdns_errno = (h.flags2 & FLAGS2_MASK_RCODE);
621 goto cleanup;
622 }
623 h.ancount = ntohs(h.ancount);
624 if (h.ancount < 1) {
625 fdns_errno = FDNS_ERR_NXDOMAIN;
626 /* no sense going on if we don't have any answers */
627 goto cleanup;
628 }
629 /* skip queries */
630 i = 0;
631 q = 0;
632 l -= 12;
633 h.qdcount = ntohs(h.qdcount);
634 while (q < h.qdcount && i < l)
635 {
636 if (h.payload[i] > 63)
637 { /* pointer */
638 i += 6; /* skip pointer, class and type */
639 q++;
640 }
641 else
642 { /* label */
643 if (h.payload[i] == 0)
644 {
645 q++;
646 i += 5; /* skip nil, class and type */
647 }
648 else
649 i += h.payload[i] + 1; /* skip length and label */
650 }
651 }
652 /* &h.payload[i] should now be the start of the first response */
653 curanswer = 0;
654 while (curanswer < h.ancount)
655 {
656 q = 0;
657 while (q == 0 && i < l)
658 {
659 if (h.payload[i] > 63)
660 { /* pointer */
661 i += 2; /* skip pointer */
662 q = 1;
663 }
664 else
665 { /* label */
666 if (h.payload[i] == 0)
667 {
668 i++;
669 q = 1;
670 }
671 else
672 i += h.payload[i] + 1; /* skip length and label */
673 }
674 }
675 if (l - i < 10)
676 goto cleanup;
677 rr = (struct s_rr_middle *)&h.payload[i];
678 src = (char *) rr;
679 dst = (char *) &rrbacking;
680 for (bytes = sizeof(rrbacking); bytes; bytes--)
681 *dst++ = *src++;
682 rr = &rrbacking;
683 i += 10;
684 rr->rdlength = ntohs(rr->rdlength);
685 if (ntohs(rr->type) != c->type)
686 {
687 curanswer++;
688 i += rr->rdlength;
689 continue;
690 }
691 if (ntohs(rr->class) != c->class)
692 {
693 curanswer++;
694 i += rr->rdlength;
695 continue;
696 }
697 break;
698 }
699
700 if (curanswer == h.ancount)
701 goto cleanup;
702 if (i + rr->rdlength > l)
703 goto cleanup;
704 if (rr->rdlength > 1023)
705 goto cleanup;
706
707 fdns_errno = FDNS_ERR_NONE;
708 memcpy(result.text,&h.payload[i],rr->rdlength);
709 result.text[rr->rdlength] = '\0';
710
711 /* Clean-up */
712 cleanup:
713 list_remove(CONNECTIONS, node);
714 node_free(node);
715 close(c->fd);
716 fdns_fdinuse--;
717 MyFree(c);
718
719 return &result;
720 }
721
722 void firedns_cycle(void)
723 {
724 node_t *node, *next;
725 struct s_connection *p;
726 struct firedns_result *res, new_result;
727 static struct pollfd *ufds = NULL;
728 int fd;
729 unsigned int size, i;
730 time_t timenow;
731
732 if(LIST_SIZE(CONNECTIONS) == 0)
733 return;
734
735 if(ufds == NULL)
736 ufds = MyMalloc((sizeof *ufds) * OptionsItem->dns_fdlimit);
737
738 time(&timenow);
739 size = 0;
740
741 LIST_FOREACH_SAFE(node, next, CONNECTIONS->head)
742 {
743 if(size >= OptionsItem->dns_fdlimit)
744 break;
745
746 p = node->data;
747
748 if(p->fd < 0)
749 continue;
750
751 if(p->fd > 0 && (p->start + FDNS_TIMEOUT) < timenow)
752 {
753 /* Timed out - remove from list */
754 list_remove(CONNECTIONS, node);
755 node_free(node);
756
757 memset(new_result.text, 0, sizeof(new_result.text));
758 new_result.info = p->info;
759 strlcpy(new_result.lookup, p->lookup, sizeof(new_result.lookup));
760
761 close(p->fd);
762 fdns_fdinuse--;
763 MyFree(p);
764
765 fdns_errno = FDNS_ERR_TIMEOUT;
766
767 if(new_result.info != NULL)
768 dnsbl_result(&new_result);
769
770 continue;
771 }
772
773 ufds[size].events = 0;
774 ufds[size].revents = 0;
775 ufds[size].fd = p->fd;
776 ufds[size].events = POLLIN;
777
778 size++;
779 }
780
781
782 switch(poll(ufds, size, 0))
783 {
784 case -1:
785 case 0:
786 return;
787 }
788
789 LIST_FOREACH_SAFE(node, next, CONNECTIONS->head)
790 {
791 p = node->data;
792 if(p->fd > 0)
793 {
794 for(i = 0; i < size; i++)
795 {
796 if((ufds[i].revents & POLLIN) && ufds[i].fd == p->fd)
797 {
798 fd = p->fd;
799 res = firedns_getresult(fd);
800
801 if(res != NULL && res->info != NULL)
802 dnsbl_result(res);
803 break;
804 }
805 }
806 }
807 else if(fdns_fdinuse < OptionsItem->dns_fdlimit)
808 {
809 firedns_doquery(p);
810 }
811 }
812 }
813
814 const char *firedns_strerror(int error)
815 {
816 if(error == FDNS_ERR_NETWORK)
817 return strerror(errno);
818 return errors[error];
819 }
820