cURL / Mailing Lists / curl-library / Single Mail

curl-library

[PATCH] Add first support for the 'unbound' DNSSEC validating resolver.

From: Björn Stenberg <bjorn_at_haxx.se>
Date: Wed, 26 Aug 2015 23:11:32 +0200

This patch adds the beginnings of support for a new resolver:
 unbound (https://www.unbound.net)

---
 acinclude.m4        |  11 ++
 configure.ac        |   1 +
 include/curl/curl.h |   3 +
 lib/Makefile.inc    |   1 +
 lib/asyn-unbound.c  | 411 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/curl_setup.h    |   6 +
 lib/url.c           |   7 +
 lib/urldata.h       |   2 +
 8 files changed, 442 insertions(+)
 create mode 100644 lib/asyn-unbound.c
diff --git a/acinclude.m4 b/acinclude.m4
index 782f32d..d049620 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -3114,3 +3114,14 @@ use vars qw(
 1;
 _EOF
 ])
+
+
+dnl CURL_CHECK_LIBS_UNBOUND
+dnl -------------------------------------------------
+dnl Check for libraries needed for unbound support,
+dnl and prepended to LIBS any needed libraries.
+
+AC_DEFUN([CURL_CHECK_LIBS_UNBOUND], [
+  #
+  AC_CHECK_LIB(unbound, ub_ctx_delete)
+  ])
diff --git a/configure.ac b/configure.ac
index d269210..5b7d5f5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -685,6 +685,7 @@ AC_HELP_STRING([--enable-libgcc],[use libgcc when linking]),
 )
 
 CURL_CHECK_LIB_XNET
