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
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