ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/hopm/releases/1.0.0beta1/src/firedns.c
Revision: 5052
Committed: Mon Dec 22 11:56:03 2014 UTC (11 years, 7 months ago) by michael
Content type: text/x-csrc
Original Path: hopm/trunk/src/firedns.c
File size: 19121 byte(s)
Log Message:
- Initial import of bopm 3.1.3

File Contents

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