+CURL_CHECK_LIBS_UNBOUND
 
 dnl gethostbyname without lib or in the nsl lib?
 AC_CHECK_FUNC(gethostbyname,
diff --git a/include/curl/curl.h b/include/curl/curl.h
index 11d90f0..0d7e744 100644
--- a/include/curl/curl.h
+++ b/include/curl/curl.h
@@ -1648,6 +1648,9 @@ typedef enum {
   /* Set the protocol used when curl is given a URL without a protocol */
   CINIT(DEFAULT_PROTOCOL, OBJECTPOINT, 238),
 
+  /* Specify a file containing DNSSEC trust anchor */
+  CINIT(DNSSEC_TA_FILE, OBJECTPOINT, 232),
+
   CURLOPT_LASTENTRY /* the last unused */
 } CURLoption;
 
diff --git a/lib/Makefile.inc b/lib/Makefile.inc
index d444a6b..4a2ba6a 100644
--- a/lib/Makefile.inc
+++ b/lib/Makefile.inc
@@ -42,6 +42,7 @@ LIB_CFILES = file.c timeval.c base64.c hostip.c progress.c formdata.c   \
   pingpong.c rtsp.c curl_threads.c warnless.c hmac.c curl_rtmp.c        \
   openldap.c curl_gethostname.c gopher.c idn_win32.c                    \
   http_negotiate_sspi.c http_proxy.c non-ascii.c asyn-ares.c            \
+  asyn-unbound.c \
   asyn-thread.c curl_gssapi.c curl_ntlm.c curl_ntlm_wb.c                \
   curl_ntlm_core.c curl_ntlm_msgs.c curl_sasl.c curl_multibyte.c        \
   hostcheck.c conncache.c pipeline.c dotdot.c x509asn1.c                \
diff --git a/lib/asyn-unbound.c b/lib/asyn-unbound.c
new file mode 100644
index 0000000..12796db
--- /dev/null
+++ b/lib/asyn-unbound.c
@@ -0,0 +1,411 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel_at_haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#undef USE_ARES
+#define USE_UNBOUND 1
+#include "curl_setup.h"
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef __VMS
+#include <in.h>
+#include <inet.h>
+#endif
+
+#ifdef HAVE_PROCESS_H
+#include <process.h>
+#endif
+
+#include <unbound.h>
+
+/* #ifdef CURLRES_ARES */
+
+#include "urldata.h"
+#include "sendf.h"
+#include "hostip.h"
+#include "hash.h"
+#include "share.h"
+#include "strerror.h"
+#include "url.h"
+#include "multiif.h"
+#include "inet_pton.h"
+#include "connect.h"
+#include "select.h"
+#include "progress.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+/* The last #include file should be: */
+#include "memdebug.h"
+
+struct ResolverResults {
+  int async_id;
+};
+
+static struct ub_ctx* unbound_ctx;
+
+/* This is called by ub_process() when resolution is completed */
+static void ubcallback(void* mydata, int err, struct ub_result* result)
+{
+  struct connectdata *conn = mydata;
+
+  conn->async.done = TRUE;
+
+  if(err != 0) {
+    failf(conn->data, "Unbound resolve error: %s\n", ub_strerror(err));
+    return;
+  }
+
+  /* show security status */
+  if(result->secure)
+    infof(conn->data, "DNS result is secure\n");
+  else {
+    if(result->bogus) {
+      infof(conn->data, "DNS %s\n", result->why_bogus);
+      failf(conn->data, "DNS validation failure");
+      conn->async.status = CURLE_COULDNT_RESOLVE_HOST;
+      goto free;
+    }
+    else
+      infof(conn->data, "DNS result is insecure\n");
+  }
+
+  /* show first result */
+  if(result->havedata) {
+    struct Curl_dns_entry *dns;
+
+    switch(result->qtype) {
+    case 1: /* A */
+    case 28: /* AAAA */
+      dns = calloc(sizeof(struct Curl_dns_entry), 1);
+      if(!dns) {
+        failf(conn->data, "Failed allocating dns entry!");
+        return;
+      }
+      dns->addr = Curl_ip2addr(result->qtype == 28 ? AF_INET6 : AF_INET,
+                               result->data[0],
+                               result->qname,
+                               conn->async.port);
+      conn->async.dns = dns;
+      conn->async.done = TRUE;
+      break;
+
+    case 52: /* TLSA */
+      infof(conn->data, "The TLSA of %s is %d %d + %d bytes\n",
+            result->qname,
+            result->data[0][0], result->data[0][1],
+            result->len[0] - 2);
+      break;
+    }
+  }
+  else {
+    if(result->nxdomain)
+      failf(conn->data, "No such domain: %s", result->qname);
+    else
+      failf(conn->data, "No %d data for %s", result->qtype, result->qname);
+    conn->async.done = TRUE;
+    conn->async.status = CURLE_COULDNT_RESOLVE_HOST;
+  }
+
+ free:
+  ub_resolve_free(result);
+}
+
+
+/*
+ * Curl_resolver_global_init() - the generic low-level asynchronous name
+ * resolve API.  Called from curl_global_init() to initialize global resolver
+ * environment.  Initializes ares library.
+ */
+int Curl_resolver_global_init(void)
+{
+  int err;
+
+  /* create context */
+  unbound_ctx = ub_ctx_create();
+  if(!unbound_ctx) {
+    return CURLE_FAILED_INIT;
+  }
+
+  /* read public keys for DNSSEC verification */
+  err=ub_ctx_add_ta_file(unbound_ctx, "unbound-keys");
+  if(err) {
+    return CURLE_FAILED_INIT;
+  }
+
+  return CURLE_OK;
+}
+
+/*
+ * Curl_resolver_global_cleanup()
+ *
+ * Called from curl_global_cleanup() to destroy global resolver environment.
+ * Deinitializes ares library.
+ */
+void Curl_resolver_global_cleanup(void)
+{
+  ub_ctx_delete(unbound_ctx);
+}
+
+/*
+ * Curl_resolver_init()
+ *
+ * Called from curl_easy_init() -> Curl_open() to initialize resolver
+ * URL-state specific environment ('resolver' member of the UrlState
+ * structure).  Fills the passed pointer by the initialized ares_channel.
+ */
+CURLcode Curl_resolver_init(void **resolver)
+{
+  (void)resolver;
+  return CURLE_OK;
+}
+
+/*
+ * Curl_resolver_cleanup()
+ *
+ * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver
+ * URL-state specific environment ('resolver' member of the UrlState
+ * structure).  Destroys the ares channel.
+ */
+void Curl_resolver_cleanup(void *resolver)
+{
+  (void)resolver;
+}
+
+/*
+ * Curl_resolver_duphandle()
+ *
+ * Called from curl_easy_duphandle() to duplicate resolver URL-state specific
+ * environment ('resolver' member of the UrlState structure).  Duplicates the
+ * 'from' ares channel and passes the resulting channel to the 'to' pointer.
+ */
+int Curl_resolver_duphandle(void **to, void *from)
+{
+  (void)to;
+  (void)from;
+  return CURLE_OK;
+}
+
+/*
+ * Cancel all possibly still on-going resolves for this connection.
+ */
+void Curl_resolver_cancel(struct connectdata *conn)
+{
+  if(conn && conn->data && conn->async.os_specific) {
+    struct ResolverResults *res = conn->async.os_specific;
+    ub_cancel(unbound_ctx, res->async_id);
+  }
+}
+
+/*
+ * Curl_resolver_getsock() is called when someone from the outside world
+ * (using curl_multi_fdset()) wants to get our fd_set setup and we're talking
+ * with ares. The caller must make sure that this function is only called when
+ * we have a working ares channel.
+ *
+ * Returns: sockets-in-use-bitmap
+ */
+
+int Curl_resolver_getsock(struct connectdata *conn,
+                          curl_socket_t *socks,
+                          int numsocks)
+
+{
+  (void)conn;
+  (void)socks;
+  (void)numsocks;
+  return ub_fd(unbound_ctx);
+}
+
+/*
+ * Curl_resolver_is_resolved() is called repeatedly to check if a previous
+ * name resolve request has completed. It should also make sure to time-out if
+ * the operation seems to take too long.
+ *
+ * Returns normal CURLcode errors.
+ */
+CURLcode Curl_resolver_is_resolved(struct connectdata *conn,
+                                   struct Curl_dns_entry **dns)
+{
+  int rc = ub_process(unbound_ctx);
+  if(rc) {
+    failf(conn->data, "unbound error %d", rc);
+    return CURLE_FAILED_INIT;
+  }
+
+  if(conn->async.done) {
+    *dns = conn->async.dns;
+    return conn->async.status;
+  }
+  else {
+    *dns = NULL; /* no results yet */
+  }
+  return CURLE_OK;
+}
+
+/*
+ * Curl_resolver_wait_resolv()
+ *
+ * waits for a resolve to finish. This function should be avoided since using
+ * this risk getting the multi interface to "hang".
+ *
+ * If 'entry' is non-NULL, make it point to the resolved dns entry
+ *
+ * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, and
+ * CURLE_OPERATION_TIMEDOUT if a time-out occurred.
+ */
+CURLcode Curl_resolver_wait_resolv(struct connectdata *conn,
+                                   struct Curl_dns_entry **dns)
+{
+  while(!conn->async.done) {
+    struct timeval now = Curl_tvnow();
+    long to = Curl_timeleft(conn->data, &now, TRUE);
+
+    int fd = ub_fd(unbound_ctx);
+
+    struct timeval timeout;
+    fd_set fdset;
+    int rc;
+    timeout.tv_sec  = 0;
+    timeout.tv_usec = to * 1000;
+    FD_ZERO(&fdset);
+    FD_SET(fd, &fdset);
+    rc = select(fd+1, &fdset, NULL, NULL, &timeout);
+    if(rc < 0) {
+      failf(conn->data, "select failed");
+      return CURLE_FAILED_INIT;
+    }
+    else {
+      if(!rc) {
+        /* timeout */
+        return CURLE_OPERATION_TIMEDOUT;
+      }
+      if(rc) {
+        infof(conn->data, "Process1...\n");
+        rc = Curl_resolver_is_resolved(conn, dns);
+        if(dns)
+          break;
+      }
+    }
+  }
+  return CURLE_OK;
+}
+
+Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn,
+                                         const char *hostname,
+                                         int port,
+                                         int *waitp)
+{
+  char *bufp;
+  int rrtype;
+
+  *waitp = 0; /* default to synchronous response, in case of error */
+
+  switch(conn->ip_version) {
+#ifdef ENABLE_IPV6
+  case CURL_IPRESOLVE_V6:
+    rrtype = 28; /* AAAA */
+    break;
+#endif
+  case CURL_IPRESOLVE_V4:
+  default:
+    rrtype = 1; /* A */
+    break;
+  }
+
+  bufp = strdup(hostname);
+  if(bufp) {
+    int ubrc;
+    struct ResolverResults *res = NULL;
+    Curl_safefree(conn->async.hostname);
+    conn->async.hostname = bufp;
+    conn->async.port = port;
+    conn->async.done = FALSE;   /* not done */
+    conn->async.status = 0;     /* clear */
+    conn->async.dns = NULL;     /* clear */
+    res = calloc(sizeof(struct ResolverResults), 1);
+    if(!res) {
+      Curl_safefree(conn->async.hostname);
+      conn->async.hostname = NULL;
+      return NULL;
+    }
+    conn->async.os_specific = res;
+
+    ubrc = ub_resolve_async(unbound_ctx,
+                            hostname,
+                            rrtype,
+                            1, /* CLASS IN (internet) */
+                            (void*)conn,
+                            ubcallback,
+                            &(res->async_id));
+    if(ubrc != 0) {
+      failf(conn->data, "resolve error: %s", ub_strerror(ubrc));
+      return NULL;
+    }
+    *waitp = 1; /* expect asynchronous response */
+  }
+  return NULL; /* no struct yet */
+}
+
+CURLcode Curl_set_dns_servers(struct SessionHandle *data,
+                              char *servers)
+{
+  (void)data;
+  (void)servers;
+  return CURLE_NOT_BUILT_IN;
+
+}
+
+CURLcode Curl_set_dns_interface(struct SessionHandle *data,
+                                const char *interf)
+{
+  (void)data;
+  (void)interf;
+  return CURLE_NOT_BUILT_IN;
+}
+
+CURLcode Curl_set_dns_local_ip4(struct SessionHandle *data,
+                                const char *local_ip4)
+{
+  (void)data;
+  (void)local_ip4;
+  return CURLE_NOT_BUILT_IN;
+}
+
+CURLcode Curl_set_dns_local_ip6(struct SessionHandle *data,
+                                const char *local_ip6)
+{
+  (void)data;
+  (void)local_ip6;
+  return CURLE_NOT_BUILT_IN;
+}
diff --git a/lib/curl_setup.h b/lib/curl_setup.h
index ab0c139..248ea71 100644
--- a/lib/curl_setup.h
+++ b/lib/curl_setup.h
@@ -531,6 +531,12 @@
 /* now undef the stock libc functions just to avoid them being used */
 #  undef HAVE_GETADDRINFO
 #  undef HAVE_GETHOSTBYNAME
