aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/librdns/resolver.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/librdns/resolver.c')
-rw-r--r--contrib/librdns/resolver.c782
1 files changed, 782 insertions, 0 deletions
diff --git a/contrib/librdns/resolver.c b/contrib/librdns/resolver.c
new file mode 100644
index 000000000..9741f7bf9
--- /dev/null
+++ b/contrib/librdns/resolver.c
@@ -0,0 +1,782 @@
+/*
+ * Copyright (c) 2014, Vsevolod Stakhov
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdarg.h>
+
+#include "rdns.h"
+#include "dns_private.h"
+#include "ottery.h"
+#include "util.h"
+#include "packet.h"
+#include "parse.h"
+#include "logger.h"
+#include "compression.h"
+
+static int
+rdns_send_request (struct rdns_request *req, int fd, bool new_req)
+{
+ int r;
+ struct rdns_server *serv = req->io->srv;
+ struct rdns_resolver *resolver = req->resolver;
+ struct rdns_request *tmp;
+ struct dns_header *header;
+ const int max_id_cycles = 32;
+
+ /* Find ID collision */
+ if (new_req) {
+ r = 0;
+ HASH_FIND_INT (req->io->requests, &req->id, tmp);
+ while (tmp != NULL) {
+ /* Check for unique id */
+ header = (struct dns_header *)req->packet;
+ header->qid = rdns_permutor_generate_id ();
+ req->id = header->qid;
+ if (++r > max_id_cycles) {
+ return -1;
+ }
+ HASH_FIND_INT (req->io->requests, &req->id, tmp);
+ }
+ }
+
+ if (resolver->curve_plugin == NULL) {
+ r = send (fd, req->packet, req->pos, 0);
+ }
+ else {
+ r = resolver->curve_plugin->cb.curve_plugin.send_cb (req,
+ resolver->curve_plugin->data);
+ }
+ if (r == -1) {
+ if (errno == EAGAIN || errno == EINTR) {
+ if (new_req) {
+ /* Write when socket is ready */
+ HASH_ADD_INT (req->io->requests, id, req);
+ req->async_event = resolver->async->add_write (resolver->async->data,
+ fd, req);
+ }
+ /*
+ * If request is already processed then the calling function
+ * should take care about events processing
+ */
+ return 0;
+ }
+ else {
+ rdns_debug ("send failed: %s for server %s", strerror (errno), serv->name);
+ return -1;
+ }
+ }
+
+ if (new_req) {
+ /* Add request to hash table */
+ HASH_ADD_INT (req->io->requests, id, req);
+ /* Fill timeout */
+ req->async_event = resolver->async->add_timer (resolver->async->data,
+ req->timeout, req);
+ req->state = RDNS_REQUEST_SENT;
+ }
+
+ return 1;
+}
+
+
+static struct rdns_reply *
+rdns_make_reply (struct rdns_request *req, enum dns_rcode rcode)
+{
+ struct rdns_reply *rep;
+
+ rep = malloc (sizeof (struct rdns_reply));
+ if (rep != NULL) {
+ rep->request = req;
+ rep->resolver = req->resolver;
+ rep->entries = NULL;
+ rep->code = rcode;
+ req->reply = rep;
+ }
+
+ return rep;
+}
+
+static struct rdns_request *
+rdns_find_dns_request (uint8_t *in, struct rdns_io_channel *ioc)
+{
+ struct dns_header *header = (struct dns_header *)in;
+ struct rdns_request *req;
+ int id;
+ struct rdns_resolver *resolver = ioc->resolver;
+
+ id = header->qid;
+ HASH_FIND_INT (ioc->requests, &id, req);
+ if (req == NULL) {
+ /* No such requests found */
+ rdns_debug ("DNS request with id %d has not been found for IO channel", (int)id);
+ }
+
+ return req;
+}
+
+static bool
+rdns_parse_reply (uint8_t *in, int r, struct rdns_request *req,
+ struct rdns_reply **_rep)
+{
+ struct dns_header *header = (struct dns_header *)in;
+ struct rdns_reply *rep;
+ struct rdns_reply_entry *elt;
+ uint8_t *pos, *npos;
+ struct rdns_resolver *resolver = req->resolver;
+ uint16_t qdcount;
+ int type;
+ bool found = false;
+
+ int i, t;
+
+ /* First check header fields */
+ if (header->qr == 0) {
+ rdns_info ("got request while waiting for reply");
+ return false;
+ }
+
+ qdcount = ntohs (header->qdcount);
+
+ if (qdcount != req->qcount) {
+ rdns_info ("request has %d queries, reply has %d queries", (int)req->qcount, (int)header->qdcount);
+ return false;
+ }
+
+ /*
+ * Now we have request and query data is now at the end of header, so compare
+ * request QR section and reply QR section
+ */
+ req->pos = sizeof (struct dns_header);
+ pos = in + sizeof (struct dns_header);
+ t = r - sizeof (struct dns_header);
+ for (i = 0; i < (int)qdcount; i ++) {
+ if ((npos = rdns_request_reply_cmp (req, pos,t)) == NULL) {
+ rdns_info ("DNS request with id %d is for different query, ignoring", (int)req->id);
+ return false;
+ }
+ t -= npos - pos;
+ pos = npos;
+ }
+ /*
+ * Now pos is in answer section, so we should extract data and form reply
+ */
+ rep = rdns_make_reply (req, header->rcode);
+
+ if (rep == NULL) {
+ rdns_warn ("Cannot allocate memory for reply");
+ return false;
+ }
+
+ type = req->requested_names[0].type;
+
+ if (rep->code == RDNS_RC_NOERROR) {
+ r -= pos - in;
+ /* Extract RR records */
+ for (i = 0; i < ntohs (header->ancount); i ++) {
+ elt = malloc (sizeof (struct rdns_reply_entry));
+ t = rdns_parse_rr (resolver, in, elt, &pos, rep, &r);
+ if (t == -1) {
+ free (elt);
+ rdns_debug ("incomplete reply");
+ break;
+ }
+ else if (t == 1) {
+ DL_APPEND (rep->entries, elt);
+ if (elt->type == type) {
+ found = true;
+ }
+ }
+ else {
+ rdns_debug ("no matching reply for %s",
+ req->requested_names[0].name);
+ free (elt);
+ }
+ }
+ }
+
+ if (!found && type != RDNS_REQUEST_ANY) {
+ /* We have not found the requested RR type */
+ rep->code = RDNS_RC_NOREC;
+ }
+
+ *_rep = rep;
+ return true;
+}
+
+static void
+rdns_request_unschedule (struct rdns_request *req)
+{
+ req->async->del_timer (req->async->data,
+ req->async_event);
+ /* Remove from id hashes */
+ HASH_DEL (req->io->requests, req);
+}
+
+void
+rdns_process_read (int fd, void *arg)
+{
+ struct rdns_io_channel *ioc = arg;
+ struct rdns_resolver *resolver;
+ struct rdns_request *req = NULL;
+ ssize_t r;
+ struct rdns_reply *rep;
+ uint8_t in[UDP_PACKET_SIZE];
+
+ resolver = ioc->resolver;
+
+ /* First read packet from socket */
+ if (resolver->curve_plugin == NULL) {
+ r = read (fd, in, sizeof (in));
+ if (r > (int)(sizeof (struct dns_header) + sizeof (struct dns_query))) {
+ req = rdns_find_dns_request (in, ioc);
+ }
+ }
+ else {
+ r = resolver->curve_plugin->cb.curve_plugin.recv_cb (ioc, in,
+ sizeof (in), resolver->curve_plugin->data, &req);
+ if (req == NULL &&
+ r > (int)(sizeof (struct dns_header) + sizeof (struct dns_query))) {
+ req = rdns_find_dns_request (in, ioc);
+ }
+ }
+
+ if (req != NULL) {
+ if (rdns_parse_reply (in, r, req, &rep)) {
+ UPSTREAM_OK (req->io->srv);
+ req->state = RDNS_REQUEST_REPLIED;
+ rdns_request_unschedule (req);
+ req->func (rep, req->arg);
+ REF_RELEASE (req);
+ }
+ }
+ else {
+ /* Still want to increase uses */
+ ioc->uses ++;
+ }
+}
+
+void
+rdns_process_timer (void *arg)
+{
+ struct rdns_request *req = (struct rdns_request *)arg;
+ struct rdns_reply *rep;
+ int r;
+ bool renew = false;
+ struct rdns_resolver *resolver;
+ struct rdns_server *serv = NULL;
+
+ req->retransmits --;
+ resolver = req->resolver;
+
+ if (req->retransmits == 0) {
+ UPSTREAM_FAIL (req->io->srv, time (NULL));
+ rep = rdns_make_reply (req, RDNS_RC_TIMEOUT);
+ req->state = RDNS_REQUEST_REPLIED;
+ rdns_request_unschedule (req);
+ req->func (rep, req->arg);
+ REF_RELEASE (req);
+
+ return;
+ }
+
+ if (!req->io->active) {
+ /* Do not reschedule IO requests on inactive sockets */
+ rdns_debug ("reschedule request with id: %d", (int)req->id);
+ rdns_request_unschedule (req);
+ REF_RELEASE (req->io);
+
+ UPSTREAM_SELECT_ROUND_ROBIN (resolver->servers, serv);
+
+ if (serv == NULL) {
+ rdns_warn ("cannot find suitable server for request");
+ rep = rdns_make_reply (req, RDNS_RC_SERVFAIL);
+ req->state = RDNS_REQUEST_REPLIED;
+ req->func (rep, req->arg);
+ REF_RELEASE (req);
+ }
+
+ /* Select random IO channel */
+ req->io = serv->io_channels[ottery_rand_uint32 () % serv->io_cnt];
+ req->io->uses ++;
+ REF_RETAIN (req->io);
+ renew = true;
+ }
+
+ r = rdns_send_request (req, req->io->sock, renew);
+ if (r == 0) {
+ /* Retransmit one more time */
+ req->async->del_timer (req->async->data,
+ req->async_event);
+ req->async_event = req->async->add_write (req->async->data,
+ req->io->sock, req);
+ req->state = RDNS_REQUEST_REGISTERED;
+ }
+ else if (r == -1) {
+ UPSTREAM_FAIL (req->io->srv, time (NULL));
+ rep = rdns_make_reply (req, RDNS_RC_NETERR);
+ req->state = RDNS_REQUEST_REPLIED;
+ rdns_request_unschedule (req);
+ req->func (rep, req->arg);
+ REF_RELEASE (req);
+ }
+ else {
+ req->async->repeat_timer (req->async->data, req->async_event);
+ }
+}
+
+static void
+rdns_process_periodic (void *arg)
+{
+ struct rdns_resolver *resolver = (struct rdns_resolver*)arg;
+
+ UPSTREAM_RESCAN (resolver->servers, time (NULL));
+}
+
+static void
+rdns_process_ioc_refresh (void *arg)
+{
+ struct rdns_resolver *resolver = (struct rdns_resolver*)arg;
+ struct rdns_server *serv;
+ struct rdns_io_channel *ioc, *nioc;
+ unsigned int i;
+
+ if (resolver->max_ioc_uses > 0) {
+ UPSTREAM_FOREACH (resolver->servers, serv) {
+ for (i = 0; i < serv->io_cnt; i ++) {
+ ioc = serv->io_channels[i];
+ if (ioc->uses > resolver->max_ioc_uses) {
+ /* Schedule IOC removing */
+ nioc = calloc (1, sizeof (struct rdns_io_channel));
+ if (nioc == NULL) {
+ rdns_err ("calloc fails to allocate rdns_io_channel");
+ continue;
+ }
+ nioc->sock = rdns_make_client_socket (serv->name, serv->port,
+ SOCK_DGRAM);
+ if (nioc->sock == -1) {
+ rdns_err ("cannot open socket to %s: %s", serv->name,
+ strerror (errno));
+ free (nioc);
+ continue;
+ }
+ nioc->srv = serv;
+ nioc->active = true;
+ nioc->resolver = resolver;
+ nioc->async_io = resolver->async->add_read (resolver->async->data,
+ nioc->sock, nioc);
+ REF_INIT_RETAIN (nioc, rdns_ioc_free);
+ serv->io_channels[i] = nioc;
+ rdns_debug ("scheduled io channel for server %s to be refreshed after "
+ "%lu usages", serv->name, (unsigned long)ioc->uses);
+ ioc->active = false;
+ REF_RELEASE (ioc);
+ }
+ }
+ }
+ }
+}
+
+void
+rdns_process_retransmit (int fd, void *arg)
+{
+ struct rdns_request *req = (struct rdns_request *)arg;
+ struct rdns_resolver *resolver;
+ struct rdns_reply *rep;
+ int r;
+
+ resolver = req->resolver;
+
+ resolver->async->del_write (resolver->async->data,
+ req->async_event);
+
+ r = rdns_send_request (req, fd, false);
+
+ if (r == 0) {
+ /* Retransmit one more time */
+ req->async_event = req->async->add_write (req->async->data,
+ fd, req);
+ req->state = RDNS_REQUEST_REGISTERED;
+ }
+ else if (r == -1) {
+ UPSTREAM_FAIL (req->io->srv, time (NULL));
+ rep = rdns_make_reply (req, RDNS_RC_NETERR);
+ req->state = RDNS_REQUEST_REPLIED;
+ req->func (rep, req->arg);
+ REF_RELEASE (req);
+ }
+ else {
+ req->async_event = req->async->add_timer (req->async->data,
+ req->timeout, req);
+ req->state = RDNS_REQUEST_SENT;
+ }
+}
+
+struct rdns_request*
+rdns_make_request_full (
+ struct rdns_resolver *resolver,
+ dns_callback_type cb,
+ void *cbdata,
+ double timeout,
+ unsigned int repeats,
+ unsigned int queries,
+ ...
+ )
+{
+ va_list args;
+ struct rdns_request *req;
+ struct rdns_server *serv;
+ int r, type;
+ unsigned int i, tlen = 0, clen = 0, cur;
+ size_t olen;
+ const char *cur_name, *last_name = NULL;
+ struct rdns_compression_entry *comp = NULL;
+
+ if (!resolver->initialized) {
+ return NULL;
+ }
+
+ req = malloc (sizeof (struct rdns_request));
+ if (req == NULL) {
+ return NULL;
+ }
+
+ req->resolver = resolver;
+ req->func = cb;
+ req->arg = cbdata;
+ req->reply = NULL;
+ req->qcount = queries;
+ req->io = NULL;
+ req->state = RDNS_REQUEST_NEW;
+ req->packet = NULL;
+ req->requested_names = calloc (queries, sizeof (struct rdns_request_name));
+ if (req->requested_names == NULL) {
+ free (req);
+ return NULL;
+ }
+
+ req->type = 0;
+#ifdef TWEETNACL
+ req->curve_plugin_data = NULL;
+#endif
+ REF_INIT_RETAIN (req, rdns_request_free);
+
+ /* Calculate packet's total length based on records count */
+ va_start (args, queries);
+ for (i = 0; i < queries * 2; i += 2) {
+ cur = i / 2;
+ cur_name = va_arg (args, const char *);
+ if (cur_name != NULL) {
+ last_name = cur_name;
+ clen = strlen (cur_name);
+ if (clen == 0) {
+ rdns_info ("got empty name to resolve");
+ rdns_request_free (req);
+ return NULL;
+ }
+ tlen += clen;
+ }
+ else if (last_name == NULL) {
+ rdns_info ("got NULL as the first name to resolve");
+ rdns_request_free (req);
+ return NULL;
+ }
+
+ if (!rdns_format_dns_name (resolver, last_name, clen,
+ &req->requested_names[cur].name, &olen)) {
+ rdns_request_free (req);
+ return NULL;
+ }
+
+ type = va_arg (args, int);
+ req->requested_names[cur].type = type;
+ req->requested_names[cur].len = olen;
+ }
+ va_end (args);
+
+ rdns_allocate_packet (req, tlen);
+ rdns_make_dns_header (req, queries);
+
+ for (i = 0; i < queries; i ++) {
+ cur_name = req->requested_names[i].name;
+ clen = req->requested_names[i].len;
+ type = req->requested_names[i].type;
+ if (queries > 1) {
+ if (!rdns_add_rr (req, cur_name, clen, type, &comp)) {
+ REF_RELEASE (req);
+ rnds_compression_free (comp);
+ return NULL;
+ }
+ }
+ else {
+ if (!rdns_add_rr (req, cur_name, clen, type, NULL)) {
+ REF_RELEASE (req);
+ rnds_compression_free (comp);
+ return NULL;
+ }
+ }
+ }
+
+ rnds_compression_free (comp);
+
+ /* Add EDNS RR */
+ rdns_add_edns0 (req);
+
+ req->retransmits = repeats;
+ req->timeout = timeout;
+ req->state = RDNS_REQUEST_NEW;
+ req->async = resolver->async;
+
+ UPSTREAM_SELECT_ROUND_ROBIN (resolver->servers, serv);
+
+ if (serv == NULL) {
+ rdns_warn ("cannot find suitable server for request");
+ REF_RELEASE (req);
+ return NULL;
+ }
+
+ /* Select random IO channel */
+ req->io = serv->io_channels[ottery_rand_uint32 () % serv->io_cnt];
+ req->io->uses ++;
+
+ /* Now send request to server */
+ r = rdns_send_request (req, req->io->sock, true);
+
+ if (r == -1) {
+ REF_RELEASE (req);
+ return NULL;
+ }
+
+ REF_RETAIN (req->io);
+ REF_RETAIN (req->resolver);
+
+ return req;
+}
+
+bool
+rdns_resolver_init (struct rdns_resolver *resolver)
+{
+ unsigned int i;
+ struct rdns_server *serv;
+ struct rdns_io_channel *ioc;
+
+ if (!resolver->async_binded) {
+ return false;
+ }
+
+ if (resolver->servers == NULL) {
+ return false;
+ }
+
+ /* Now init io channels to all servers */
+ UPSTREAM_FOREACH (resolver->servers, serv) {
+ serv->io_channels = calloc (serv->io_cnt, sizeof (struct rdns_io_channel *));
+ for (i = 0; i < serv->io_cnt; i ++) {
+ ioc = calloc (1, sizeof (struct rdns_io_channel));
+ if (ioc == NULL) {
+ rdns_err ("cannot allocate memory for the resolver");
+ return false;
+ }
+ ioc->sock = rdns_make_client_socket (serv->name, serv->port, SOCK_DGRAM);
+ ioc->active = true;
+ if (ioc->sock == -1) {
+ rdns_err ("cannot open socket to %s:%d %s", serv->name, serv->port, strerror (errno));
+ free (ioc);
+ return false;
+ }
+ else {
+ ioc->srv = serv;
+ ioc->resolver = resolver;
+ ioc->async_io = resolver->async->add_read (resolver->async->data,
+ ioc->sock, ioc);
+ REF_INIT_RETAIN (ioc, rdns_ioc_free);
+ serv->io_channels[i] = ioc;
+ }
+ }
+ }
+
+ if (resolver->async->add_periodic) {
+ resolver->periodic = resolver->async->add_periodic (resolver->async->data,
+ UPSTREAM_REVIVE_TIME, rdns_process_periodic, resolver);
+ }
+
+ resolver->initialized = true;
+
+ return true;
+}
+
+void
+rdns_resolver_register_plugin (struct rdns_resolver *resolver,
+ struct rdns_plugin *plugin)
+{
+ if (resolver != NULL && plugin != NULL) {
+ /* XXX: support only network plugin now, and only a single one */
+ if (plugin->type == RDNS_PLUGIN_CURVE) {
+ resolver->curve_plugin = plugin;
+ }
+ }
+}
+
+bool
+rdns_resolver_add_server (struct rdns_resolver *resolver,
+ const char *name, unsigned int port,
+ int priority, unsigned int io_cnt)
+{
+ struct rdns_server *serv;
+ union {
+ struct in_addr v4;
+ struct in6_addr v6;
+ } addr;
+
+ if (inet_pton (AF_INET, name, &addr) == 0 &&
+ inet_pton (AF_INET6, name, &addr) == 0) {
+ /* Invalid IP */
+ return false;
+ }
+
+ if (io_cnt == 0) {
+ return false;
+ }
+ if (port == 0 || port > UINT16_MAX) {
+ return false;
+ }
+
+ serv = calloc (1, sizeof (struct rdns_server));
+ if (serv == NULL) {
+ return false;
+ }
+ serv->name = strdup (name);
+ if (serv->name == NULL) {
+ free (serv);
+ return false;
+ }
+
+ serv->io_cnt = io_cnt;
+ serv->port = port;
+
+ UPSTREAM_ADD (resolver->servers, serv, priority);
+
+ return true;
+}
+
+void
+rdns_resolver_set_logger (struct rdns_resolver *resolver,
+ rdns_log_function logger, void *log_data)
+{
+ resolver->logger = logger;
+ resolver->log_data = log_data;
+}
+
+void
+rdns_resolver_set_log_level (struct rdns_resolver *resolver,
+ enum rdns_log_level level)
+{
+ resolver->log_level = level;
+}
+
+
+void
+rdns_resolver_set_max_io_uses (struct rdns_resolver *resolver,
+ uint64_t max_ioc_uses, double check_time)
+{
+ if (resolver->refresh_ioc_periodic != NULL) {
+ resolver->async->del_periodic (resolver->async->data,
+ resolver->refresh_ioc_periodic);
+ resolver->refresh_ioc_periodic = NULL;
+ }
+
+ resolver->max_ioc_uses = max_ioc_uses;
+ if (check_time > 0.0 && resolver->async->add_periodic) {
+ resolver->refresh_ioc_periodic =
+ resolver->async->add_periodic (resolver->async->data,
+ check_time, rdns_process_ioc_refresh, resolver);
+ }
+}
+
+static void
+rdns_resolver_free (struct rdns_resolver *resolver)
+{
+ struct rdns_server *serv, *stmp;
+ struct rdns_io_channel *ioc;
+ unsigned int i;
+
+ if (resolver->initialized) {
+ if (resolver->periodic != NULL) {
+ resolver->async->del_periodic (resolver->async->data, resolver->periodic);
+ }
+ if (resolver->refresh_ioc_periodic != NULL) {
+ resolver->async->del_periodic (resolver->async->data,
+ resolver->refresh_ioc_periodic);
+ }
+ if (resolver->curve_plugin != NULL && resolver->curve_plugin->dtor != NULL) {
+ resolver->curve_plugin->dtor (resolver, resolver->curve_plugin->data);
+ }
+ /* Stop IO watch on all IO channels */
+ UPSTREAM_FOREACH_SAFE (resolver->servers, serv, stmp) {
+ for (i = 0; i < serv->io_cnt; i ++) {
+ ioc = serv->io_channels[i];
+ REF_RELEASE (ioc);
+ }
+ serv->io_cnt = 0;
+ UPSTREAM_DEL (resolver->servers, serv);
+ free (serv->io_channels);
+ free (serv->name);
+ free (serv);
+ }
+ }
+ free (resolver->async);
+ free (resolver);
+}
+
+
+struct rdns_resolver *
+rdns_resolver_new (void)
+{
+ struct rdns_resolver *new;
+
+ new = calloc (1, sizeof (struct rdns_resolver));
+
+ REF_INIT_RETAIN (new, rdns_resolver_free);
+
+ new->logger = rdns_logger_internal;
+ new->log_data = new;
+
+ return new;
+}
+
+void
+rdns_resolver_async_bind (struct rdns_resolver *resolver,
+ struct rdns_async_context *ctx)
+{
+ if (resolver != NULL && ctx != NULL) {
+ resolver->async = ctx;
+ resolver->async_binded = true;
+ }
+}