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