summaryrefslogtreecommitdiffstats
path: root/src/firedns.c
diff options
context:
space:
mode:
authorLibravatarUnit 193 <unit193@ubuntu.com>2019-04-07 00:14:13 -0400
committerLibravatarUnit 193 <unit193@ubuntu.com>2019-04-07 00:14:13 -0400
commit5140318f8f758141b4e350871db1fe869eb858dc (patch)
tree3cf26f4845b2c4674af81f108fbdbf47a996dcff /src/firedns.c
Import Upstream version 1.1.5upstream/1.1.5
Diffstat (limited to 'src/firedns.c')
-rw-r--r--src/firedns.c881
1 files changed, 881 insertions, 0 deletions
diff --git a/src/firedns.c b/src/firedns.c
new file mode 100644
index 0000000..bcee5da
--- /dev/null
+++ b/src/firedns.c
@@ -0,0 +1,881 @@
+/*
+firedns.c - firedns library
+Copyright (C) 2002 Ian Gulliver
+
+This file has been gutted and mucked with for use in BOPM - see the
+real library at http://ares.penguinhosting.net/~ian/ before you judge
+firedns based on this..
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of version 2 of the GNU General Public License as
+published by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include "setup.h"
+
+#include <stdlib.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <poll.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include "compat.h"
+#include "memory.h"
+#include "firedns.h"
+#include "config.h"
+#include "list.h"
+#include "log.h"
+#include "dnsbl.h"
+
+#define FIREDNS_TRIES 3
+
+int firedns_errno = FDNS_ERR_NONE;
+
+/* Variables local to this file */
+static unsigned int firedns_fdinuse;
+
+/* up to FDNS_MAX nameservers; populated by firedns_init() */
+static struct in_addr servers4[FDNS_MAX];
+static struct in6_addr servers6[FDNS_MAX];
+
+/* actual count of nameservers; set by firedns_init() */
+static unsigned int i4;
+static unsigned int i6;
+
+/*
+ * Linked list of open DNS queries; populated by firedns_add_query(),
+ * decimated by firedns_getresult()
+ */
+static list_t CONNECTIONS;
+
+/*
+ * List of errors, in order of values used in FDNS_ERR_*, returned by
+ * firedns_strerror
+ */
+static const char *const errors[] =
+{
+ [FDNS_ERR_NONE] = "Success",
+ [FDNS_ERR_FORMAT] = "Format error",
+ [FDNS_ERR_SERVFAIL] = "Server failure",
+ [FDNS_ERR_NXDOMAIN] = "Name error",
+ [FDNS_ERR_NOIMPT] = "Not implemented",
+ [FDNS_ERR_REFUSED] = "Refused",
+ [FDNS_ERR_TIMEOUT] = "Timeout",
+ [FDNS_ERR_NETWORK] = "Network error",
+ [FDNS_ERR_FDLIMIT] = "FD Limit reached",
+ [FDNS_ERR_OTHER] = "Unknown error"
+};
+
+/* Structures */
+
+/* open DNS query */
+struct s_connection
+{
+ node_t node; /**< List node; linked into CONNECTIONS */
+
+ /*
+ * unique ID (random number), matches header ID; both set by
+ * firedns_add_query()
+ */
+ unsigned char id[2];
+ uint16_t class;
+ uint16_t type;
+
+ /* file descriptor returned from sockets */
+ int fd;
+ void *info;
+ time_t start;
+ char lookup[256];
+ int v6;
+};
+
+struct s_rr_middle
+{
+ uint16_t type;
+ uint16_t class;
+
+ /* XXX - firedns depends on this being 4 bytes */
+ uint32_t ttl;
+ uint16_t rdlength;
+};
+
+/* DNS query header */
+struct s_header
+{
+ unsigned char id[2];
+ unsigned char flags1;
+#define FLAGS1_MASK_QR 0x80
+/* bitshift right 3 */
+#define FLAGS1_MASK_OPCODE 0x78
+#define FLAGS1_MASK_AA 0x04
+#define FLAGS1_MASK_TC 0x02
+#define FLAGS1_MASK_RD 0x01
+
+ unsigned char flags2;
+#define FLAGS2_MASK_RA 0x80
+#define FLAGS2_MASK_Z 0x70
+#define FLAGS2_MASK_RCODE 0x0f
+
+ uint16_t qdcount;
+ uint16_t ancount;
+ uint16_t nscount;
+ uint16_t arcount;
+
+ /* DNS question, populated by firedns_build_query_payload() */
+ unsigned char payload[512];
+};
+
+/* Function prototypes */
+static struct s_connection *firedns_add_query(void);
+static int firedns_doquery(struct s_connection *);
+static int firedns_build_query_payload(const char *const, uint16_t, uint16_t, unsigned char *);
+static int firedns_send_requests(struct s_header *, struct s_connection *, int);
+
+
+void
+firedns_init(void)
+{
+ /*
+ * populates servers4 (or -6) struct with up to FDNS_MAX nameserver IP
+ * addresses from /etc/firedns.conf (or /etc/resolv.conf)
+ */
+ FILE *f;
+ struct in_addr addr4;
+ struct in6_addr addr6;
+ char buf[1024];
+ char *p = NULL;
+
+ i6 = 0;
+ i4 = 0;
+
+ srand((unsigned int)time(NULL));
+ memset(servers4, 0, sizeof(servers4));
+ memset(servers6, 0, sizeof(servers6));
+
+ /* read etc/firedns.conf if we've got it, otherwise parse /etc/resolv.conf */
+ f = fopen(FDNS_CONFIG_PREF, "r");
+
+ if (f == NULL)
+ {
+ f = fopen(FDNS_CONFIG_FBCK, "r");
+
+ if (f == NULL)
+ {
+ log_printf("Unable to open %s", FDNS_CONFIG_FBCK);
+ return;
+ }
+
+ while (fgets(buf, sizeof(buf), f))
+ {
+ if ((p = strchr(buf, '\n')))
+ *p = '\0';
+
+ if (strncmp(buf, "nameserver", 10) == 0)
+ {
+ unsigned int i = 10;
+
+ while (buf[i] == ' ' || buf[i] == '\t')
+ ++i;
+
+ if (i6 < FDNS_MAX)
+ {
+ if (inet_pton(AF_INET6, &buf[i], &addr6) > 0)
+ {
+ memcpy(&servers6[i6++], &addr6, sizeof(struct in6_addr));
+ continue;
+ }
+ }
+
+ if (i4 < FDNS_MAX)
+ {
+ if (inet_pton(AF_INET, &buf[i], &addr4) > 0)
+ memcpy(&servers4[i4++], &addr4, sizeof(struct in_addr));
+ }
+ }
+ }
+ }
+ else
+ {
+ while (fgets(buf, sizeof(buf), f))
+ {
+ if ((p = strchr(buf, '\n')))
+ *p = '\0';
+
+ if (i6 < FDNS_MAX)
+ {
+ if (inet_pton(AF_INET6, buf, &addr6) > 0)
+ {
+ memcpy(&servers6[i6++], &addr6, sizeof(struct in6_addr));
+ continue;
+ }
+ }
+
+ if (i4 < FDNS_MAX)
+ {
+ if (inet_pton(AF_INET, buf, &addr4) > 0)
+ memcpy(&servers4[i4++], &addr4, sizeof(struct in_addr));
+ }
+ }
+ }
+
+ fclose(f);
+}
+
+/*
+ * These little hacks are here to avoid alignment and type sizing issues completely by doing manual copies
+ */
+static inline void
+firedns_fill_rr(struct s_rr_middle *restrict const rr, const unsigned char *const restrict input)
+{
+ rr->type = input[0] * 256 + input[1];
+ rr->class = input[2] * 256 + input[3];
+ rr->ttl = input[4] * 16777216 + input[5] * 65536 + input[6] * 256 + input[7];
+ rr->rdlength = input[8] * 256 + input[9];
+}
+
+static inline void
+firedns_fill_header(struct s_header *const restrict header, const unsigned char *const restrict input, const int l)
+{
+ header->id[0] = input[0];
+ header->id[1] = input[1];
+ header->flags1 = input[2];
+ header->flags2 = input[3];
+ header->qdcount = input[4] * 256 + input[5];
+ header->ancount = input[6] * 256 + input[7];
+ header->nscount = input[8] * 256 + input[9];
+ header->arcount = input[10] * 256 + input[11];
+ memcpy(header->payload, &input[12], l);
+}
+
+static inline void
+firedns_empty_header(unsigned char *const restrict output, const struct s_header *const restrict header, const int l)
+{
+ output[0] = header->id[0];
+ output[1] = header->id[1];
+ output[2] = header->flags1;
+ output[3] = header->flags2;
+ output[4] = header->qdcount / 256;
+ output[5] = header->qdcount % 256;
+ output[6] = header->ancount / 256;
+ output[7] = header->ancount % 256;
+ output[8] = header->nscount / 256;
+ output[9] = header->nscount % 256;
+ output[10] = header->arcount / 256;
+ output[11] = header->arcount % 256;
+ memcpy(&output[12], header->payload, l);
+}
+
+/* immediate A query */
+struct in_addr *
+firedns_resolveip4(const char *const name)
+{
+ static struct in_addr addr;
+
+ if (inet_pton(AF_INET, name, &addr) > 0)
+ return &addr;
+
+ return firedns_resolveip(FDNS_QRY_A, name);
+}
+
+/* immediate AAAA query */
+struct in6_addr *
+firedns_resolveip6(const char *const name)
+{
+ static struct in6_addr addr;
+
+ if (inet_pton(AF_INET6, name, &addr) > 0)
+ return &addr;
+
+ return firedns_resolveip(FDNS_QRY_AAAA, name);
+}
+
+/* resolve a query of a given type */
+void *
+firedns_resolveip(int type, const char *const name)
+{
+ struct firedns_result *result;
+ struct timeval tv;
+ fd_set s;
+
+ for (unsigned int t = 0; t < FIREDNS_TRIES; ++t)
+ {
+ int fd = firedns_getip(type, name, NULL);
+ if (fd == -1)
+ return NULL;
+
+ tv.tv_sec = 5;
+ tv.tv_usec = 0;
+ FD_ZERO(&s);
+ FD_SET(fd, &s);
+ select(fd + 1, &s, NULL, NULL, &tv);
+
+ result = firedns_getresult(fd);
+
+ if (firedns_errno == FDNS_ERR_NONE)
+ /*
+ * Return is from static memory in getresult, so there is no need to
+ * copy it until the next call to firedns.
+ */
+ return result->text;
+ else if (firedns_errno == FDNS_ERR_NXDOMAIN)
+ return NULL;
+ }
+
+ if (firedns_errno == FDNS_ERR_NONE)
+ firedns_errno = FDNS_ERR_TIMEOUT;
+
+ return NULL;
+}
+
+/*
+ * build, add and send specified query; retrieve result with
+ * firedns_getresult()
+ */
+int
+firedns_getip(int type, const char *const name, void *info)
+{
+ struct s_connection *s;
+
+ s = firedns_add_query();
+ s->class = 1;
+ s->type = type;
+ s->info = info;
+ strlcpy(s->lookup, name, sizeof(s->lookup));
+
+ if (firedns_fdinuse >= OptionsItem.dns_fdlimit)
+ {
+ firedns_errno = FDNS_ERR_FDLIMIT;
+
+ /* Don't add to queue if there is no info */
+ if (info == NULL)
+ xfree(s);
+ else
+ list_add(s, &s->node, &CONNECTIONS);
+
+ return -1;
+ }
+
+ int fd = firedns_doquery(s);
+ if (fd == -1)
+ {
+ xfree(s);
+ return -1;
+ }
+
+ list_add(s, &s->node, &CONNECTIONS);
+
+ return fd;
+}
+
+/* build DNS query, add to list */
+static struct s_connection *
+firedns_add_query(void)
+{
+ struct s_connection *s;
+
+ /* create new connection object */
+ s = xcalloc(sizeof(*s));
+
+ /* verified by firedns_getresult() */
+ s->id[0] = rand() % 255;
+ s->id[1] = rand() % 255;
+ s->fd = -1;
+
+ return s;
+}
+
+static int
+firedns_doquery(struct s_connection *s)
+{
+ struct s_header h;
+
+ int len = firedns_build_query_payload(s->lookup, s->type, 1, (unsigned char *)&h.payload);
+ if (len == -1)
+ {
+ firedns_errno = FDNS_ERR_FORMAT;
+ return -1;
+ }
+
+ return firedns_send_requests(&h, s, len);
+}
+
+/*
+ * populate payload with query: name= question, rr= record type
+ */
+static int
+firedns_build_query_payload(const char *const name, uint16_t rr, uint16_t class,
+ unsigned char *payload)
+{
+ int16_t payloadpos = 0;
+ const char *tempchr, *tempchr2;
+ uint16_t l;
+
+ tempchr2 = name;
+
+ /* split name up into labels, create query */
+ while ((tempchr = strchr(tempchr2, '.')))
+ {
+ l = tempchr - tempchr2;
+
+ if (payloadpos + l + 1 > 507)
+ return -1;
+
+ payload[payloadpos++] = l;
+ memcpy(&payload[payloadpos], tempchr2, l);
+ payloadpos += l;
+ tempchr2 = &tempchr[1];
+ }
+
+ l = strlen(tempchr2);
+
+ if (l)
+ {
+ if (payloadpos + l + 2 > 507)
+ return -1;
+
+ payload[payloadpos++] = l;
+ memcpy(&payload[payloadpos], tempchr2, l);
+ payloadpos += l;
+ payload[payloadpos++] = '\0';
+ }
+
+ if (payloadpos > 508)
+ return -1;
+
+ l = htons(rr);
+ memcpy(&payload[payloadpos], &l, 2);
+
+ l = htons(class);
+ memcpy(&payload[payloadpos + 2], &l, 2);
+
+ return payloadpos + 4;
+}
+
+/* send DNS query */
+static int
+firedns_send_requests(struct s_header *h, struct s_connection *s, int l)
+{
+ int sent_ok = 0;
+ struct sockaddr_in addr4;
+ struct sockaddr_in6 addr6;
+ unsigned char payload[sizeof(struct s_header)];
+
+ /* set header flags */
+ h->id[0] = s->id[0];
+ h->id[1] = s->id[1];
+ h->flags1 = 0 | FLAGS1_MASK_RD;
+ h->flags2 = 0;
+ h->qdcount = 1;
+ h->ancount = 0;
+ h->nscount = 0;
+ h->arcount = 0;
+
+ /* try to create ipv6 or ipv4 socket */
+ s->v6 = 0;
+
+ if (i6 > 0)
+ {
+ s->fd = socket(AF_INET6, SOCK_DGRAM, 0);
+
+ if (s->fd != -1)
+ {
+ if (fcntl(s->fd, F_SETFL, O_NONBLOCK))
+ {
+ close(s->fd);
+ s->fd = -1;
+ }
+ }
+
+ if (s->fd != -1)
+ {
+ memset(&addr6, 0, sizeof(addr6));
+ addr6.sin6_family = AF_INET6;
+
+ if (bind(s->fd, (struct sockaddr *)&addr6, sizeof(addr6)) == 0)
+ s->v6 = 1;
+ else
+ close(s->fd);
+ }
+ }
+
+ if (s->v6 == 0)
+ {
+ s->fd = socket(AF_INET, SOCK_DGRAM, 0);
+
+ if (s->fd != -1)
+ {
+ if (fcntl(s->fd, F_SETFL, O_NONBLOCK))
+ {
+ close(s->fd);
+ s->fd = -1;
+ }
+ }
+
+ if (s->fd != -1)
+ {
+ memset(&addr4, 0, sizeof(addr4));
+ addr4.sin_family = AF_INET;
+ addr4.sin_port = 0;
+ addr4.sin_addr.s_addr = INADDR_ANY;
+
+ if (bind(s->fd, (struct sockaddr *)&addr4, sizeof(addr4)) != 0)
+ {
+ close(s->fd);
+ s->fd = -1;
+ }
+ }
+
+ if (s->fd == -1)
+ {
+ firedns_errno = FDNS_ERR_NETWORK;
+ return -1;
+ }
+ }
+
+ firedns_empty_header(payload, h, l);
+
+ /* if we've got ipv6 support, an ip v6 socket, and ipv6 servers, send to them */
+ if (i6 > 0 && s->v6 == 1)
+ {
+ for (unsigned int i = 0; i < i6; ++i)
+ {
+ memset(&addr6, 0, sizeof(addr6));
+ memcpy(&addr6.sin6_addr, &servers6[i], sizeof(addr6.sin6_addr));
+
+ addr6.sin6_family = AF_INET6;
+ addr6.sin6_port = htons(FDNS_PORT);
+
+ if (sendto(s->fd, payload, l + 12, 0, (struct sockaddr *)&addr6, sizeof(addr6)) > 0)
+ sent_ok = 1;
+ }
+ }
+
+ for (unsigned int i = 0; i < i4; ++i)
+ {
+ /* send via ipv4-over-ipv6 if we've got an ipv6 socket */
+ if (s->v6 == 1)
+ {
+ memset(&addr6, 0, sizeof(addr6));
+ memcpy(addr6.sin6_addr.s6_addr, "\0\0\0\0\0\0\0\0\0\0\xff\xff", 12);
+ memcpy(&addr6.sin6_addr.s6_addr[12], &servers4[i].s_addr, 4);
+ addr6.sin6_family = AF_INET6;
+ addr6.sin6_port = htons(FDNS_PORT);
+
+ if (sendto(s->fd, payload, l + 12, 0, (struct sockaddr *)&addr6, sizeof(addr6)) > 0)
+ sent_ok = 1;
+
+ continue;
+ }
+
+ /* otherwise send via standard ipv4 boringness */
+ memset(&addr4, 0, sizeof(addr4));
+ memcpy(&addr4.sin_addr, &servers4[i], sizeof(addr4.sin_addr));
+ addr4.sin_family = AF_INET;
+ addr4.sin_port = htons(FDNS_PORT);
+
+ if (sendto(s->fd, payload, l + 12, 0, (struct sockaddr *)&addr4, sizeof(addr4)) > 0)
+ sent_ok = 1;
+ }
+
+ if (!sent_ok)
+ {
+ close(s->fd);
+ s->fd = -1;
+ firedns_errno = FDNS_ERR_NETWORK;
+ return -1;
+ }
+
+ time(&s->start);
+ firedns_fdinuse++;
+ firedns_errno = FDNS_ERR_NONE;
+
+ return s->fd;
+}
+
+/* retrieve result of DNS query */
+struct firedns_result *
+firedns_getresult(const int fd)
+{
+ static struct firedns_result result;
+ struct s_header h;
+ struct s_connection *c;
+ node_t *node;
+ int l, i, q, curanswer;
+ struct s_rr_middle rr = { .rdlength = 0 };
+ unsigned char buffer[sizeof(struct s_header)];
+
+ firedns_errno = FDNS_ERR_OTHER;
+ result.info = NULL;
+
+ memset(result.text, 0, sizeof(result.text));
+
+ /* Find query in list of dns lookups */
+ LIST_FOREACH(node, CONNECTIONS.head)
+ {
+ c = node->data;
+
+ if (c->fd == fd)
+ break;
+ else
+ c = NULL;
+ }
+
+ /* query not found */
+ if (c == NULL)
+ return &result;
+
+ /* query found -- we remove in cleanup */
+ l = recv(c->fd, buffer, sizeof(struct s_header), 0);
+
+ result.info = c->info;
+ strlcpy(result.lookup, c->lookup, sizeof(result.lookup));
+
+ if (l == -1)
+ {
+ firedns_errno = FDNS_ERR_NETWORK;
+ goto cleanup;
+ }
+
+ if (l < 12)
+ goto cleanup;
+
+ firedns_fill_header(&h, buffer, l - 12);
+
+ if (c->id[0] != h.id[0] || c->id[1] != h.id[1])
+ /*
+ * ID mismatch: we keep the connection, as this could be an answer to
+ * a previous lookup..
+ */
+ return NULL;
+
+ if ((h.flags1 & FLAGS1_MASK_QR) == 0)
+ goto cleanup;
+
+ if ((h.flags1 & FLAGS1_MASK_OPCODE) != 0)
+ goto cleanup;
+
+ if ((h.flags2 & FLAGS2_MASK_RCODE) != 0)
+ {
+ firedns_errno = (h.flags2 & FLAGS2_MASK_RCODE);
+ goto cleanup;
+ }
+
+ if (h.ancount < 1)
+ {
+ firedns_errno = FDNS_ERR_NXDOMAIN;
+ /* no sense going on if we don't have any answers */
+ goto cleanup;
+ }
+
+ /* skip queries */
+ i = 0;
+ q = 0;
+ l -= 12;
+
+ while (q < h.qdcount && i < l)
+ {
+ if (h.payload[i] > 63)
+ {
+ /* pointer */
+ i += 6; /* skip pointer, class and type */
+ q++;
+ }
+ else
+ {
+ /* label */
+ if (h.payload[i] == 0)
+ {
+ q++;
+ i += 5; /* skip nil, class and type */
+ }
+ else
+ i += h.payload[i] + 1; /* skip length and label */
+ }
+ }
+
+ /* &h.payload[i] should now be the start of the first response */
+ curanswer = 0;
+
+ while (curanswer < h.ancount)
+ {
+ q = 0;
+
+ while (q == 0 && i < l)
+ {
+ if (h.payload[i] > 63)
+ {
+ /* pointer */
+ i += 2; /* skip pointer */
+ q = 1;
+ }
+ else
+ {
+ /* label */
+ if (h.payload[i] == 0)
+ {
+ i++;
+ q = 1;
+ }
+ else
+ i += h.payload[i] + 1; /* skip length and label */
+ }
+ }
+
+ if (l - i < 10)
+ goto cleanup;
+
+ firedns_fill_rr(&rr, &h.payload[i]);
+ i += 10;
+
+ if (rr.type != c->type)
+ {
+ curanswer++;
+ i += rr.rdlength;
+ continue;
+ }
+
+ if (rr.class != c->class)
+ {
+ curanswer++;
+ i += rr.rdlength;
+ continue;
+ }
+
+ break;
+ }
+
+ if (curanswer == h.ancount)
+ goto cleanup;
+ if (i + rr.rdlength > l)
+ goto cleanup;
+ if (rr.rdlength > 1023)
+ goto cleanup;
+
+ firedns_errno = FDNS_ERR_NONE;
+ memcpy(result.text, &h.payload[i], rr.rdlength);
+ result.text[rr.rdlength] = '\0';
+
+/* Clean-up */
+cleanup:
+ list_remove(&c->node, &CONNECTIONS);
+
+ close(c->fd);
+ xfree(c);
+ firedns_fdinuse--;
+
+ return &result;
+}
+
+void
+firedns_cycle(void)
+{
+ node_t *node, *node_next;
+ struct s_connection *p;
+ struct firedns_result *res, new_result;
+ static struct pollfd *ufds = NULL;
+ unsigned int size = 0;
+ time_t timenow;
+
+ if (LIST_SIZE(&CONNECTIONS) == 0)
+ return;
+
+ if (ufds == NULL)
+ ufds = xcalloc(sizeof(*ufds) * OptionsItem.dns_fdlimit);
+
+ time(&timenow);
+
+ LIST_FOREACH_SAFE(node, node_next, CONNECTIONS.head)
+ {
+ if (size >= OptionsItem.dns_fdlimit)
+ break;
+
+ p = node->data;
+
+ if (p->fd < 0)
+ continue;
+
+ if (p->fd > 0 && (p->start + OptionsItem.dns_timeout) < timenow)
+ {
+ /* Timed out - remove from list */
+ list_remove(&p->node, &CONNECTIONS);
+
+ memset(new_result.text, 0, sizeof(new_result.text));
+ new_result.info = p->info;
+ strlcpy(new_result.lookup, p->lookup, sizeof(new_result.lookup));
+
+ close(p->fd);
+ xfree(p);
+ firedns_fdinuse--;
+
+ firedns_errno = FDNS_ERR_TIMEOUT;
+
+ if (new_result.info)
+ dnsbl_result(&new_result);
+
+ continue;
+ }
+
+ ufds[size].events = 0;
+ ufds[size].revents = 0;
+ ufds[size].fd = p->fd;
+ ufds[size].events = POLLIN;
+
+ ++size;
+ }
+
+ switch (poll(ufds, size, 0))
+ {
+ case -1:
+ case 0:
+ return;
+ }
+
+ LIST_FOREACH_SAFE(node, node_next, CONNECTIONS.head)
+ {
+ p = node->data;
+
+ if (p->fd > 0)
+ {
+ for (unsigned int i = 0; i < size; ++i)
+ {
+ if ((ufds[i].revents & POLLIN) && ufds[i].fd == p->fd)
+ {
+ res = firedns_getresult(p->fd);
+
+ if (res && res->info)
+ dnsbl_result(res);
+
+ break;
+ }
+ }
+ }
+ else if (firedns_fdinuse < OptionsItem.dns_fdlimit)
+ firedns_doquery(p);
+ }
+}
+
+const char *
+firedns_strerror(int error)
+{
+ if (error == FDNS_ERR_NETWORK)
+ return strerror(errno);
+
+ return errors[error];
+}