summaryrefslogtreecommitdiff
path: root/deps/udns/udns_resolver.c
diff options
context:
space:
mode:
Diffstat (limited to 'deps/udns/udns_resolver.c')
-rw-r--r--deps/udns/udns_resolver.c1294
1 files changed, 0 insertions, 1294 deletions
diff --git a/deps/udns/udns_resolver.c b/deps/udns/udns_resolver.c
deleted file mode 100644
index d083397d4d..0000000000
--- a/deps/udns/udns_resolver.c
+++ /dev/null
@@ -1,1294 +0,0 @@
-/* $Id: udns_resolver.c,v 1.98 2007/01/10 13:32:33 mjt Exp $
- resolver stuff (main module)
-
- Copyright (C) 2005 Michael Tokarev <mjt@corpit.ru>
- This file is part of UDNS library, an async DNS stub resolver.
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library, in file named COPYING.LGPL; if not,
- write to the Free Software Foundation, Inc., 59 Temple Place,
- Suite 330, Boston, MA 02111-1307 USA
-
- */
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-#ifdef WINDOWS
-# include <winsock2.h> /* includes <windows.h> */
-# include <ws2tcpip.h> /* needed for struct in6_addr */
-#else
-# include <sys/types.h>
-# include <sys/socket.h>
-# include <netinet/in.h>
-# include <unistd.h>
-# include <fcntl.h>
-# include <sys/time.h>
-# ifdef HAVE_POLL
-# include <sys/poll.h>
-# else
-# ifdef HAVE_SYS_SELECT_H
-# include <sys/select.h>
-# endif
-# endif
-# ifdef HAVE_TIMES
-# include <sys/times.h>
-# endif
-# define closesocket(sock) close(sock)
-#endif /* !WINDOWS */
-
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <errno.h>
-#include <assert.h>
-#include <stddef.h>
-#include "udns.h"
-
-#ifndef EAFNOSUPPORT
-# define EAFNOSUPPORT EINVAL
-#endif
-#ifndef MSG_DONTWAIT
-# define MSG_DONTWAIT 0
-#endif
-
-struct dns_qlink {
- struct dns_query *next, *prev;
-};
-
-struct dns_query {
- struct dns_qlink dnsq_link; /* list entry (should be first) */
- unsigned dnsq_origdnl0; /* original query DN len w/o last 0 */
- unsigned dnsq_flags; /* control flags for this query */
- unsigned dnsq_servi; /* index of next server to try */
- unsigned dnsq_servwait; /* bitmask: servers left to wait */
- unsigned dnsq_servskip; /* bitmask: servers to skip */
- unsigned dnsq_servnEDNS0; /* bitmask: servers refusing EDNS0 */
- unsigned dnsq_try; /* number of tries made so far */
- dnscc_t *dnsq_nxtsrch; /* next search pointer @dnsc_srchbuf */
- time_t dnsq_deadline; /* when current try will expire */
- dns_parse_fn *dnsq_parse; /* parse: raw => application */
- dns_query_fn *dnsq_cbck; /* the callback to call when done */
- void *dnsq_cbdata; /* user data for the callback */
-#ifndef NDEBUG
- struct dns_ctx *dnsq_ctx; /* the resolver context */
-#endif
- /* char fields at the end to avoid padding */
- dnsc_t dnsq_id[2]; /* query ID */
- dnsc_t dnsq_typcls[4]; /* requested RR type+class */
- dnsc_t dnsq_dn[DNS_MAXDN+DNS_DNPAD]; /* the query DN +alignment */
-};
-
-/* working with dns_query lists */
-
-static __inline void qlist_init(struct dns_qlink *list) {
- list->next = list->prev = (struct dns_query *)list;
-}
-
-static __inline int qlist_isempty(const struct dns_qlink *list) {
- return list->next == (const struct dns_query *)list ? 1 : 0;
-}
-
-static __inline struct dns_query *qlist_first(struct dns_qlink *list) {
- return list->next == (struct dns_query *)list ? 0 : list->next;
-}
-
-static __inline void qlist_remove(struct dns_query *q) {
- q->dnsq_link.next->dnsq_link.prev = q->dnsq_link.prev;
- q->dnsq_link.prev->dnsq_link.next = q->dnsq_link.next;
-}
-
-/* insert q between prev and next */
-static __inline void
-qlist_insert(struct dns_query *q,
- struct dns_query *prev, struct dns_query *next) {
- q->dnsq_link.next = next;
- q->dnsq_link.prev = prev;
- prev->dnsq_link.next = next->dnsq_link.prev = q;
-}
-
-static __inline void
-qlist_insert_after(struct dns_query *q, struct dns_query *prev) {
- qlist_insert(q, prev, prev->dnsq_link.next);
-}
-
-static __inline void
-qlist_insert_before(struct dns_query *q, struct dns_query *next) {
- qlist_insert(q, next->dnsq_link.prev, next);
-}
-
-static __inline void
-qlist_add_tail(struct dns_query *q, struct dns_qlink *top) {
- qlist_insert_before(q, (struct dns_query *)top);
-}
-
-static __inline void
-qlist_add_head(struct dns_query *q, struct dns_qlink *top) {
- qlist_insert_after(q, (struct dns_query *)top);
-}
-
-#define QLIST_FIRST(list, direction) ((list)->direction)
-#define QLIST_ISLAST(list, q) ((q) == (struct dns_query*)(list))
-#define QLIST_NEXT(q, direction) ((q)->dnsq_link.direction)
-
-#define QLIST_FOR_EACH(list, q, direction) \
- for(q = QLIST_FIRST(list, direction); \
- !QLIST_ISLAST(list, q); q = QLIST_NEXT(q, direction))
-
-union sockaddr_ns {
- struct sockaddr sa;
- struct sockaddr_in sin;
-#ifdef HAVE_IPv6
- struct sockaddr_in6 sin6;
-#endif
-};
-
-#define sin_eq(a,b) \
- ((a).sin_port == (b).sin_port && \
- (a).sin_addr.s_addr == (b).sin_addr.s_addr)
-#define sin6_eq(a,b) \
- ((a).sin6_port == (b).sin6_port && \
- memcmp(&(a).sin6_addr, &(b).sin6_addr, sizeof(struct in6_addr)) == 0)
-
-struct dns_ctx { /* resolver context */
- /* settings */
- unsigned dnsc_flags; /* various flags */
- unsigned dnsc_timeout; /* timeout (base value) for queries */
- unsigned dnsc_ntries; /* number of retries */
- unsigned dnsc_ndots; /* ndots to assume absolute name */
- unsigned dnsc_port; /* default port (DNS_PORT) */
- unsigned dnsc_udpbuf; /* size of UDP buffer */
- /* array of nameserver addresses */
- union sockaddr_ns dnsc_serv[DNS_MAXSERV];
- unsigned dnsc_nserv; /* number of nameservers */
- unsigned dnsc_salen; /* length of socket addresses */
- dnsc_t dnsc_srchbuf[1024]; /* buffer for searchlist */
- dnsc_t *dnsc_srchend; /* current end of srchbuf */
-
- dns_utm_fn *dnsc_utmfn; /* register/cancel timer events */
- void *dnsc_utmctx; /* user timer context for utmfn() */
- time_t dnsc_utmexp; /* when user timer expires */
-
- dns_dbgfn *dnsc_udbgfn; /* debugging function */
-
- /* dynamic data */
- unsigned dnsc_nextid; /* next queue ID to use */
- int dnsc_udpsock; /* UDP socket */
- struct dns_qlink dnsc_qactive; /* active list sorted by deadline */
- int dnsc_nactive; /* number entries in dnsc_qactive */
- dnsc_t *dnsc_pbuf; /* packet buffer (udpbuf size) */
- int dnsc_qstatus; /* last query status value */
-};
-
-static const struct {
- const char *name;
- enum dns_opt opt;
- unsigned offset;
- unsigned min, max;
-} dns_opts[] = {
-#define opt(name,opt,field,min,max) \
- {name,opt,offsetof(struct dns_ctx,field),min,max}
- opt("retrans", DNS_OPT_TIMEOUT, dnsc_timeout, 1,300),
- opt("timeout", DNS_OPT_TIMEOUT, dnsc_timeout, 1,300),
- opt("retry", DNS_OPT_NTRIES, dnsc_ntries, 1,50),
- opt("attempts", DNS_OPT_NTRIES, dnsc_ntries, 1,50),
- opt("ndots", DNS_OPT_NDOTS, dnsc_ndots, 0,1000),
- opt("port", DNS_OPT_PORT, dnsc_port, 1,0xffff),
- opt("udpbuf", DNS_OPT_UDPSIZE, dnsc_udpbuf, DNS_MAXPACKET,65536),
-#undef opt
-};
-#define dns_ctxopt(ctx,idx) (*((unsigned*)(((char*)ctx)+dns_opts[idx].offset)))
-
-#define ISSPACE(x) (x == ' ' || x == '\t' || x == '\r' || x == '\n')
-
-struct dns_ctx dns_defctx;
-
-#define SETCTX(ctx) if (!ctx) ctx = &dns_defctx
-#define SETCTXINITED(ctx) SETCTX(ctx); assert(CTXINITED(ctx))
-#define CTXINITED(ctx) (ctx->dnsc_flags & DNS_INITED)
-#define SETCTXFRESH(ctx) SETCTXINITED(ctx); assert(!CTXOPEN(ctx))
-#define SETCTXINACTIVE(ctx) \
- SETCTXINITED(ctx); assert(!ctx->dnsc_nactive)
-#define SETCTXOPEN(ctx) SETCTXINITED(ctx); assert(CTXOPEN(ctx))
-#define CTXOPEN(ctx) (ctx->dnsc_udpsock >= 0)
-
-#if defined(NDEBUG) || !defined(DEBUG)
-#define dns_assert_ctx(ctx)
-#else
-static void dns_assert_ctx(const struct dns_ctx *ctx) {
- int nactive = 0;
- const struct dns_query *q;
- QLIST_FOR_EACH(&ctx->dnsc_qactive, q, next) {
- assert(q->dnsq_ctx == ctx);
- assert(q->dnsq_link.next->dnsq_link.prev == q);
- assert(q->dnsq_link.prev->dnsq_link.next == q);
- ++nactive;
- }
- assert(nactive == ctx->dnsc_nactive);
-}
-#endif
-
-enum {
- DNS_INTERNAL = 0xffff, /* internal flags mask */
- DNS_INITED = 0x0001, /* the context is initialized */
- DNS_ASIS_DONE = 0x0002, /* search: skip the last as-is query */
- DNS_SEEN_NODATA = 0x0004, /* search: NODATA has been received */
-};
-
-int dns_add_serv(struct dns_ctx *ctx, const char *serv) {
- union sockaddr_ns *sns;
- SETCTXFRESH(ctx);
- if (!serv)
- return (ctx->dnsc_nserv = 0);
- if (ctx->dnsc_nserv >= DNS_MAXSERV)
- return errno = ENFILE, -1;
- sns = &ctx->dnsc_serv[ctx->dnsc_nserv];
- memset(sns, 0, sizeof(*sns));
- if (dns_pton(AF_INET, serv, &sns->sin.sin_addr) > 0) {
- sns->sin.sin_family = AF_INET;
- return ++ctx->dnsc_nserv;
- }
-#ifdef HAVE_IPv6
- if (dns_pton(AF_INET6, serv, &sns->sin6.sin6_addr) > 0) {
- sns->sin6.sin6_family = AF_INET6;
- return ++ctx->dnsc_nserv;
- }
-#endif
- errno = EINVAL;
- return -1;
-}
-
-int dns_add_serv_s(struct dns_ctx *ctx, const struct sockaddr *sa) {
- SETCTXFRESH(ctx);
- if (!sa)
- return (ctx->dnsc_nserv = 0);
- if (ctx->dnsc_nserv >= DNS_MAXSERV)
- return errno = ENFILE, -1;
-#ifdef HAVE_IPv6
- else if (sa->sa_family == AF_INET6)
- ctx->dnsc_serv[ctx->dnsc_nserv].sin6 = *(struct sockaddr_in6*)sa;
-#endif
- else if (sa->sa_family == AF_INET)
- ctx->dnsc_serv[ctx->dnsc_nserv].sin = *(struct sockaddr_in*)sa;
- else
- return errno = EAFNOSUPPORT, -1;
- return ++ctx->dnsc_nserv;
-}
-
-int dns_set_opts(struct dns_ctx *ctx, const char *opts) {
- unsigned i, v;
- SETCTXINACTIVE(ctx);
- for(;;) {
- while(ISSPACE(*opts)) ++opts;
- if (!*opts) break;
- for(i = 0; i < sizeof(dns_opts)/sizeof(dns_opts[0]); ++i) {
- v = strlen(dns_opts[i].name);
- if (strncmp(dns_opts[i].name, opts, v) != 0 ||
- (opts[v] != ':' && opts[v] != '='))
- continue;
- opts += v + 1;
- v = 0;
- if (*opts < '0' || *opts > '9') break;
- do v = v * 10 + (*opts++ - '0');
- while (*opts >= '0' && *opts <= '9');
- if (v < dns_opts[i].min) v = dns_opts[i].min;
- if (v > dns_opts[i].max) v = dns_opts[i].max;
- dns_ctxopt(ctx, i) = v;
- break;
- }
- while(*opts && !ISSPACE(*opts)) ++opts;
- }
- return 0;
-}
-
-int dns_set_opt(struct dns_ctx *ctx, enum dns_opt opt, int val) {
- int prev;
- unsigned i;
- SETCTXINACTIVE(ctx);
- for(i = 0; i < sizeof(dns_opts)/sizeof(dns_opts[0]); ++i) {
- if (dns_opts[i].opt != opt) continue;
- prev = dns_ctxopt(ctx, i);
- if (val >= 0) {
- unsigned v = val;
- if (v < dns_opts[i].min || v > dns_opts[i].max) {
- errno = EINVAL;
- return -1;
- }
- dns_ctxopt(ctx, i) = v;
- }
- return prev;
- }
- if (opt == DNS_OPT_FLAGS) {
- prev = ctx->dnsc_flags & ~DNS_INTERNAL;
- if (val >= 0)
- ctx->dnsc_flags =
- (ctx->dnsc_flags & DNS_INTERNAL) | (val & ~DNS_INTERNAL);
- return prev;
- }
- errno = ENOSYS;
- return -1;
-}
-
-int dns_add_srch(struct dns_ctx *ctx, const char *srch) {
- int dnl;
- SETCTXINACTIVE(ctx);
- if (!srch) {
- memset(ctx->dnsc_srchbuf, 0, sizeof(ctx->dnsc_srchbuf));
- ctx->dnsc_srchend = ctx->dnsc_srchbuf;
- return 0;
- }
- dnl =
- sizeof(ctx->dnsc_srchbuf) - (ctx->dnsc_srchend - ctx->dnsc_srchbuf) - 1;
- dnl = dns_sptodn(srch, ctx->dnsc_srchend, dnl);
- if (dnl > 0)
- ctx->dnsc_srchend += dnl;
- ctx->dnsc_srchend[0] = '\0'; /* we ensure the list is always ends at . */
- if (dnl > 0)
- return 0;
- errno = EINVAL;
- return -1;
-}
-
-static void dns_drop_utm(struct dns_ctx *ctx) {
- if (ctx->dnsc_utmfn)
- ctx->dnsc_utmfn(NULL, -1, ctx->dnsc_utmctx);
- ctx->dnsc_utmctx = NULL;
- ctx->dnsc_utmexp = -1;
-}
-
-static void
-_dns_request_utm(struct dns_ctx *ctx, time_t now) {
- struct dns_query *q;
- time_t deadline;
- int timeout;
- q = qlist_first(&ctx->dnsc_qactive);
- if (!q)
- deadline = -1, timeout = -1;
- else if (!now || q->dnsq_deadline <= now)
- deadline = 0, timeout = 0;
- else
- deadline = q->dnsq_deadline, timeout = (int)(deadline - now);
- if (ctx->dnsc_utmexp == deadline)
- return;
- ctx->dnsc_utmfn(ctx, timeout, ctx->dnsc_utmctx);
- ctx->dnsc_utmexp = deadline;
-}
-
-static __inline void
-dns_request_utm(struct dns_ctx *ctx, time_t now) {
- if (ctx->dnsc_utmfn)
- _dns_request_utm(ctx, now);
-}
-
-void dns_set_dbgfn(struct dns_ctx *ctx, dns_dbgfn *dbgfn) {
- SETCTXINITED(ctx);
- ctx->dnsc_udbgfn = dbgfn;
-}
-
-void
-dns_set_tmcbck(struct dns_ctx *ctx, dns_utm_fn *fn, void *data) {
- SETCTXINITED(ctx);
- dns_drop_utm(ctx);
- ctx->dnsc_utmfn = fn;
- ctx->dnsc_utmctx = data;
- if (CTXOPEN(ctx))
- dns_request_utm(ctx, 0);
-}
-
-unsigned dns_random16(void) {
-#ifdef WINDOWS
- FILETIME ft;
- GetSystemTimeAsFileTime(&ft);
-#define x (ft.dwLowDateTime)
-#else
- struct timeval tv;
- gettimeofday(&tv, NULL);
-#define x (tv.tv_usec)
-#endif
- return ((unsigned)x ^ ((unsigned)x >> 16)) & 0xffff;
-#undef x
-}
-
-void dns_close(struct dns_ctx *ctx) {
- struct dns_query *q;
- SETCTX(ctx);
- if (CTXINITED(ctx)) {
- if (ctx->dnsc_udpsock >= 0)
- closesocket(ctx->dnsc_udpsock);
- ctx->dnsc_udpsock = -1;
- if (ctx->dnsc_pbuf)
- free(ctx->dnsc_pbuf);
- ctx->dnsc_pbuf = NULL;
- while((q = qlist_first(&ctx->dnsc_qactive)) != NULL) {
- qlist_remove(q);
- free(q);
- }
- ctx->dnsc_nactive = 0;
- dns_drop_utm(ctx);
- }
-}
-
-void dns_reset(struct dns_ctx *ctx) {
- SETCTX(ctx);
- dns_close(ctx);
- memset(ctx, 0, sizeof(*ctx));
- ctx->dnsc_timeout = 4;
- ctx->dnsc_ntries = 3;
- ctx->dnsc_ndots = 1;
- ctx->dnsc_udpbuf = DNS_EDNS0PACKET;
- ctx->dnsc_port = DNS_PORT;
- ctx->dnsc_udpsock = -1;
- ctx->dnsc_srchend = ctx->dnsc_srchbuf;
- qlist_init(&ctx->dnsc_qactive);
- ctx->dnsc_nextid = dns_random16();
- ctx->dnsc_flags = DNS_INITED;
-}
-
-struct dns_ctx *dns_new(const struct dns_ctx *copy) {
- struct dns_ctx *ctx;
- SETCTXINITED(copy);
- dns_assert_ctx(copy);
- ctx = malloc(sizeof(*ctx));
- if (!ctx)
- return NULL;
- *ctx = *copy;
- ctx->dnsc_udpsock = -1;
- qlist_init(&ctx->dnsc_qactive);
- ctx->dnsc_nactive = 0;
- ctx->dnsc_pbuf = NULL;
- ctx->dnsc_qstatus = 0;
- ctx->dnsc_utmfn = NULL;
- ctx->dnsc_utmctx = NULL;
- ctx->dnsc_nextid = dns_random16();
- return ctx;
-}
-
-void dns_free(struct dns_ctx *ctx) {
- assert(ctx != NULL && ctx != &dns_defctx);
- dns_reset(ctx);
- free(ctx);
-}
-
-int dns_open(struct dns_ctx *ctx) {
- int sock;
- unsigned i;
- int port;
- union sockaddr_ns *sns;
-#ifdef HAVE_IPv6
- unsigned have_inet6 = 0;
-#endif
-
- SETCTXINITED(ctx);
- assert(!CTXOPEN(ctx));
-
- port = htons((unsigned short)ctx->dnsc_port);
- /* ensure we have at least one server */
- if (!ctx->dnsc_nserv) {
- sns = ctx->dnsc_serv;
- sns->sin.sin_family = AF_INET;
- sns->sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
- ctx->dnsc_nserv = 1;
- }
-
- for (i = 0; i < ctx->dnsc_nserv; ++i) {
- sns = &ctx->dnsc_serv[i];
- /* set port for each sockaddr */
-#ifdef HAVE_IPv6
- if (sns->sa.sa_family == AF_INET6) {
- if (!sns->sin6.sin6_port) sns->sin6.sin6_port = (unsigned short)port;
- ++have_inet6;
- }
- else
-#endif
- {
- assert(sns->sa.sa_family == AF_INET);
- if (!sns->sin.sin_port) sns->sin.sin_port = (unsigned short)port;
- }
- }
-
-#ifdef HAVE_IPv6
- if (have_inet6 && have_inet6 < ctx->dnsc_nserv) {
- /* convert all IPv4 addresses to IPv6 V4MAPPED */
- struct sockaddr_in6 sin6;
- memset(&sin6, 0, sizeof(sin6));
- sin6.sin6_family = AF_INET6;
- /* V4MAPPED: ::ffff:1.2.3.4 */
- sin6.sin6_addr.s6_addr[10] = 0xff;
- sin6.sin6_addr.s6_addr[11] = 0xff;
- for(i = 0; i < ctx->dnsc_nserv; ++i) {
- sns = &ctx->dnsc_serv[i];
- if (sns->sa.sa_family == AF_INET) {
- sin6.sin6_port = sns->sin.sin_port;
- ((struct in_addr*)&sin6.sin6_addr)[3] = sns->sin.sin_addr;
- sns->sin6 = sin6;
- }
- }
- }
-
- ctx->dnsc_salen = have_inet6 ?
- sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in);
-
- if (have_inet6)
- sock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
- else
- sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
-#else /* !HAVE_IPv6 */
- sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
- ctx->dnsc_salen = sizeof(struct sockaddr_in);
-#endif /* HAVE_IPv6 */
-
- if (sock < 0) {
- ctx->dnsc_qstatus = DNS_E_TEMPFAIL;
- return -1;
- }
-#ifdef WINDOWS
- { unsigned long on = 1;
- if (ioctlsocket(sock, FIONBIO, &on) == SOCKET_ERROR) {
- closesocket(sock);
- ctx->dnsc_qstatus = DNS_E_TEMPFAIL;
- return -1;
- }
- }
-#else /* !WINDOWS */
- if (fcntl(sock, F_SETFL, fcntl(sock, F_GETFL) | O_NONBLOCK) < 0 ||
- fcntl(sock, F_SETFD, FD_CLOEXEC) < 0) {
- closesocket(sock);
- ctx->dnsc_qstatus = DNS_E_TEMPFAIL;
- return -1;
- }
-#endif /* WINDOWS */
- /* allocate the packet buffer */
- if ((ctx->dnsc_pbuf = malloc(ctx->dnsc_udpbuf)) == NULL) {
- closesocket(sock);
- ctx->dnsc_qstatus = DNS_E_NOMEM;
- errno = ENOMEM;
- return -1;
- }
-
- ctx->dnsc_udpsock = sock;
- dns_request_utm(ctx, 0);
- return sock;
-}
-
-int dns_sock(const struct dns_ctx *ctx) {
- SETCTXINITED(ctx);
- return ctx->dnsc_udpsock;
-}
-
-int dns_active(const struct dns_ctx *ctx) {
- SETCTXINITED(ctx);
- dns_assert_ctx(ctx);
- return ctx->dnsc_nactive;
-}
-
-int dns_status(const struct dns_ctx *ctx) {
- SETCTX(ctx);
- return ctx->dnsc_qstatus;
-}
-void dns_setstatus(struct dns_ctx *ctx, int status) {
- SETCTX(ctx);
- ctx->dnsc_qstatus = status;
-}
-
-/* End the query: disconnect it from the active list, free it,
- * and return the result to the caller.
- */
-static void
-dns_end_query(struct dns_ctx *ctx, struct dns_query *q,
- int status, void *result) {
- dns_query_fn *cbck = q->dnsq_cbck;
- void *cbdata = q->dnsq_cbdata;
- ctx->dnsc_qstatus = status;
- assert((status < 0 && result == 0) || (status >= 0 && result != 0));
- assert(cbck != 0); /*XXX callback may be NULL */
- assert(ctx->dnsc_nactive > 0);
- --ctx->dnsc_nactive;
- qlist_remove(q);
- /* force the query to be unconnected */
- /*memset(q, 0, sizeof(*q));*/
-#ifndef NDEBUG
- q->dnsq_ctx = NULL;
-#endif
- free(q);
- cbck(ctx, result, cbdata);
-}
-
-#define DNS_DBG(ctx, code, sa, slen, pkt, plen) \
- do { \
- if (ctx->dnsc_udbgfn) \
- ctx->dnsc_udbgfn(code, (sa), slen, pkt, plen, 0, 0); \
- } while(0)
-#define DNS_DBGQ(ctx, q, code, sa, slen, pkt, plen) \
- do { \
- if (ctx->dnsc_udbgfn) \
- ctx->dnsc_udbgfn(code, (sa), slen, pkt, plen, q, q->dnsq_cbdata); \
- } while(0)
-
-static void dns_newid(struct dns_ctx *ctx, struct dns_query *q) {
- /* this is how we choose an identifier for a new query (qID).
- * For now, it's just sequential number, incremented for every query, and
- * thus obviously trivial to guess.
- * There are two choices:
- * a) use sequential numbers. It is plain insecure. In DNS, there are two
- * places where random numbers are (or can) be used to increase security:
- * random qID and random source port number. Without this randomness
- * (udns uses fixed port for all queries), or when the randomness is weak,
- * it's trivial to spoof query replies. With randomness however, it
- * becomes a bit more difficult task. Too bad we only have 16 bits for
- * our security, as qID is only two bytes. It isn't a security per se,
- * to rely on those 16 bits - an attacker can just flood us with fake
- * replies with all possible qIDs (only 65536 of them), and in this case,
- * even if we'll use true random qIDs, we'll be in trouble (not protected
- * against spoofing). Yes, this is only possible on a high-speed network
- * (probably on the LAN only, since usually a border router for a LAN
- * protects internal machines from packets with spoofed local addresses
- * from outside, and usually a nameserver resides on LAN), but it's
- * still very well possible to send us fake replies.
- * In other words: there's nothing a DNS (stub) resolver can do against
- * spoofing attacks, unless DNSSEC is in use, which helps here alot.
- * Too bad that DNSSEC isn't widespread, so relying on it isn't an
- * option in almost all cases...
- * b) use random qID, based on some random-number generation mechanism.
- * This way, we increase our protection a bit (see above - it's very weak
- * still), but we also increase risk of qID reuse and matching late replies
- * that comes to queries we've sent before against new queries. There are
- * some more corner cases around that, as well - for example, normally,
- * udns tries to find the query for a given reply by qID, *and* by
- * verifying that the query DN and other parameters are also the same
- * (so if the new query is against another domain name, old reply will
- * be ignored automatically). But certain types of replies which we now
- * handle - for example, FORMERR reply from servers which refuses to
- * process EDNS0-enabled packets - comes without all the query parameters
- * but the qID - so we're forced to use qID only when determining which
- * query the given reply corresponds to. This makes us even more
- * vulnerable to spoofing attacks, because an attacker don't even need to
- * know which queries we perform to spoof the replies - he only needs to
- * flood us with fake FORMERR "replies".
- *
- * That all to say: using sequential (or any other trivially guessable)
- * numbers for qIDs is insecure, but the whole thing is inherently insecure
- * as well, and this "extra weakness" that comes from weak qID choosing
- * algorithm adds almost nothing to the underlying problem.
- *
- * It CAN NOT be made secure. Period. That's it.
- * Unless we choose to implement DNSSEC, which is a whole different story.
- * Forcing TCP mode makes it better, but who uses TCP for DNS anyway?
- * (and it's hardly possible because of huge impact on the recursive
- * nameservers).
- *
- * Note that ALL stub resolvers (again, unless they implement and enforce
- * DNSSEC) suffers from this same problem.
- *
- * So, instead of trying to be more secure (which actually is not - false
- * sense of security is - I think - is worse than no security), I'm trying
- * to be more robust here (by preventing qID reuse, which helps in normal
- * conditions). And use sequential qID generation scheme.
- */
- dns_put16(q->dnsq_id, ctx->dnsc_nextid++);
- /* reset all parameters relevant for previous query lifetime */
- q->dnsq_try = 0;
- q->dnsq_servi = 0;
- /*XXX probably should keep dnsq_servnEDNS0 bits?
- * See also comments in dns_ioevent() about FORMERR case */
- q->dnsq_servwait = q->dnsq_servskip = q->dnsq_servnEDNS0 = 0;
-}
-
-/* Find next search suffix and fills in q->dnsq_dn.
- * Return 0 if no more to try. */
-static int dns_next_srch(struct dns_ctx *ctx, struct dns_query *q) {
- unsigned dnl;
-
- for(;;) {
- if (q->dnsq_nxtsrch > ctx->dnsc_srchend)
- return 0;
- dnl = dns_dnlen(q->dnsq_nxtsrch);
- if (dnl + q->dnsq_origdnl0 <= DNS_MAXDN &&
- (*q->dnsq_nxtsrch || !(q->dnsq_flags & DNS_ASIS_DONE)))
- break;
- q->dnsq_nxtsrch += dnl;
- }
- memcpy(q->dnsq_dn + q->dnsq_origdnl0, q->dnsq_nxtsrch, dnl);
- if (!*q->dnsq_nxtsrch)
- q->dnsq_flags |= DNS_ASIS_DONE;
- q->dnsq_nxtsrch += dnl;
- dns_newid(ctx, q); /* new ID for new qDN */
- return 1;
-}
-
-/* find the server to try for current iteration.
- * Note that current dnsq_servi may point to a server we should skip --
- * in that case advance to the next server.
- * Return true if found, false if all tried.
- */
-static int dns_find_serv(const struct dns_ctx *ctx, struct dns_query *q) {
- while(q->dnsq_servi < ctx->dnsc_nserv) {
- if (!(q->dnsq_servskip & (1 << q->dnsq_servi)))
- return 1;
- ++q->dnsq_servi;
- }
- return 0;
-}
-
-/* format and send the query to a given server.
- * In case of network problem (sendto() fails), return -1,
- * else return 0.
- */
-static int
-dns_send_this(struct dns_ctx *ctx, struct dns_query *q,
- unsigned servi, time_t now) {
- unsigned qlen;
- unsigned tries;
-
- { /* format the query buffer */
- dnsc_t *p = ctx->dnsc_pbuf;
- memset(p, 0, DNS_HSIZE);
- if (!(q->dnsq_flags & DNS_NORD)) p[DNS_H_F1] |= DNS_HF1_RD;
- if (q->dnsq_flags & DNS_AAONLY) p[DNS_H_F1] |= DNS_HF1_AA;
- p[DNS_H_QDCNT2] = 1;
- memcpy(p + DNS_H_QID, q->dnsq_id, 2);
- p = dns_payload(p);
- /* copy query dn */
- p += dns_dntodn(q->dnsq_dn, p, DNS_MAXDN);
- /* query type and class */
- memcpy(p, q->dnsq_typcls, 4); p += 4;
- /* add EDNS0 size record */
- if (ctx->dnsc_udpbuf > DNS_MAXPACKET &&
- !(q->dnsq_servnEDNS0 & (1 << servi))) {
- *p++ = 0; /* empty (root) DN */
- p = dns_put16(p, DNS_T_OPT);
- p = dns_put16(p, ctx->dnsc_udpbuf);
- /* EDNS0 RCODE & VERSION; rest of the TTL field; RDLEN */
- memset(p, 0, 2+2+2); p += 2+2+2;
- ctx->dnsc_pbuf[DNS_H_ARCNT2] = 1;
- }
- qlen = p - ctx->dnsc_pbuf;
- assert(qlen <= ctx->dnsc_udpbuf);
- }
-
- /* send the query */
- tries = 10;
- while (sendto(ctx->dnsc_udpsock, (void*)ctx->dnsc_pbuf, qlen, 0,
- &ctx->dnsc_serv[servi].sa, ctx->dnsc_salen) < 0) {
- /*XXX just ignore the sendto() error for now and try again.
- * In the future, it may be possible to retrieve the error code
- * and find which operation/query failed.
- *XXX try the next server too? (if ENETUNREACH is returned immediately)
- */
- if (--tries) continue;
- /* if we can't send the query, fail it. */
- dns_end_query(ctx, q, DNS_E_TEMPFAIL, 0);
- return -1;
- }
- DNS_DBGQ(ctx, q, 1,
- &ctx->dnsc_serv[servi].sa, sizeof(union sockaddr_ns),
- ctx->dnsc_pbuf, qlen);
- q->dnsq_servwait |= 1 << servi; /* expect reply from this ns */
-
- q->dnsq_deadline = now +
- (dns_find_serv(ctx, q) ? 1 : ctx->dnsc_timeout << q->dnsq_try);
-
- /* move the query to the proper place, according to the new deadline */
- qlist_remove(q);
- { /* insert from the tail */
- struct dns_query *p;
- QLIST_FOR_EACH(&ctx->dnsc_qactive, p, prev)
- if (p->dnsq_deadline <= q->dnsq_deadline)
- break;
- qlist_insert_after(q, p);
- }
-
- return 0;
-}
-
-/* send the query out using next available server
- * and add it to the active list, or, if no servers available,
- * end it.
- */
-static void
-dns_send(struct dns_ctx *ctx, struct dns_query *q, time_t now) {
-
- /* if we can't send the query, return TEMPFAIL even when searching:
- * we can't be sure whenever the name we tried to search exists or not,
- * so don't continue searching, or we may find the wrong name. */
-
- if (!dns_find_serv(ctx, q)) {
- /* no more servers in this iteration. Try the next cycle */
- q->dnsq_servi = 0; /* reset */
- q->dnsq_try++; /* next try */
- if (q->dnsq_try >= ctx->dnsc_ntries ||
- !dns_find_serv(ctx, q)) {
- /* no more servers and tries, fail the query */
- /* return TEMPFAIL even when searching: no more tries for this
- * searchlist, and no single definitive reply (handled in dns_ioevent()
- * in NOERROR or NXDOMAIN cases) => all nameservers failed to process
- * current search list element, so we don't know whenever the name exists.
- */
- dns_end_query(ctx, q, DNS_E_TEMPFAIL, 0);
- return;
- }
- }
-
- dns_send_this(ctx, q, q->dnsq_servi++, now);
-}
-
-static void dns_dummy_cb(struct dns_ctx *ctx, void *result, void *data) {
- if (result) free(result);
- data = ctx = 0; /* used */
-}
-
-/* The (only, main, real) query submission routine.
- * Allocate new query structure, initialize it, check validity of
- * parameters, and add it to the head of the active list, without
- * trying to send it (to be picked up on next event).
- * Error return (without calling the callback routine) -
- * no memory or wrong parameters.
- *XXX The `no memory' case probably should go to the callback anyway...
- */
-struct dns_query *
-dns_submit_dn(struct dns_ctx *ctx,
- dnscc_t *dn, int qcls, int qtyp, int flags,
- dns_parse_fn *parse, dns_query_fn *cbck, void *data) {
- struct dns_query *q;
- SETCTXOPEN(ctx);
- dns_assert_ctx(ctx);
-
- q = calloc(sizeof(*q), 1);
- if (!q) {
- ctx->dnsc_qstatus = DNS_E_NOMEM;
- return NULL;
- }
-
-#ifndef NDEBUG
- q->dnsq_ctx = ctx;
-#endif
- q->dnsq_parse = parse;
- q->dnsq_cbck = cbck ? cbck : dns_dummy_cb;
- q->dnsq_cbdata = data;
-
- q->dnsq_origdnl0 = dns_dntodn(dn, q->dnsq_dn, sizeof(q->dnsq_dn));
- assert(q->dnsq_origdnl0 > 0);
- --q->dnsq_origdnl0; /* w/o the trailing 0 */
- dns_put16(q->dnsq_typcls+0, qtyp);
- dns_put16(q->dnsq_typcls+2, qcls);
- q->dnsq_flags = (flags | ctx->dnsc_flags) & ~DNS_INTERNAL;
-
- if (flags & DNS_NOSRCH ||
- dns_dnlabels(q->dnsq_dn) > ctx->dnsc_ndots) {
- q->dnsq_nxtsrch = flags & DNS_NOSRCH ?
- ctx->dnsc_srchend /* end of the search list if no search requested */ :
- ctx->dnsc_srchbuf /* beginning of the list, but try as-is first */;
- q->dnsq_flags |= DNS_ASIS_DONE;
- dns_newid(ctx, q);
- }
- else {
- q->dnsq_nxtsrch = ctx->dnsc_srchbuf;
- dns_next_srch(ctx, q);
- }
-
- qlist_add_head(q, &ctx->dnsc_qactive);
- ++ctx->dnsc_nactive;
- dns_request_utm(ctx, 0);
-
- return q;
-}
-
-struct dns_query *
-dns_submit_p(struct dns_ctx *ctx,
- const char *name, int qcls, int qtyp, int flags,
- dns_parse_fn *parse, dns_query_fn *cbck, void *data) {
- int isabs;
- SETCTXOPEN(ctx);
- if (dns_ptodn(name, 0, ctx->dnsc_pbuf, DNS_MAXDN, &isabs) <= 0) {
- ctx->dnsc_qstatus = DNS_E_BADQUERY;
- return NULL;
- }
- if (isabs)
- flags |= DNS_NOSRCH;
- return
- dns_submit_dn(ctx, ctx->dnsc_pbuf, qcls, qtyp, flags, parse, cbck, data);
-}
-
-/* process readable fd condition.
- * To be usable in edge-triggered environment, the routine
- * should consume all input so it should loop over.
- * Note it isn't really necessary to loop here, because
- * an application may perform the loop just fine by it's own,
- * but in this case we should return some sensitive result,
- * to indicate when to stop calling and error conditions.
- * Note also we may encounter all sorts of recvfrom()
- * errors which aren't fatal, and at the same time we may
- * loop forever if an error IS fatal.
- */
-void dns_ioevent(struct dns_ctx *ctx, time_t now) {
- int r;
- unsigned servi;
- struct dns_query *q;
- dnsc_t *pbuf;
- dnscc_t *pend, *pcur;
- void *result;
- union sockaddr_ns sns;
- socklen_t slen;
-
- SETCTX(ctx);
- if (!CTXOPEN(ctx))
- return;
- dns_assert_ctx(ctx);
- pbuf = ctx->dnsc_pbuf;
-
- if (!now) now = time(NULL);
-
-again: /* receive the reply */
-
- slen = sizeof(sns);
- r = recvfrom(ctx->dnsc_udpsock, (void*)pbuf, ctx->dnsc_udpbuf,
- MSG_DONTWAIT, &sns.sa, &slen);
- if (r < 0) {
- /*XXX just ignore recvfrom() errors for now.
- * in the future it may be possible to determine which
- * query failed and requeue it.
- * Note there may be various error conditions, triggered
- * by both local problems and remote problems. It isn't
- * quite trivial to determine whenever an error is local
- * or remote. On local errors, we should stop, while
- * remote errors should be ignored (for now anyway).
- */
-#ifdef WINDOWS
- if (WSAGetLastError() == WSAEWOULDBLOCK)
-#else
- if (errno == EAGAIN)
-#endif
- {
- dns_request_utm(ctx, now);
- return;
- }
- goto again;
- }
-
- pend = pbuf + r;
- pcur = dns_payload(pbuf);
-
- /* check reply header */
- if (pcur > pend || dns_numqd(pbuf) > 1 || dns_opcode(pbuf) != 0) {
- DNS_DBG(ctx, -1/*bad reply*/, &sns.sa, slen, pbuf, r);
- goto again;
- }
-
- /* find the matching query, by qID */
- for (q = QLIST_FIRST(&ctx->dnsc_qactive, next);; q = QLIST_NEXT(q, next)) {
- if (QLIST_ISLAST(&ctx->dnsc_qactive, q)) {
- /* no more requests: old reply? */
- DNS_DBG(ctx, -5/*no matching query*/, &sns.sa, slen, pbuf, r);
- goto again;
- }
- if (pbuf[DNS_H_QID1] == q->dnsq_id[0] &&
- pbuf[DNS_H_QID2] == q->dnsq_id[1])
- break;
- }
-
- /* if we have numqd, compare with our query qDN */
- if (dns_numqd(pbuf)) {
- /* decode the qDN */
- dnsc_t dn[DNS_MAXDN];
- if (dns_getdn(pbuf, &pcur, pend, dn, sizeof(dn)) < 0 ||
- pcur + 4 > pend) {
- DNS_DBG(ctx, -1/*bad reply*/, &sns.sa, slen, pbuf, r);
- goto again;
- }
- if (!dns_dnequal(dn, q->dnsq_dn) ||
- memcmp(pcur, q->dnsq_typcls, 4) != 0) {
- /* not this query */
- DNS_DBG(ctx, -5/*no matching query*/, &sns.sa, slen, pbuf, r);
- goto again;
- }
- /* here, query match, and pcur points past qDN in query section in pbuf */
- }
- /* if no numqd, we only allow FORMERR rcode */
- else if (dns_rcode(pbuf) != DNS_R_FORMERR) {
- /* treat it as bad reply if !FORMERR */
- DNS_DBG(ctx, -1/*bad reply*/, &sns.sa, slen, pbuf, r);
- goto again;
- }
- else {
- /* else it's FORMERR, handled below */
- }
-
- /* find server */
-#ifdef HAVE_IPv6
- if (sns.sa.sa_family == AF_INET6 && slen >= sizeof(sns.sin6)) {
- for(servi = 0; servi < ctx->dnsc_nserv; ++servi)
- if (sin6_eq(ctx->dnsc_serv[servi].sin6, sns.sin6))
- break;
- }
- else
-#endif
- if (sns.sa.sa_family == AF_INET && slen >= sizeof(sns.sin)) {
- for(servi = 0; servi < ctx->dnsc_nserv; ++servi)
- if (sin_eq(ctx->dnsc_serv[servi].sin, sns.sin))
- break;
- }
- else
- servi = ctx->dnsc_nserv;
-
- /* check if we expect reply from this server.
- * Note we can receive reply from first try if we're already at next */
- if (!(q->dnsq_servwait & (1 << servi))) { /* if ever asked this NS */
- DNS_DBG(ctx, -2/*wrong server*/, &sns.sa, slen, pbuf, r);
- goto again;
- }
-
- /* we got (some) reply for our query */
-
- DNS_DBGQ(ctx, q, 0, &sns.sa, slen, pbuf, r);
- q->dnsq_servwait &= ~(1 << servi); /* don't expect reply from this serv */
-
- /* process the RCODE */
- switch(dns_rcode(pbuf)) {
-
- case DNS_R_NOERROR:
- if (dns_tc(pbuf)) {
- /* possible truncation. We can't deal with it. */
- /*XXX for now, treat TC bit the same as SERVFAIL.
- * It is possible to:
- * a) try to decode the reply - may be ANSWER section is ok;
- * b) check if server understands EDNS0, and if it is, and
- * answer still don't fit, end query.
- */
- break;
- }
- if (!dns_numan(pbuf)) { /* no data of requested type */
- if (dns_next_srch(ctx, q)) {
- /* if we're searching, try next searchlist element,
- * but remember NODATA reply. */
- q->dnsq_flags |= DNS_SEEN_NODATA;
- dns_send(ctx, q, now);
- }
- else
- /* else - nothing to search any more - finish the query.
- * It will be NODATA since we've seen a NODATA reply. */
- dns_end_query(ctx, q, DNS_E_NODATA, 0);
- }
- /* we've got a positive reply here */
- else if (q->dnsq_parse) {
- /* if we have parsing routine, call it and return whatever it returned */
- /* don't try to re-search if NODATA here. For example,
- * if we asked for A but only received CNAME. Unless we'll
- * someday do recursive queries. And that's problematic too, since
- * we may be dealing with specific AA-only nameservers for a given
- * domain, but CNAME points elsewhere...
- */
- r = q->dnsq_parse(q->dnsq_dn, pbuf, pcur, pend, &result);
- dns_end_query(ctx, q, r, r < 0 ? NULL : result);
- }
- /* else just malloc+copy the raw DNS reply */
- else if ((result = malloc(r)) == NULL)
- dns_end_query(ctx, q, DNS_E_NOMEM, NULL);
- else {
- memcpy(result, pbuf, r);
- dns_end_query(ctx, q, r, result);
- }
- goto again;
-
- case DNS_R_NXDOMAIN: /* Non-existing domain. */
- if (dns_next_srch(ctx, q))
- /* more search entries exists, try them. */
- dns_send(ctx, q, now);
- else
- /* nothing to search anymore. End the query, returning either NODATA
- * if we've seen it before, or NXDOMAIN if not. */
- dns_end_query(ctx, q,
- q->dnsq_flags & DNS_SEEN_NODATA ? DNS_E_NODATA : DNS_E_NXDOMAIN, 0);
- goto again;
-
- case DNS_R_FORMERR:
- case DNS_R_NOTIMPL:
- /* for FORMERR and NOTIMPL rcodes, if we tried EDNS0-enabled query,
- * try w/o EDNS0. */
- if (ctx->dnsc_udpbuf > DNS_MAXPACKET &&
- !(q->dnsq_servnEDNS0 & (1 << servi))) {
- /* we always trying EDNS0 first if enabled, and retry a given query
- * if not available. Maybe it's better to remember inavailability of
- * EDNS0 in ctx as a per-NS flag, and never try again for this NS.
- * For long-running applications.. maybe they will change the nameserver
- * while we're running? :) Also, since FORMERR is the only rcode we
- * allow to be header-only, and in this case the only check we do to
- * find a query it belongs to is qID (not qDN+qCLS+qTYP), it's much
- * easier to spoof and to force us to perform non-EDNS0 queries only...
- */
- q->dnsq_servnEDNS0 |= 1 << servi;
- dns_send_this(ctx, q, servi, now);
- goto again;
- }
- /* else we handle it the same as SERVFAIL etc */
-
- case DNS_R_SERVFAIL:
- case DNS_R_REFUSED:
- /* for these rcodes, advance this request
- * to the next server and reschedule */
- default: /* unknown rcode? hmmm... */
- break;
- }
-
- /* here, we received unexpected reply */
- q->dnsq_servskip |= (1 << servi); /* don't retry this server */
-
- /* we don't expect replies from this server anymore.
- * But there may be other servers. Some may be still processing our
- * query, and some may be left to try.
- * We just ignore this reply and wait a bit more if some NSes haven't
- * replied yet (dnsq_servwait != 0), and let the situation to be handled
- * on next event processing. Timeout for this query is set correctly,
- * if not taking into account the one-second difference - we can try
- * next server in the same iteration sooner.
- */
-
- /* try next server */
- if (!q->dnsq_servwait) {
- /* next retry: maybe some other servers will reply next time.
- * dns_send() will end the query for us if no more servers to try.
- * Note we can't continue with the next searchlist element here:
- * we don't know if the current qdn exists or not, there's no definitive
- * answer yet (which is seen in cases above).
- *XXX standard resolver also tries as-is query in case all nameservers
- * failed to process our query and if not tried before. We don't do it.
- */
- dns_send(ctx, q, now);
- }
- else {
- /* else don't do anything - not all servers replied yet */
- }
- goto again;
-
-}
-
-/* handle all timeouts */
-int dns_timeouts(struct dns_ctx *ctx, int maxwait, time_t now) {
- /* this is a hot routine */
- struct dns_query *q;
-
- SETCTX(ctx);
- dns_assert_ctx(ctx);
-
- /* Pick up first entry from query list.
- * If its deadline has passed, (re)send it
- * (dns_send() will move it next in the list).
- * If not, this is the query which determines the closest deadline.
- */
-
- q = qlist_first(&ctx->dnsc_qactive);
- if (!q)
- return maxwait;
- if (!now)
- now = time(NULL);
- do {
- if (q->dnsq_deadline > now) { /* first non-expired query */
- int w = (int)(q->dnsq_deadline - now);
- if (maxwait < 0 || maxwait > w)
- maxwait = w;
- break;
- }
- else {
- /* process expired deadline */
- dns_send(ctx, q, now);
- }
- } while((q = qlist_first(&ctx->dnsc_qactive)) != NULL);
-
- dns_request_utm(ctx, now); /* update timer with new deadline */
- return maxwait;
-}
-
-struct dns_resolve_data {
- int dnsrd_done;
- void *dnsrd_result;
-};
-
-static void dns_resolve_cb(struct dns_ctx *ctx, void *result, void *data) {
- struct dns_resolve_data *d = data;
- d->dnsrd_result = result;
- d->dnsrd_done = 1;
- ctx = ctx;
-}
-
-void *dns_resolve(struct dns_ctx *ctx, struct dns_query *q) {
- time_t now;
- struct dns_resolve_data d;
- int n;
- SETCTXOPEN(ctx);
-
- if (!q)
- return NULL;
-
- assert(ctx == q->dnsq_ctx);
- dns_assert_ctx(ctx);
- /* do not allow re-resolving syncronous queries */
- assert(q->dnsq_cbck != dns_resolve_cb && "can't resolve syncronous query");
- if (q->dnsq_cbck == dns_resolve_cb) {
- ctx->dnsc_qstatus = DNS_E_BADQUERY;
- return NULL;
- }
- q->dnsq_cbck = dns_resolve_cb;
- q->dnsq_cbdata = &d;
- d.dnsrd_done = 0;
-
- now = time(NULL);
- while(!d.dnsrd_done && (n = dns_timeouts(ctx, -1, now)) >= 0) {
-#ifdef HAVE_POLL
- struct pollfd pfd;
- pfd.fd = ctx->dnsc_udpsock;
- pfd.events = POLLIN;
- n = poll(&pfd, 1, n * 1000);
-#else
- fd_set rfd;
- struct timeval tv;
- FD_ZERO(&rfd);
- FD_SET(ctx->dnsc_udpsock, &rfd);
- tv.tv_sec = n; tv.tv_usec = 0;
- n = select(ctx->dnsc_udpsock + 1, &rfd, NULL, NULL, &tv);
-#endif
- now = time(NULL);
- if (n > 0)
- dns_ioevent(ctx, now);
- }
-
- return d.dnsrd_result;
-}
-
-void *dns_resolve_dn(struct dns_ctx *ctx,
- dnscc_t *dn, int qcls, int qtyp, int flags,
- dns_parse_fn *parse) {
- return
- dns_resolve(ctx,
- dns_submit_dn(ctx, dn, qcls, qtyp, flags, parse, NULL, NULL));
-}
-
-void *dns_resolve_p(struct dns_ctx *ctx,
- const char *name, int qcls, int qtyp, int flags,
- dns_parse_fn *parse) {
- return
- dns_resolve(ctx,
- dns_submit_p(ctx, name, qcls, qtyp, flags, parse, NULL, NULL));
-}
-
-int dns_cancel(struct dns_ctx *ctx, struct dns_query *q) {
- SETCTX(ctx);
- dns_assert_ctx(ctx);
- assert(q->dnsq_ctx == ctx);
- /* do not allow cancelling syncronous queries */
- assert(q->dnsq_cbck != dns_resolve_cb && "can't cancel syncronous query");
- if (q->dnsq_cbck == dns_resolve_cb)
- return (ctx->dnsc_qstatus = DNS_E_BADQUERY);
- qlist_remove(q);
- --ctx->dnsc_nactive;
- dns_request_utm(ctx, 0);
- return 0;
-}
-