+#elif defined(HAVE_LIBUNBOUND)
+#  define CURLRES_ASYNCH
+#  define CURLRES_UNBOUND
+/* now undef the stock libc functions just to avoid them being used */
+#  undef HAVE_GETADDRINFO
+#  undef HAVE_GETHOSTBYNAME
 #elif defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32)
 #  define CURLRES_ASYNCH
 #  define CURLRES_THREADED
diff --git a/lib/url.c b/lib/url.c
index d572f01..6998a65 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -2651,6 +2651,13 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
                        va_arg(param, char *));
     break;
 #endif
+  case CURLOPT_DNSSEC_TA_FILE:
+    /*
+     * Option to allow specify a file containing DNSSEC trust anchor
+     */
+    result = setstropt(&data->set.str[STRING_DNSSEC_TA_FILE],
+                       va_arg(param, char *));
+    break;
 
   case CURLOPT_PATH_AS_IS:
     data->set.path_as_is = (0 != va_arg(param, long))?TRUE:FALSE;
diff --git a/lib/urldata.h b/lib/urldata.h
index 3207e61..97ad420 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -1417,6 +1417,8 @@ enum dupstring {
 
   STRING_COPYPOSTFIELDS,  /* if POST, set the fields' values here */
 
+  STRING_DNSSEC_TA_FILE,  /* DNSSEC trust anchor file */
+  /* -- end of strings -- */
   STRING_LAST /* not used, just an end-of-list marker */
 };
 
-- 
2.1.4
--NzB8fVQJ5HfG6fxh
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: base64
Content-Disposition: inline
LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0t
LS0tLS0tLS0tLQpMaXN0IGFkbWluOiBodHRwOi8vY29vbC5oYXh4LnNlL2xpc3QvbGlzdGluZm8v
Y3VybC1saWJyYXJ5CkV0aXF1ZXR0ZTogIGh0dHA6Ly9jdXJsLmhheHguc2UvbWFpbC9ldGlxdWV0
dGUuaHRtbA==
--NzB8fVQJ5HfG6fxh--
Received on 2001-09-17