mirror of
https://github.com/moparisthebest/curl
synced 2025-02-28 17:31:46 -05:00
parent
3f3b26d6fe
commit
abff183387
@ -199,6 +199,8 @@ Bind connection locally to port range. See \fICURLOPT_LOCALPORTRANGE(3)\fP
|
|||||||
Timeout for DNS cache. See \fICURLOPT_DNS_CACHE_TIMEOUT(3)\fP
|
Timeout for DNS cache. See \fICURLOPT_DNS_CACHE_TIMEOUT(3)\fP
|
||||||
.IP CURLOPT_DNS_USE_GLOBAL_CACHE
|
.IP CURLOPT_DNS_USE_GLOBAL_CACHE
|
||||||
OBSOLETE Enable global DNS cache. See \fICURLOPT_DNS_USE_GLOBAL_CACHE(3)\fP
|
OBSOLETE Enable global DNS cache. See \fICURLOPT_DNS_USE_GLOBAL_CACHE(3)\fP
|
||||||
|
.IP CURLOPT_DOH_URL
|
||||||
|
Use this DOH server for name resolves. See \fICURLOPT_DOH_URL(3)\fP
|
||||||
.IP CURLOPT_BUFFERSIZE
|
.IP CURLOPT_BUFFERSIZE
|
||||||
Ask for alternate buffer size. See \fICURLOPT_BUFFERSIZE(3)\fP
|
Ask for alternate buffer size. See \fICURLOPT_BUFFERSIZE(3)\fP
|
||||||
.IP CURLOPT_PORT
|
.IP CURLOPT_PORT
|
||||||
|
71
docs/libcurl/opts/CURLOPT_DOH_URL.3
Normal file
71
docs/libcurl/opts/CURLOPT_DOH_URL.3
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
.\" **************************************************************************
|
||||||
|
.\" * _ _ ____ _
|
||||||
|
.\" * Project ___| | | | _ \| |
|
||||||
|
.\" * / __| | | | |_) | |
|
||||||
|
.\" * | (__| |_| | _ <| |___
|
||||||
|
.\" * \___|\___/|_| \_\_____|
|
||||||
|
.\" *
|
||||||
|
.\" * Copyright (C) 2018, Daniel Stenberg, <daniel@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 https://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.
|
||||||
|
.\" *
|
||||||
|
.\" **************************************************************************
|
||||||
|
.\"
|
||||||
|
.TH CURLOPT_DOH_URL 3 "18 Jun 2018" "libcurl 7.62.0" "curl_easy_setopt options"
|
||||||
|
.SH NAME
|
||||||
|
CURLOPT_DOH_URL \- provide the DNS-over-HTTPS URL
|
||||||
|
.SH SYNOPSIS
|
||||||
|
#include <curl/curl.h>
|
||||||
|
|
||||||
|
CURLcode curl_easy_setopt(CURL *handle, CURLOPT_DOH_URL, char *URL);
|
||||||
|
.SH DESCRIPTION
|
||||||
|
Pass in a pointer to a \fIURL\fP for the DOH server to use for name
|
||||||
|
resolving. The parameter should be a char * to a zero terminated string which
|
||||||
|
must be URL-encoded in the following format: "https://host:port/path". It MUST
|
||||||
|
specify a HTTPS URL.
|
||||||
|
|
||||||
|
libcurl doesn't validate the syntax or use this variable until the transfer is
|
||||||
|
issued. Even if you set a crazy value here, \fIcurl_easy_setopt(3)\fP will
|
||||||
|
still return \fICURLE_OK\fP.
|
||||||
|
|
||||||
|
curl sends POST requests to the given DNS-over-HTTPS URL.
|
||||||
|
|
||||||
|
To find the DOH server itself, which might be specified using a name, libcurl
|
||||||
|
will use the default name lookup function. You can bootstrap that by providing
|
||||||
|
the address for the DOH server with \fICURLOPT_RESOLVE(3)\fP.
|
||||||
|
|
||||||
|
Disable DOH use again by setting this option to NULL.
|
||||||
|
.SH DEFAULT
|
||||||
|
NULL - there is no default DOH URL. If this option isn't set, libcurl will use
|
||||||
|
the default name resolver.
|
||||||
|
.SH PROTOCOLS
|
||||||
|
All
|
||||||
|
.SH EXAMPLE
|
||||||
|
.nf
|
||||||
|
CURL *curl = curl_easy_init();
|
||||||
|
if(curl) {
|
||||||
|
curl_easy_setopt(curl, CURLOPT_URL, "http://example.com");
|
||||||
|
curl_easy_setopt(curl, CURLOPT_DOH_URL, "https://dns.example.com");
|
||||||
|
curl_easy_perform(curl);
|
||||||
|
}
|
||||||
|
.fi
|
||||||
|
.SH AVAILABILITY
|
||||||
|
Added in 7.62.0
|
||||||
|
.SH RETURN VALUE
|
||||||
|
Returns CURLE_OK on success or CURLE_OUT_OF_MEMORY if there was insufficient
|
||||||
|
heap space.
|
||||||
|
|
||||||
|
Note that \fIcurl_easy_setopt(3)\fP won't actually parse the given string so
|
||||||
|
given a bad DOH URL, curl will not detect a problem until it tries to resolve
|
||||||
|
a name with it.
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
.BR CURLOPT_VERBOSE "(3), " CURLOPT_RESOLVE "(3), "
|
@ -122,6 +122,7 @@ man_MANS = \
|
|||||||
CURLOPT_DNS_SERVERS.3 \
|
CURLOPT_DNS_SERVERS.3 \
|
||||||
CURLOPT_DNS_SHUFFLE_ADDRESSES.3 \
|
CURLOPT_DNS_SHUFFLE_ADDRESSES.3 \
|
||||||
CURLOPT_DNS_USE_GLOBAL_CACHE.3 \
|
CURLOPT_DNS_USE_GLOBAL_CACHE.3 \
|
||||||
|
CURLOPT_DOH_URL.3 \
|
||||||
CURLOPT_EGDSOCKET.3 \
|
CURLOPT_EGDSOCKET.3 \
|
||||||
CURLOPT_ERRORBUFFER.3 \
|
CURLOPT_ERRORBUFFER.3 \
|
||||||
CURLOPT_EXPECT_100_TIMEOUT_MS.3 \
|
CURLOPT_EXPECT_100_TIMEOUT_MS.3 \
|
||||||
|
@ -384,6 +384,7 @@ CURLOPT_DNS_LOCAL_IP6 7.33.0
|
|||||||
CURLOPT_DNS_SERVERS 7.24.0
|
CURLOPT_DNS_SERVERS 7.24.0
|
||||||
CURLOPT_DNS_SHUFFLE_ADDRESSES 7.60.0
|
CURLOPT_DNS_SHUFFLE_ADDRESSES 7.60.0
|
||||||
CURLOPT_DNS_USE_GLOBAL_CACHE 7.9.3 7.11.1
|
CURLOPT_DNS_USE_GLOBAL_CACHE 7.9.3 7.11.1
|
||||||
|
CURLOPT_DOH_URL 7.62.0
|
||||||
CURLOPT_EGDSOCKET 7.7
|
CURLOPT_EGDSOCKET 7.7
|
||||||
CURLOPT_ENCODING 7.10
|
CURLOPT_ENCODING 7.10
|
||||||
CURLOPT_ERRORBUFFER 7.1
|
CURLOPT_ERRORBUFFER 7.1
|
||||||
|
@ -1859,6 +1859,9 @@ typedef enum {
|
|||||||
/* Disallow specifying username/login in URL. */
|
/* Disallow specifying username/login in URL. */
|
||||||
CINIT(DISALLOW_USERNAME_IN_URL, LONG, 278),
|
CINIT(DISALLOW_USERNAME_IN_URL, LONG, 278),
|
||||||
|
|
||||||
|
/* DNS-over-HTTPS URL */
|
||||||
|
CINIT(DOH_URL, STRINGPOINT, 279),
|
||||||
|
|
||||||
CURLOPT_LASTENTRY /* the last unused */
|
CURLOPT_LASTENTRY /* the last unused */
|
||||||
} CURLoption;
|
} CURLoption;
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* | (__| |_| | _ <| |___
|
* | (__| |_| | _ <| |___
|
||||||
* \___|\___/|_| \_\_____|
|
* \___|\___/|_| \_\_____|
|
||||||
*
|
*
|
||||||
* Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
|
* Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||||
*
|
*
|
||||||
* This software is licensed as described in the file COPYING, which
|
* This software is licensed as described in the file COPYING, which
|
||||||
* you should have received as part of this distribution. The terms
|
* you should have received as part of this distribution. The terms
|
||||||
@ -269,6 +269,7 @@ _CURL_WARNING(_curl_easy_getinfo_err_curl_off_t,
|
|||||||
(option) == CURLOPT_DNS_LOCAL_IP4 || \
|
(option) == CURLOPT_DNS_LOCAL_IP4 || \
|
||||||
(option) == CURLOPT_DNS_LOCAL_IP6 || \
|
(option) == CURLOPT_DNS_LOCAL_IP6 || \
|
||||||
(option) == CURLOPT_DNS_SERVERS || \
|
(option) == CURLOPT_DNS_SERVERS || \
|
||||||
|
(option) == CURLOPT_DOH_URL || \
|
||||||
(option) == CURLOPT_EGDSOCKET || \
|
(option) == CURLOPT_EGDSOCKET || \
|
||||||
(option) == CURLOPT_FTPPORT || \
|
(option) == CURLOPT_FTPPORT || \
|
||||||
(option) == CURLOPT_FTP_ACCOUNT || \
|
(option) == CURLOPT_FTP_ACCOUNT || \
|
||||||
@ -500,7 +501,8 @@ _CURL_WARNING(_curl_easy_getinfo_err_curl_off_t,
|
|||||||
/* evaluates to true if expr can be passed as POST data (void* or char*) */
|
/* evaluates to true if expr can be passed as POST data (void* or char*) */
|
||||||
#define _curl_is_postfields(expr) \
|
#define _curl_is_postfields(expr) \
|
||||||
(_curl_is_ptr((expr), void) || \
|
(_curl_is_ptr((expr), void) || \
|
||||||
_curl_is_arr((expr), char))
|
_curl_is_arr((expr), char) || \
|
||||||
|
_curl_is_arr((expr), unsigned char))
|
||||||
|
|
||||||
/* FIXME: the whole callback checking is messy...
|
/* FIXME: the whole callback checking is messy...
|
||||||
* The idea is to tolerate char vs. void and const vs. not const
|
* The idea is to tolerate char vs. void and const vs. not const
|
||||||
|
@ -54,7 +54,8 @@ LIB_CFILES = file.c timeval.c base64.c hostip.c progress.c formdata.c \
|
|||||||
http_ntlm.c curl_ntlm_wb.c curl_ntlm_core.c curl_sasl.c rand.c \
|
http_ntlm.c curl_ntlm_wb.c curl_ntlm_core.c curl_sasl.c rand.c \
|
||||||
curl_multibyte.c hostcheck.c conncache.c pipeline.c dotdot.c \
|
curl_multibyte.c hostcheck.c conncache.c pipeline.c dotdot.c \
|
||||||
x509asn1.c http2.c smb.c curl_endian.c curl_des.c system_win32.c \
|
x509asn1.c http2.c smb.c curl_endian.c curl_des.c system_win32.c \
|
||||||
mime.c sha256.c setopt.c curl_path.c curl_ctype.c curl_range.c psl.c
|
mime.c sha256.c setopt.c curl_path.c curl_ctype.c curl_range.c psl.c \
|
||||||
|
doh.c
|
||||||
|
|
||||||
LIB_HFILES = arpa_telnet.h netrc.h file.h timeval.h hostip.h progress.h \
|
LIB_HFILES = arpa_telnet.h netrc.h file.h timeval.h hostip.h progress.h \
|
||||||
formdata.h cookie.h http.h sendf.h ftp.h url.h dict.h if2ip.h \
|
formdata.h cookie.h http.h sendf.h ftp.h url.h dict.h if2ip.h \
|
||||||
@ -74,7 +75,7 @@ LIB_HFILES = arpa_telnet.h netrc.h file.h timeval.h hostip.h progress.h \
|
|||||||
curl_setup_once.h multihandle.h setup-vms.h pipeline.h dotdot.h \
|
curl_setup_once.h multihandle.h setup-vms.h pipeline.h dotdot.h \
|
||||||
x509asn1.h http2.h sigpipe.h smb.h curl_endian.h curl_des.h \
|
x509asn1.h http2.h sigpipe.h smb.h curl_endian.h curl_des.h \
|
||||||
curl_printf.h system_win32.h rand.h mime.h curl_sha256.h setopt.h \
|
curl_printf.h system_win32.h rand.h mime.h curl_sha256.h setopt.h \
|
||||||
curl_path.h curl_ctype.h curl_range.h psl.h
|
curl_path.h curl_ctype.h curl_range.h psl.h doh.h
|
||||||
|
|
||||||
LIB_RCFILES = libcurl.rc
|
LIB_RCFILES = libcurl.rc
|
||||||
|
|
||||||
|
915
lib/doh.c
Normal file
915
lib/doh.c
Normal file
@ -0,0 +1,915 @@
|
|||||||
|
/***************************************************************************
|
||||||
|
* _ _ ____ _
|
||||||
|
* Project ___| | | | _ \| |
|
||||||
|
* / __| | | | |_) | |
|
||||||
|
* | (__| |_| | _ <| |___
|
||||||
|
* \___|\___/|_| \_\_____|
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018, Daniel Stenberg, <daniel@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 https://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.
|
||||||
|
*
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#include "curl_setup.h"
|
||||||
|
#include "urldata.h"
|
||||||
|
#include "curl_addrinfo.h"
|
||||||
|
#include "sendf.h"
|
||||||
|
#include "multiif.h"
|
||||||
|
#include "url.h"
|
||||||
|
#include "share.h"
|
||||||
|
#include "curl_base64.h"
|
||||||
|
#include "connect.h"
|
||||||
|
#include "doh.h"
|
||||||
|
/* The last 3 #include files should be in this order */
|
||||||
|
#include "curl_printf.h"
|
||||||
|
#include "curl_memory.h"
|
||||||
|
#include "memdebug.h"
|
||||||
|
|
||||||
|
#define DNS_CLASS_IN 0x01
|
||||||
|
#define DOH_MAX_RESPONSE_SIZE 3000 /* bytes */
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
DNS_TYPE_A = 1,
|
||||||
|
DNS_TYPE_NS = 2,
|
||||||
|
DNS_TYPE_CNAME = 5,
|
||||||
|
DNS_TYPE_AAAA = 28
|
||||||
|
} DNStype;
|
||||||
|
|
||||||
|
#define MAX_ADDR 24
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
DOH_OK,
|
||||||
|
DOH_DNS_BAD_LABEL, /* 1 */
|
||||||
|
DOH_DNS_OUT_OF_RANGE, /* 2 */
|
||||||
|
DOH_DNS_LABEL_LOOP, /* 3 */
|
||||||
|
DOH_TOO_SMALL_BUFFER, /* 4 */
|
||||||
|
DOH_OUT_OF_MEM, /* 5 */
|
||||||
|
DOH_DNS_RDATA_LEN, /* 6 */
|
||||||
|
DOH_DNS_MALFORMAT, /* 7 */
|
||||||
|
DOH_DNS_BAD_RCODE, /* 8 - no such name */
|
||||||
|
DOH_DNS_UNEXPECTED_TYPE, /* 9 */
|
||||||
|
DOH_DNS_UNEXPECTED_CLASS, /* 10 */
|
||||||
|
DOH_NO_CONTENT, /* 11 */
|
||||||
|
DOH_DNS_BAD_ID /* 12 */
|
||||||
|
} DOHcode;
|
||||||
|
|
||||||
|
static const char * const errors[]={
|
||||||
|
"",
|
||||||
|
"Bad label",
|
||||||
|
"Out of range",
|
||||||
|
"Label loop",
|
||||||
|
"Too small",
|
||||||
|
"Out of memory",
|
||||||
|
"RDATA length",
|
||||||
|
"Malformat",
|
||||||
|
"Bad RCODE",
|
||||||
|
"Unexpected TYPE",
|
||||||
|
"Unexpected CLASS",
|
||||||
|
"No content",
|
||||||
|
"Bad ID"
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *doh_strerror(DOHcode code)
|
||||||
|
{
|
||||||
|
if((code >= DOH_OK) && (code <= DOH_DNS_BAD_ID))
|
||||||
|
return errors[code];
|
||||||
|
return "bad error code";
|
||||||
|
}
|
||||||
|
|
||||||
|
static DOHcode doh_encode(const char *host,
|
||||||
|
DNStype dnstype,
|
||||||
|
unsigned char *dnsp, /* buffer */
|
||||||
|
size_t len, /* buffer size */
|
||||||
|
size_t *olen) /* output length */
|
||||||
|
{
|
||||||
|
size_t hostlen = strlen(host);
|
||||||
|
unsigned char *orig = dnsp;
|
||||||
|
const char *hostp = host;
|
||||||
|
|
||||||
|
if(len < (12 + hostlen + 4))
|
||||||
|
return DOH_TOO_SMALL_BUFFER;
|
||||||
|
|
||||||
|
*dnsp++ = 0; /* 16 bit id */
|
||||||
|
*dnsp++ = 0;
|
||||||
|
*dnsp++ = 0x01; /* |QR| Opcode |AA|TC|RD| Set the RD bit */
|
||||||
|
*dnsp++ = '\0'; /* |RA| Z | RCODE | */
|
||||||
|
*dnsp++ = '\0';
|
||||||
|
*dnsp++ = 1; /* QDCOUNT (number of entries in the question section) */
|
||||||
|
*dnsp++ = '\0';
|
||||||
|
*dnsp++ = '\0'; /* ANCOUNT */
|
||||||
|
*dnsp++ = '\0';
|
||||||
|
*dnsp++ = '\0'; /* NSCOUNT */
|
||||||
|
*dnsp++ = '\0';
|
||||||
|
*dnsp++ = '\0'; /* ARCOUNT */
|
||||||
|
|
||||||
|
/* store a QNAME */
|
||||||
|
do {
|
||||||
|
char *dot = strchr(hostp, '.');
|
||||||
|
size_t labellen;
|
||||||
|
bool found = false;
|
||||||
|
if(dot) {
|
||||||
|
found = true;
|
||||||
|
labellen = dot - hostp;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
labellen = strlen(hostp);
|
||||||
|
if(labellen > 63)
|
||||||
|
/* too long label, error out */
|
||||||
|
return DOH_DNS_BAD_LABEL;
|
||||||
|
*dnsp++ = (unsigned char)labellen;
|
||||||
|
memcpy(dnsp, hostp, labellen);
|
||||||
|
dnsp += labellen;
|
||||||
|
hostp += labellen + 1;
|
||||||
|
if(!found) {
|
||||||
|
*dnsp++ = 0; /* terminating zero */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while(1);
|
||||||
|
|
||||||
|
*dnsp++ = '\0'; /* upper 8 bit TYPE */
|
||||||
|
*dnsp++ = (unsigned char)dnstype;
|
||||||
|
*dnsp++ = '\0'; /* upper 8 bit CLASS */
|
||||||
|
*dnsp++ = DNS_CLASS_IN; /* IN - "the Internet" */
|
||||||
|
|
||||||
|
*olen = dnsp - orig;
|
||||||
|
return DOH_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t
|
||||||
|
doh_write_cb(void *contents, size_t size, size_t nmemb, void *userp)
|
||||||
|
{
|
||||||
|
size_t realsize = size * nmemb;
|
||||||
|
struct dohresponse *mem = (struct dohresponse *)userp;
|
||||||
|
|
||||||
|
if((mem->size + realsize) > DOH_MAX_RESPONSE_SIZE)
|
||||||
|
/* suspiciously much for us */
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
mem->memory = realloc(mem->memory, mem->size + realsize);
|
||||||
|
if(mem->memory == NULL)
|
||||||
|
/* out of memory! */
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
memcpy(&(mem->memory[mem->size]), contents, realsize);
|
||||||
|
mem->size += realsize;
|
||||||
|
|
||||||
|
return realsize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* called from multi.c when this DOH transfer is complete */
|
||||||
|
static int Curl_doh_done(struct Curl_easy *doh, CURLcode result)
|
||||||
|
{
|
||||||
|
struct Curl_easy *data = doh->set.dohfor;
|
||||||
|
/* so one of the DOH request done for the 'data' transfer is now complete! */
|
||||||
|
data->req.doh.pending--;
|
||||||
|
infof(data, "a DOH request is completed, %d to go\n", data->req.doh.pending);
|
||||||
|
if(result)
|
||||||
|
infof(data, "DOH request %s\n", curl_easy_strerror(result));
|
||||||
|
|
||||||
|
if(!data->req.doh.pending) {
|
||||||
|
/* DOH completed */
|
||||||
|
curl_slist_free_all(data->req.doh.headers);
|
||||||
|
data->req.doh.headers = NULL;
|
||||||
|
Curl_expire(data, 0, EXPIRE_RUN_NOW);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ERROR_CHECK_SETOPT(x,y) result = curl_easy_setopt(doh, x, y); \
|
||||||
|
if(result) goto error
|
||||||
|
|
||||||
|
static CURLcode dohprobe(struct Curl_easy *data,
|
||||||
|
struct dnsprobe *p, DNStype dnstype,
|
||||||
|
const char *host,
|
||||||
|
const char *url, CURLM *multi,
|
||||||
|
struct curl_slist *headers)
|
||||||
|
{
|
||||||
|
struct Curl_easy *doh = NULL;
|
||||||
|
char *nurl = NULL;
|
||||||
|
CURLcode result = CURLE_OK;
|
||||||
|
timediff_t timeout_ms;
|
||||||
|
DOHcode d = doh_encode(host, dnstype, p->dohbuffer, sizeof(p->dohbuffer),
|
||||||
|
&p->dohlen);
|
||||||
|
if(d) {
|
||||||
|
failf(data, "Failed to encode DOH packet [%d]\n", d);
|
||||||
|
return CURLE_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
p->dnstype = dnstype;
|
||||||
|
p->serverdoh.memory = NULL;
|
||||||
|
/* the memory will be grown as needed by realloc in the doh_write_cb
|
||||||
|
function */
|
||||||
|
p->serverdoh.size = 0;
|
||||||
|
|
||||||
|
/* Note: this is code for sending the DoH request with GET but there's still
|
||||||
|
no logic that actually enables this. We should either add that ability or
|
||||||
|
yank out the GET code. Discuss! */
|
||||||
|
if(data->set.doh_get) {
|
||||||
|
char *b64;
|
||||||
|
size_t b64len;
|
||||||
|
result = Curl_base64url_encode(data, (char *)p->dohbuffer, p->dohlen,
|
||||||
|
&b64, &b64len);
|
||||||
|
if(result)
|
||||||
|
goto error;
|
||||||
|
nurl = aprintf("%s?dns=%s", url, b64);
|
||||||
|
free(b64);
|
||||||
|
if(!nurl) {
|
||||||
|
result = CURLE_OUT_OF_MEMORY;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
url = nurl;
|
||||||
|
}
|
||||||
|
|
||||||
|
timeout_ms = Curl_timeleft(data, NULL, TRUE);
|
||||||
|
|
||||||
|
/* Curl_open() is the internal version of curl_easy_init() */
|
||||||
|
result = Curl_open(&doh);
|
||||||
|
if(!result) {
|
||||||
|
ERROR_CHECK_SETOPT(CURLOPT_URL, url);
|
||||||
|
ERROR_CHECK_SETOPT(CURLOPT_WRITEFUNCTION, doh_write_cb);
|
||||||
|
ERROR_CHECK_SETOPT(CURLOPT_WRITEDATA, (void *)&p->serverdoh);
|
||||||
|
if(!data->set.doh_get) {
|
||||||
|
ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDS, &p->dohbuffer[0]);
|
||||||
|
ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDSIZE, p->dohlen);
|
||||||
|
}
|
||||||
|
ERROR_CHECK_SETOPT(CURLOPT_HTTPHEADER, headers);
|
||||||
|
ERROR_CHECK_SETOPT(CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS);
|
||||||
|
#ifndef CURLDEBUG
|
||||||
|
/* enforce HTTPS if not debug */
|
||||||
|
ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTPS);
|
||||||
|
#endif
|
||||||
|
ERROR_CHECK_SETOPT(CURLOPT_TIMEOUT_MS, (long)timeout_ms);
|
||||||
|
ERROR_CHECK_SETOPT(CURLOPT_VERBOSE, 1L);
|
||||||
|
doh->set.fmultidone = Curl_doh_done;
|
||||||
|
doh->set.dohfor = data; /* identify for which transfer this is done */
|
||||||
|
p->easy = doh;
|
||||||
|
|
||||||
|
/* add this transfer to the multi handle */
|
||||||
|
if(curl_multi_add_handle(multi, doh))
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
goto error;
|
||||||
|
free(nurl);
|
||||||
|
return CURLE_OK;
|
||||||
|
|
||||||
|
error:
|
||||||
|
free(nurl);
|
||||||
|
Curl_close(doh);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Curl_doh() resolves a name using DOH. It resolves a name and returns a
|
||||||
|
* 'Curl_addrinfo *' with the address information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
Curl_addrinfo *Curl_doh(struct connectdata *conn,
|
||||||
|
const char *hostname,
|
||||||
|
int port,
|
||||||
|
int *waitp)
|
||||||
|
{
|
||||||
|
struct Curl_easy *data = conn->data;
|
||||||
|
CURLcode result = CURLE_OK;
|
||||||
|
*waitp = TRUE; /* this never returns synchronously */
|
||||||
|
(void)conn;
|
||||||
|
(void)hostname;
|
||||||
|
(void)port;
|
||||||
|
|
||||||
|
/* start clean, consider allocating this struct on demand */
|
||||||
|
memset(&data->req.doh, 0, sizeof(struct dohdata));
|
||||||
|
|
||||||
|
data->req.doh.host = hostname;
|
||||||
|
data->req.doh.port = port;
|
||||||
|
data->req.doh.headers =
|
||||||
|
curl_slist_append(NULL,
|
||||||
|
"Content-Type: application/dns-message");
|
||||||
|
if(!data->req.doh.headers)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if(conn->ip_version != CURL_IPRESOLVE_V6) {
|
||||||
|
/* create IPv4 DOH request */
|
||||||
|
result = dohprobe(data, &data->req.doh.probe[0], DNS_TYPE_A,
|
||||||
|
hostname, data->set.str[STRING_DOH],
|
||||||
|
data->multi, data->req.doh.headers);
|
||||||
|
if(result)
|
||||||
|
goto error;
|
||||||
|
data->req.doh.pending++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(conn->ip_version != CURL_IPRESOLVE_V4) {
|
||||||
|
/* create IPv6 DOH request */
|
||||||
|
result = dohprobe(data, &data->req.doh.probe[1], DNS_TYPE_AAAA,
|
||||||
|
hostname, data->set.str[STRING_DOH],
|
||||||
|
data->multi, data->req.doh.headers);
|
||||||
|
if(result)
|
||||||
|
goto error;
|
||||||
|
data->req.doh.pending++;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
error:
|
||||||
|
curl_slist_free_all(data->req.doh.headers);
|
||||||
|
data->req.doh.headers = NULL;
|
||||||
|
curl_easy_cleanup(data->req.doh.probe[0].easy);
|
||||||
|
data->req.doh.probe[0].easy = NULL;
|
||||||
|
curl_easy_cleanup(data->req.doh.probe[1].easy);
|
||||||
|
data->req.doh.probe[1].easy = NULL;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DOHcode skipqname(unsigned char *doh, size_t dohlen,
|
||||||
|
unsigned int *indexp)
|
||||||
|
{
|
||||||
|
unsigned char length;
|
||||||
|
do {
|
||||||
|
if(dohlen < (*indexp + 1))
|
||||||
|
return DOH_DNS_OUT_OF_RANGE;
|
||||||
|
length = doh[*indexp];
|
||||||
|
if((length & 0xc0) == 0xc0) {
|
||||||
|
/* name pointer, advance over it and be done */
|
||||||
|
if(dohlen < (*indexp + 2))
|
||||||
|
return DOH_DNS_OUT_OF_RANGE;
|
||||||
|
*indexp += 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(length & 0xc0)
|
||||||
|
return DOH_DNS_BAD_LABEL;
|
||||||
|
if(dohlen < (*indexp + 1 + length))
|
||||||
|
return DOH_DNS_OUT_OF_RANGE;
|
||||||
|
*indexp += 1 + length;
|
||||||
|
} while(length);
|
||||||
|
return DOH_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned short get16bit(unsigned char *doh, int index)
|
||||||
|
{
|
||||||
|
return (unsigned short)((doh[index] << 8) | doh[index + 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int get32bit(unsigned char *doh, int index)
|
||||||
|
{
|
||||||
|
return (doh[index] << 24) | (doh[index + 1] << 16) |
|
||||||
|
(doh[index + 2] << 8) | doh[index + 3];
|
||||||
|
}
|
||||||
|
|
||||||
|
struct addr6 {
|
||||||
|
unsigned char byte[16];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct cnamestore {
|
||||||
|
size_t len; /* length of cname */
|
||||||
|
char *alloc; /* allocated pointer */
|
||||||
|
size_t allocsize; /* allocated size */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct dohaddr {
|
||||||
|
int type;
|
||||||
|
union {
|
||||||
|
unsigned int v4;
|
||||||
|
struct addr6 v6;
|
||||||
|
} ip;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct dohentry {
|
||||||
|
unsigned int ttl;
|
||||||
|
int numaddr;
|
||||||
|
struct dohaddr addr[MAX_ADDR];
|
||||||
|
int numcname;
|
||||||
|
struct cnamestore cname[MAX_ADDR];
|
||||||
|
};
|
||||||
|
|
||||||
|
static DOHcode store_a(unsigned char *doh, int index, struct dohentry *d)
|
||||||
|
{
|
||||||
|
/* silently ignore addresses over the limit */
|
||||||
|
if(d->numaddr < MAX_ADDR) {
|
||||||
|
struct dohaddr *a = &d->addr[d->numaddr];
|
||||||
|
a->type = DNS_TYPE_A;
|
||||||
|
a->ip.v4 = ntohl(get32bit(doh, index));
|
||||||
|
d->numaddr++;
|
||||||
|
}
|
||||||
|
return DOH_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DOHcode store_aaaa(unsigned char *doh, int index, struct dohentry *d)
|
||||||
|
{
|
||||||
|
/* silently ignore addresses over the limit */
|
||||||
|
if(d->numaddr < MAX_ADDR) {
|
||||||
|
struct dohaddr *a = &d->addr[d->numaddr];
|
||||||
|
struct addr6 *inet6p = &a->ip.v6;
|
||||||
|
a->type = DNS_TYPE_AAAA;
|
||||||
|
memcpy(inet6p, &doh[index], 16);
|
||||||
|
d->numaddr++;
|
||||||
|
}
|
||||||
|
return DOH_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DOHcode cnameappend(struct cnamestore *c,
|
||||||
|
unsigned char *src,
|
||||||
|
size_t len)
|
||||||
|
{
|
||||||
|
if(!c->alloc) {
|
||||||
|
c->allocsize = len + 1;
|
||||||
|
c->alloc = malloc(c->allocsize);
|
||||||
|
if(!c->alloc)
|
||||||
|
return DOH_OUT_OF_MEM;
|
||||||
|
}
|
||||||
|
else if(c->allocsize < (c->allocsize + len + 1)) {
|
||||||
|
char *ptr;
|
||||||
|
c->allocsize += len + 1;
|
||||||
|
ptr = realloc(c->alloc, c->allocsize);
|
||||||
|
if(!ptr) {
|
||||||
|
free(c->alloc);
|
||||||
|
return DOH_OUT_OF_MEM;
|
||||||
|
}
|
||||||
|
c->alloc = ptr;
|
||||||
|
}
|
||||||
|
memcpy(&c->alloc[c->len], src, len);
|
||||||
|
c->len += len;
|
||||||
|
c->alloc[c->len] = 0; /* keep it zero terminated */
|
||||||
|
return DOH_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DOHcode store_cname(unsigned char *doh,
|
||||||
|
size_t dohlen,
|
||||||
|
unsigned int index,
|
||||||
|
struct dohentry *d)
|
||||||
|
{
|
||||||
|
struct cnamestore *c = &d->cname[d->numcname++];
|
||||||
|
unsigned int loop = 128; /* a valid DNS name can never loop this much */
|
||||||
|
unsigned char length;
|
||||||
|
do {
|
||||||
|
if(index >= dohlen)
|
||||||
|
return DOH_DNS_OUT_OF_RANGE;
|
||||||
|
length = doh[index];
|
||||||
|
if((length & 0xc0) == 0xc0) {
|
||||||
|
int newpos;
|
||||||
|
/* name pointer, get the new offset (14 bits) */
|
||||||
|
if((index + 1) >= dohlen)
|
||||||
|
return DOH_DNS_OUT_OF_RANGE;
|
||||||
|
|
||||||
|
/* move to the the new index */
|
||||||
|
newpos = (length & 0x3f) << 8 | doh[index + 1];
|
||||||
|
index = newpos;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if(length & 0xc0)
|
||||||
|
return DOH_DNS_BAD_LABEL; /* bad input */
|
||||||
|
else
|
||||||
|
index++;
|
||||||
|
|
||||||
|
if(length) {
|
||||||
|
DOHcode rc;
|
||||||
|
if(c->len) {
|
||||||
|
rc = cnameappend(c, (unsigned char *)".", 1);
|
||||||
|
if(rc)
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
if((index + length) > dohlen)
|
||||||
|
return DOH_DNS_BAD_LABEL;
|
||||||
|
|
||||||
|
rc = cnameappend(c, &doh[index], length);
|
||||||
|
if(rc)
|
||||||
|
return rc;
|
||||||
|
index += length;
|
||||||
|
}
|
||||||
|
} while(length && --loop);
|
||||||
|
|
||||||
|
if(!loop)
|
||||||
|
return DOH_DNS_LABEL_LOOP;
|
||||||
|
return DOH_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DOHcode rdata(unsigned char *doh,
|
||||||
|
size_t dohlen,
|
||||||
|
unsigned short rdlength,
|
||||||
|
unsigned short type,
|
||||||
|
int index,
|
||||||
|
struct dohentry *d)
|
||||||
|
{
|
||||||
|
/* RDATA
|
||||||
|
- A (TYPE 1): 4 bytes
|
||||||
|
- AAAA (TYPE 28): 16 bytes
|
||||||
|
- NS (TYPE 2): N bytes */
|
||||||
|
DOHcode rc;
|
||||||
|
|
||||||
|
switch(type) {
|
||||||
|
case DNS_TYPE_A:
|
||||||
|
if(rdlength != 4)
|
||||||
|
return DOH_DNS_RDATA_LEN;
|
||||||
|
rc = store_a(doh, index, d);
|
||||||
|
if(rc)
|
||||||
|
return rc;
|
||||||
|
break;
|
||||||
|
case DNS_TYPE_AAAA:
|
||||||
|
if(rdlength != 16)
|
||||||
|
return DOH_DNS_RDATA_LEN;
|
||||||
|
rc = store_aaaa(doh, index, d);
|
||||||
|
if(rc)
|
||||||
|
return rc;
|
||||||
|
break;
|
||||||
|
case DNS_TYPE_CNAME:
|
||||||
|
rc = store_cname(doh, dohlen, index, d);
|
||||||
|
if(rc)
|
||||||
|
return rc;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* unsupported type, just skip it */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return DOH_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DOHcode doh_decode(unsigned char *doh,
|
||||||
|
size_t dohlen,
|
||||||
|
DNStype dnstype,
|
||||||
|
struct dohentry *d)
|
||||||
|
{
|
||||||
|
unsigned char rcode;
|
||||||
|
unsigned short qdcount;
|
||||||
|
unsigned short ancount;
|
||||||
|
unsigned short type = 0;
|
||||||
|
unsigned short class;
|
||||||
|
unsigned short rdlength;
|
||||||
|
unsigned short nscount;
|
||||||
|
unsigned short arcount;
|
||||||
|
unsigned int index = 12;
|
||||||
|
DOHcode rc;
|
||||||
|
|
||||||
|
d->ttl = INT_MAX;
|
||||||
|
|
||||||
|
if(dohlen < 12)
|
||||||
|
return DOH_TOO_SMALL_BUFFER; /* too small */
|
||||||
|
if(doh[0] || doh[1])
|
||||||
|
return DOH_DNS_BAD_ID; /* bad ID */
|
||||||
|
rcode = doh[3] & 0x0f;
|
||||||
|
if(rcode)
|
||||||
|
return DOH_DNS_BAD_RCODE; /* bad rcode */
|
||||||
|
|
||||||
|
qdcount = get16bit(doh, 4);
|
||||||
|
while(qdcount) {
|
||||||
|
rc = skipqname(doh, dohlen, &index);
|
||||||
|
if(rc)
|
||||||
|
return rc; /* bad qname */
|
||||||
|
if(dohlen < (index + 4))
|
||||||
|
return DOH_DNS_OUT_OF_RANGE;
|
||||||
|
index += 4; /* skip question's type and class */
|
||||||
|
qdcount--;
|
||||||
|
}
|
||||||
|
|
||||||
|
ancount = get16bit(doh, 6);
|
||||||
|
while(ancount) {
|
||||||
|
unsigned int ttl;
|
||||||
|
|
||||||
|
rc = skipqname(doh, dohlen, &index);
|
||||||
|
if(rc)
|
||||||
|
return rc; /* bad qname */
|
||||||
|
|
||||||
|
if(dohlen < (index + 2))
|
||||||
|
return DOH_DNS_OUT_OF_RANGE;
|
||||||
|
|
||||||
|
type = get16bit(doh, index);
|
||||||
|
if((type != DNS_TYPE_CNAME) && (type != dnstype))
|
||||||
|
/* Not the same type as was asked for nor CNAME */
|
||||||
|
return DOH_DNS_UNEXPECTED_TYPE;
|
||||||
|
index += 2;
|
||||||
|
|
||||||
|
if(dohlen < (index + 2))
|
||||||
|
return DOH_DNS_OUT_OF_RANGE;
|
||||||
|
class = get16bit(doh, index);
|
||||||
|
if(DNS_CLASS_IN != class)
|
||||||
|
return DOH_DNS_UNEXPECTED_CLASS; /* unsupported */
|
||||||
|
index += 2;
|
||||||
|
|
||||||
|
if(dohlen < (index + 4))
|
||||||
|
return DOH_DNS_OUT_OF_RANGE;
|
||||||
|
|
||||||
|
ttl = get32bit(doh, index);
|
||||||
|
if(ttl < d->ttl)
|
||||||
|
d->ttl = ttl;
|
||||||
|
index += 4;
|
||||||
|
|
||||||
|
if(dohlen < (index + 2))
|
||||||
|
return DOH_DNS_OUT_OF_RANGE;
|
||||||
|
|
||||||
|
rdlength = get16bit(doh, index);
|
||||||
|
index += 2;
|
||||||
|
if(dohlen < (index + rdlength))
|
||||||
|
return DOH_DNS_OUT_OF_RANGE;
|
||||||
|
|
||||||
|
rc = rdata(doh, dohlen, rdlength, type, index, d);
|
||||||
|
if(rc)
|
||||||
|
return rc; /* bad rdata */
|
||||||
|
index += rdlength;
|
||||||
|
ancount--;
|
||||||
|
}
|
||||||
|
|
||||||
|
nscount = get16bit(doh, 8);
|
||||||
|
while(nscount) {
|
||||||
|
rc = skipqname(doh, dohlen, &index);
|
||||||
|
if(rc)
|
||||||
|
return rc; /* bad qname */
|
||||||
|
|
||||||
|
if(dohlen < (index + 8))
|
||||||
|
return DOH_DNS_OUT_OF_RANGE;
|
||||||
|
|
||||||
|
index += 2 + 2 + 4; /* type, class and ttl */
|
||||||
|
|
||||||
|
if(dohlen < (index + 2))
|
||||||
|
return DOH_DNS_OUT_OF_RANGE;
|
||||||
|
|
||||||
|
rdlength = get16bit(doh, index);
|
||||||
|
index += 2;
|
||||||
|
if(dohlen < (index + rdlength))
|
||||||
|
return DOH_DNS_OUT_OF_RANGE;
|
||||||
|
index += rdlength;
|
||||||
|
nscount--;
|
||||||
|
}
|
||||||
|
|
||||||
|
arcount = get16bit(doh, 10);
|
||||||
|
while(arcount) {
|
||||||
|
rc = skipqname(doh, dohlen, &index);
|
||||||
|
if(rc)
|
||||||
|
return rc; /* bad qname */
|
||||||
|
|
||||||
|
if(dohlen < (index + 8))
|
||||||
|
return DOH_DNS_OUT_OF_RANGE;
|
||||||
|
|
||||||
|
index += 2 + 2 + 4; /* type, class and ttl */
|
||||||
|
|
||||||
|
if(dohlen < (index + 2))
|
||||||
|
return DOH_DNS_OUT_OF_RANGE;
|
||||||
|
|
||||||
|
rdlength = get16bit(doh, index);
|
||||||
|
index += 2;
|
||||||
|
if(dohlen < (index + rdlength))
|
||||||
|
return DOH_DNS_OUT_OF_RANGE;
|
||||||
|
index += rdlength;
|
||||||
|
arcount--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(index != dohlen)
|
||||||
|
return DOH_DNS_MALFORMAT; /* something is wrong */
|
||||||
|
|
||||||
|
if((type != DNS_TYPE_NS) && !d->numcname && !d->numaddr)
|
||||||
|
/* nothing stored! */
|
||||||
|
return DOH_NO_CONTENT;
|
||||||
|
|
||||||
|
return DOH_OK; /* ok */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void showdoh(struct Curl_easy *data,
|
||||||
|
struct dohentry *d)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
infof(data, "TTL: %u seconds\n", d->ttl);
|
||||||
|
for(i = 0; i < d->numaddr; i++) {
|
||||||
|
struct dohaddr *a = &d->addr[i];
|
||||||
|
if(a->type == DNS_TYPE_A) {
|
||||||
|
infof(data, "DOH A: %d.%d.%d.%d\n",
|
||||||
|
a->ip.v4 & 0xff, (a->ip.v4>>8) & 0xff,
|
||||||
|
(a->ip.v4>>16) & 0xff, a->ip.v4 >>24);
|
||||||
|
}
|
||||||
|
else if(a->type == DNS_TYPE_AAAA) {
|
||||||
|
int j;
|
||||||
|
char buffer[128];
|
||||||
|
char *ptr;
|
||||||
|
size_t len;
|
||||||
|
snprintf(buffer, 128, "DOH AAAA: ");
|
||||||
|
ptr = &buffer[10];
|
||||||
|
len = 118;
|
||||||
|
for(j = 0; j < 16; j += 2) {
|
||||||
|
size_t l;
|
||||||
|
snprintf(ptr, len, "%s%02x%02x", j?":":"", d->addr[i].ip.v6.byte[j],
|
||||||
|
d->addr[i].ip.v6.byte[j + 1]);
|
||||||
|
l = strlen(ptr);
|
||||||
|
len -= l;
|
||||||
|
ptr += l;
|
||||||
|
}
|
||||||
|
infof(data, "%s\n", buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(i = 0; i < d->numcname; i++) {
|
||||||
|
infof(data, "CNAME: %s\n", d->cname[i].alloc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* doh2ai()
|
||||||
|
*
|
||||||
|
* This function returns a pointer to the first element of a newly allocated
|
||||||
|
* Curl_addrinfo struct linked list filled with the data from a set of DOH
|
||||||
|
* lookups. Curl_addrinfo is meant to work like the addrinfo struct does for
|
||||||
|
* a IPv6 stack, but usable also for IPv4, all hosts and environments.
|
||||||
|
*
|
||||||
|
* The memory allocated by this function *MUST* be free'd later on calling
|
||||||
|
* Curl_freeaddrinfo(). For each successful call to this function there
|
||||||
|
* must be an associated call later to Curl_freeaddrinfo().
|
||||||
|
*/
|
||||||
|
|
||||||
|
static Curl_addrinfo *
|
||||||
|
doh2ai(const struct dohentry *de, const char *hostname, int port)
|
||||||
|
{
|
||||||
|
Curl_addrinfo *ai;
|
||||||
|
Curl_addrinfo *prevai = NULL;
|
||||||
|
Curl_addrinfo *firstai = NULL;
|
||||||
|
struct sockaddr_in *addr;
|
||||||
|
#ifdef ENABLE_IPV6
|
||||||
|
struct sockaddr_in6 *addr6;
|
||||||
|
#endif
|
||||||
|
CURLcode result = CURLE_OK;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if(!de)
|
||||||
|
/* no input == no output! */
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
for(i = 0; i < de->numaddr; i++) {
|
||||||
|
size_t ss_size;
|
||||||
|
CURL_SA_FAMILY_T addrtype;
|
||||||
|
if(de->addr[i].type == DNS_TYPE_AAAA) {
|
||||||
|
#ifndef ENABLE_IPV6
|
||||||
|
/* we can't handle IPv6 addresses */
|
||||||
|
continue;
|
||||||
|
#else
|
||||||
|
ss_size = sizeof(struct sockaddr_in6);
|
||||||
|
addrtype = AF_INET6;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ss_size = sizeof(struct sockaddr_in);
|
||||||
|
addrtype = AF_INET;
|
||||||
|
}
|
||||||
|
|
||||||
|
ai = calloc(1, sizeof(Curl_addrinfo));
|
||||||
|
if(!ai) {
|
||||||
|
result = CURLE_OUT_OF_MEMORY;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ai->ai_canonname = strdup(hostname);
|
||||||
|
if(!ai->ai_canonname) {
|
||||||
|
result = CURLE_OUT_OF_MEMORY;
|
||||||
|
free(ai);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ai->ai_addr = calloc(1, ss_size);
|
||||||
|
if(!ai->ai_addr) {
|
||||||
|
result = CURLE_OUT_OF_MEMORY;
|
||||||
|
free(ai->ai_canonname);
|
||||||
|
free(ai);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!firstai)
|
||||||
|
/* store the pointer we want to return from this function */
|
||||||
|
firstai = ai;
|
||||||
|
|
||||||
|
if(prevai)
|
||||||
|
/* make the previous entry point to this */
|
||||||
|
prevai->ai_next = ai;
|
||||||
|
|
||||||
|
ai->ai_family = addrtype;
|
||||||
|
|
||||||
|
/* we return all names as STREAM, so when using this address for TFTP
|
||||||
|
the type must be ignored and conn->socktype be used instead! */
|
||||||
|
ai->ai_socktype = SOCK_STREAM;
|
||||||
|
|
||||||
|
ai->ai_addrlen = (curl_socklen_t)ss_size;
|
||||||
|
|
||||||
|
/* leave the rest of the struct filled with zero */
|
||||||
|
|
||||||
|
switch(ai->ai_family) {
|
||||||
|
case AF_INET:
|
||||||
|
addr = (void *)ai->ai_addr; /* storage area for this info */
|
||||||
|
|
||||||
|
memcpy(&addr->sin_addr, &de->addr[i].ip.v4, sizeof(struct in_addr));
|
||||||
|
addr->sin_family = (CURL_SA_FAMILY_T)addrtype;
|
||||||
|
addr->sin_port = htons((unsigned short)port);
|
||||||
|
break;
|
||||||
|
|
||||||
|
#ifdef ENABLE_IPV6
|
||||||
|
case AF_INET6:
|
||||||
|
addr6 = (void *)ai->ai_addr; /* storage area for this info */
|
||||||
|
|
||||||
|
memcpy(&addr6->sin6_addr, &de->addr[i].ip.v6, sizeof(struct in6_addr));
|
||||||
|
addr6->sin6_family = (CURL_SA_FAMILY_T)addrtype;
|
||||||
|
addr6->sin6_port = htons((unsigned short)port);
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
prevai = ai;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(result) {
|
||||||
|
Curl_freeaddrinfo(firstai);
|
||||||
|
firstai = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return firstai;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *type2name(DNStype dnstype)
|
||||||
|
{
|
||||||
|
return (dnstype == DNS_TYPE_A)?"A":"AAAA";
|
||||||
|
}
|
||||||
|
|
||||||
|
static void de_cleanup(struct dohentry *d)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
for(i = 0; i < d->numcname; i++) {
|
||||||
|
free(d->cname[i].alloc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CURLcode Curl_doh_is_resolved(struct connectdata *conn,
|
||||||
|
struct Curl_dns_entry **dnsp)
|
||||||
|
{
|
||||||
|
struct Curl_easy *data = conn->data;
|
||||||
|
*dnsp = NULL; /* defaults to no response */
|
||||||
|
|
||||||
|
if(!data->req.doh.probe[0].easy && !data->req.doh.probe[1].easy) {
|
||||||
|
failf(data, "Could not DOH-resolve: %s", conn->async.hostname);
|
||||||
|
return conn->bits.proxy?CURLE_COULDNT_RESOLVE_PROXY:
|
||||||
|
CURLE_COULDNT_RESOLVE_HOST;
|
||||||
|
}
|
||||||
|
else if(!data->req.doh.pending) {
|
||||||
|
DOHcode rc;
|
||||||
|
DOHcode rc2;
|
||||||
|
struct dohentry de;
|
||||||
|
struct Curl_dns_entry *dns;
|
||||||
|
struct Curl_addrinfo *ai;
|
||||||
|
/* remove DOH handles from multi handle and close them */
|
||||||
|
curl_multi_remove_handle(data->multi, data->req.doh.probe[0].easy);
|
||||||
|
Curl_close(data->req.doh.probe[0].easy);
|
||||||
|
curl_multi_remove_handle(data->multi, data->req.doh.probe[1].easy);
|
||||||
|
Curl_close(data->req.doh.probe[1].easy);
|
||||||
|
|
||||||
|
/* parse the responses, create the struct and return it! */
|
||||||
|
memset(&de, 0, sizeof(de));
|
||||||
|
rc = doh_decode(data->req.doh.probe[0].serverdoh.memory,
|
||||||
|
data->req.doh.probe[0].serverdoh.size,
|
||||||
|
data->req.doh.probe[0].dnstype,
|
||||||
|
&de);
|
||||||
|
free(data->req.doh.probe[0].serverdoh.memory);
|
||||||
|
if(rc) {
|
||||||
|
infof(data, "DOH: %s type %s for %s\n", doh_strerror(rc),
|
||||||
|
type2name(data->req.doh.probe[0].dnstype),
|
||||||
|
data->req.doh.host);
|
||||||
|
}
|
||||||
|
rc2 = doh_decode(data->req.doh.probe[1].serverdoh.memory,
|
||||||
|
data->req.doh.probe[1].serverdoh.size,
|
||||||
|
data->req.doh.probe[1].dnstype,
|
||||||
|
&de);
|
||||||
|
free(data->req.doh.probe[1].serverdoh.memory);
|
||||||
|
if(rc2) {
|
||||||
|
infof(data, "DOG: %s type %s for %s\n", doh_strerror(rc2),
|
||||||
|
type2name(data->req.doh.probe[1].dnstype),
|
||||||
|
data->req.doh.host);
|
||||||
|
}
|
||||||
|
if(!rc || !rc2) {
|
||||||
|
infof(data, "DOH Host name: %s\n", data->req.doh.host);
|
||||||
|
showdoh(data, &de);
|
||||||
|
|
||||||
|
ai = doh2ai(&de, data->req.doh.host, data->req.doh.port);
|
||||||
|
if(!ai) {
|
||||||
|
de_cleanup(&de);
|
||||||
|
return CURLE_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(data->share)
|
||||||
|
Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
|
||||||
|
|
||||||
|
/* we got a response, store it in the cache */
|
||||||
|
dns = Curl_cache_addr(data, ai, data->req.doh.host, data->req.doh.port);
|
||||||
|
|
||||||
|
if(data->share)
|
||||||
|
Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
|
||||||
|
|
||||||
|
de_cleanup(&de);
|
||||||
|
if(!dns)
|
||||||
|
/* returned failure, bail out nicely */
|
||||||
|
Curl_freeaddrinfo(ai);
|
||||||
|
else {
|
||||||
|
conn->async.dns = dns;
|
||||||
|
*dnsp = dns;
|
||||||
|
return CURLE_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
de_cleanup(&de);
|
||||||
|
|
||||||
|
return CURLE_COULDNT_RESOLVE_HOST;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CURLE_OK;
|
||||||
|
}
|
44
lib/doh.h
Normal file
44
lib/doh.h
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#ifndef HEADER_CURL_DOH_H
|
||||||
|
#define HEADER_CURL_DOH_H
|
||||||
|
/***************************************************************************
|
||||||
|
* _ _ ____ _
|
||||||
|
* Project ___| | | | _ \| |
|
||||||
|
* / __| | | | |_) | |
|
||||||
|
* | (__| |_| | _ <| |___
|
||||||
|
* \___|\___/|_| \_\_____|
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018, Daniel Stenberg, <daniel@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 https://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.
|
||||||
|
*
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#include "urldata.h"
|
||||||
|
#include "curl_addrinfo.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Curl_doh() resolve a name using DoH (DNS-over-HTTPS). It resolves a name
|
||||||
|
* and returns a 'Curl_addrinfo *' with the address information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
Curl_addrinfo *Curl_doh(struct connectdata *conn,
|
||||||
|
const char *hostname,
|
||||||
|
int port,
|
||||||
|
int *waitp);
|
||||||
|
|
||||||
|
CURLcode Curl_doh_is_resolved(struct connectdata *conn,
|
||||||
|
struct Curl_dns_entry **dns);
|
||||||
|
|
||||||
|
int Curl_doh_getsock(struct connectdata *conn, curl_socket_t *socks,
|
||||||
|
int numsocks);
|
||||||
|
|
||||||
|
#endif
|
@ -111,31 +111,6 @@ CURLcode Curl_addrinfo_callback(struct connectdata *conn,
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Call this function after Curl_connect() has returned async=TRUE and
|
|
||||||
then a successful name resolve has been received.
|
|
||||||
|
|
||||||
Note: this function disconnects and frees the conn data in case of
|
|
||||||
resolve failure */
|
|
||||||
CURLcode Curl_async_resolved(struct connectdata *conn,
|
|
||||||
bool *protocol_done)
|
|
||||||
{
|
|
||||||
CURLcode result;
|
|
||||||
|
|
||||||
if(conn->async.dns) {
|
|
||||||
conn->dns_entry = conn->async.dns;
|
|
||||||
conn->async.dns = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = Curl_setup_conn(conn, protocol_done);
|
|
||||||
|
|
||||||
if(result)
|
|
||||||
/* We're not allowed to return failure with memory left allocated
|
|
||||||
in the connectdata struct, free those here */
|
|
||||||
Curl_disconnect(conn->data, conn, TRUE); /* close the connection */
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Curl_getaddrinfo() is the generic low-level name resolve API within this
|
* Curl_getaddrinfo() is the generic low-level name resolve API within this
|
||||||
* source file. There are several versions of this function - for different
|
* source file. There are several versions of this function - for different
|
||||||
|
77
lib/hostip.c
77
lib/hostip.c
@ -60,6 +60,7 @@
|
|||||||
#include "url.h"
|
#include "url.h"
|
||||||
#include "inet_ntop.h"
|
#include "inet_ntop.h"
|
||||||
#include "multiif.h"
|
#include "multiif.h"
|
||||||
|
#include "doh.h"
|
||||||
#include "warnless.h"
|
#include "warnless.h"
|
||||||
/* The last 3 #include files should be in this order */
|
/* The last 3 #include files should be in this order */
|
||||||
#include "curl_printf.h"
|
#include "curl_printf.h"
|
||||||
@ -565,23 +566,27 @@ int Curl_resolv(struct connectdata *conn,
|
|||||||
return CURLRESOLV_ERROR;
|
return CURLRESOLV_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If Curl_getaddrinfo() returns NULL, 'respwait' might be set to a
|
if(data->set.doh) {
|
||||||
non-zero value indicating that we need to wait for the response to the
|
addr = Curl_doh(conn, hostname, port, &respwait);
|
||||||
resolve call */
|
}
|
||||||
addr = Curl_getaddrinfo(conn,
|
else {
|
||||||
|
/* If Curl_getaddrinfo() returns NULL, 'respwait' might be set to a
|
||||||
|
non-zero value indicating that we need to wait for the response to the
|
||||||
|
resolve call */
|
||||||
|
addr = Curl_getaddrinfo(conn,
|
||||||
#ifdef DEBUGBUILD
|
#ifdef DEBUGBUILD
|
||||||
(data->set.str[STRING_DEVICE]
|
(data->set.str[STRING_DEVICE]
|
||||||
&& !strcmp(data->set.str[STRING_DEVICE],
|
&& !strcmp(data->set.str[STRING_DEVICE],
|
||||||
"LocalHost"))?"localhost":
|
"LocalHost"))?"localhost":
|
||||||
#endif
|
#endif
|
||||||
hostname, port, &respwait);
|
hostname, port, &respwait);
|
||||||
|
}
|
||||||
if(!addr) {
|
if(!addr) {
|
||||||
if(respwait) {
|
if(respwait) {
|
||||||
/* the response to our resolve call will come asynchronously at
|
/* the response to our resolve call will come asynchronously at
|
||||||
a later time, good or bad */
|
a later time, good or bad */
|
||||||
/* First, check that we haven't received the info by now */
|
/* First, check that we haven't received the info by now */
|
||||||
result = Curl_resolver_is_resolved(conn, &dns);
|
result = Curl_resolv_check(conn, &dns);
|
||||||
if(result) /* error detected */
|
if(result) /* error detected */
|
||||||
return CURLRESOLV_ERROR;
|
return CURLRESOLV_ERROR;
|
||||||
if(dns)
|
if(dns)
|
||||||
@ -1053,3 +1058,55 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data)
|
|||||||
|
|
||||||
return CURLE_OK;
|
return CURLE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CURLcode Curl_resolv_check(struct connectdata *conn,
|
||||||
|
struct Curl_dns_entry **dns)
|
||||||
|
{
|
||||||
|
if(conn->data->set.doh)
|
||||||
|
return Curl_doh_is_resolved(conn, dns);
|
||||||
|
return Curl_resolver_is_resolved(conn, dns);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Curl_resolv_getsock(struct connectdata *conn,
|
||||||
|
curl_socket_t *socks,
|
||||||
|
int numsocks)
|
||||||
|
{
|
||||||
|
#ifdef CURLRES_ASYNCH
|
||||||
|
if(conn->data->set.doh)
|
||||||
|
/* nothing to wait for during DOH resolve, those handles have their own
|
||||||
|
sockets */
|
||||||
|
return GETSOCK_BLANK;
|
||||||
|
return Curl_resolver_getsock(conn, socks, numsocks);
|
||||||
|
#else
|
||||||
|
(void)conn;
|
||||||
|
(void)socks;
|
||||||
|
(void)numsocks;
|
||||||
|
return GETSOCK_BLANK;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Call this function after Curl_connect() has returned async=TRUE and
|
||||||
|
then a successful name resolve has been received.
|
||||||
|
|
||||||
|
Note: this function disconnects and frees the conn data in case of
|
||||||
|
resolve failure */
|
||||||
|
CURLcode Curl_once_resolved(struct connectdata *conn,
|
||||||
|
bool *protocol_done)
|
||||||
|
{
|
||||||
|
CURLcode result;
|
||||||
|
|
||||||
|
if(conn->async.dns) {
|
||||||
|
conn->dns_entry = conn->async.dns;
|
||||||
|
conn->async.dns = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = Curl_setup_conn(conn, protocol_done);
|
||||||
|
|
||||||
|
if(result)
|
||||||
|
/* We're not allowed to return failure with memory left allocated
|
||||||
|
in the connectdata struct, free those here */
|
||||||
|
Curl_disconnect(conn->data, conn, TRUE); /* close the connection */
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
13
lib/hostip.h
13
lib/hostip.h
@ -145,12 +145,7 @@ int curl_dogetnameinfo(GETNAMEINFO_QUAL_ARG1 GETNAMEINFO_TYPE_ARG1 sa,
|
|||||||
/* IPv4 threadsafe resolve function used for synch and asynch builds */
|
/* IPv4 threadsafe resolve function used for synch and asynch builds */
|
||||||
Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, int port);
|
Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, int port);
|
||||||
|
|
||||||
CURLcode Curl_async_resolved(struct connectdata *conn,
|
CURLcode Curl_once_resolved(struct connectdata *conn, bool *protocol_connect);
|
||||||
bool *protocol_connect);
|
|
||||||
|
|
||||||
#ifndef CURLRES_ASYNCH
|
|
||||||
#define Curl_async_resolved(x,y) CURLE_OK
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Curl_addrinfo_callback() is used when we build with any asynch specialty.
|
* Curl_addrinfo_callback() is used when we build with any asynch specialty.
|
||||||
@ -258,4 +253,10 @@ void Curl_hostcache_destroy(struct Curl_easy *data);
|
|||||||
*/
|
*/
|
||||||
CURLcode Curl_loadhostpairs(struct Curl_easy *data);
|
CURLcode Curl_loadhostpairs(struct Curl_easy *data);
|
||||||
|
|
||||||
|
CURLcode Curl_resolv_check(struct connectdata *conn,
|
||||||
|
struct Curl_dns_entry **dns);
|
||||||
|
int Curl_resolv_getsock(struct connectdata *conn,
|
||||||
|
curl_socket_t *socks,
|
||||||
|
int numsocks);
|
||||||
|
|
||||||
#endif /* HEADER_CURL_HOSTIP_H */
|
#endif /* HEADER_CURL_HOSTIP_H */
|
||||||
|
30
lib/multi.c
30
lib/multi.c
@ -906,7 +906,7 @@ static int multi_getsock(struct Curl_easy *data,
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
case CURLM_STATE_WAITRESOLVE:
|
case CURLM_STATE_WAITRESOLVE:
|
||||||
return Curl_resolver_getsock(data->easy_conn, socks, numsocks);
|
return Curl_resolv_getsock(data->easy_conn, socks, numsocks);
|
||||||
|
|
||||||
case CURLM_STATE_PROTOCONNECT:
|
case CURLM_STATE_PROTOCONNECT:
|
||||||
case CURLM_STATE_SENDPROTOCONNECT:
|
case CURLM_STATE_SENDPROTOCONNECT:
|
||||||
@ -1236,7 +1236,7 @@ static CURLcode multi_reconnect_request(struct connectdata **connp)
|
|||||||
return result;
|
return result;
|
||||||
|
|
||||||
/* Resolved, continue with the connection */
|
/* Resolved, continue with the connection */
|
||||||
result = Curl_async_resolved(conn, &protocol_done);
|
result = Curl_once_resolved(conn, &protocol_done);
|
||||||
if(result)
|
if(result)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -1512,7 +1512,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(!dns)
|
if(!dns)
|
||||||
result = Curl_resolver_is_resolved(data->easy_conn, &dns);
|
result = Curl_resolv_check(data->easy_conn, &dns);
|
||||||
|
|
||||||
/* Update sockets here, because the socket(s) may have been
|
/* Update sockets here, because the socket(s) may have been
|
||||||
closed and the application thus needs to be told, even if it
|
closed and the application thus needs to be told, even if it
|
||||||
@ -1525,10 +1525,10 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
|
|||||||
if(dns) {
|
if(dns) {
|
||||||
/* Perform the next step in the connection phase, and then move on
|
/* Perform the next step in the connection phase, and then move on
|
||||||
to the WAITCONNECT state */
|
to the WAITCONNECT state */
|
||||||
result = Curl_async_resolved(data->easy_conn, &protocol_connect);
|
result = Curl_once_resolved(data->easy_conn, &protocol_connect);
|
||||||
|
|
||||||
if(result)
|
if(result)
|
||||||
/* if Curl_async_resolved() returns failure, the connection struct
|
/* if Curl_once_resolved() returns failure, the connection struct
|
||||||
is already freed and gone */
|
is already freed and gone */
|
||||||
data->easy_conn = NULL; /* no more connection */
|
data->easy_conn = NULL; /* no more connection */
|
||||||
else {
|
else {
|
||||||
@ -2132,15 +2132,21 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(CURLM_STATE_COMPLETED == data->mstate) {
|
if(CURLM_STATE_COMPLETED == data->mstate) {
|
||||||
/* now fill in the Curl_message with this info */
|
if(data->set.fmultidone) {
|
||||||
msg = &data->msg;
|
/* signal via callback instead */
|
||||||
|
data->set.fmultidone(data, result);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* now fill in the Curl_message with this info */
|
||||||
|
msg = &data->msg;
|
||||||
|
|
||||||
msg->extmsg.msg = CURLMSG_DONE;
|
msg->extmsg.msg = CURLMSG_DONE;
|
||||||
msg->extmsg.easy_handle = data;
|
msg->extmsg.easy_handle = data;
|
||||||
msg->extmsg.data.result = result;
|
msg->extmsg.data.result = result;
|
||||||
|
|
||||||
rc = multi_addmsg(multi, msg);
|
rc = multi_addmsg(multi, msg);
|
||||||
DEBUGASSERT(!data->easy_conn);
|
DEBUGASSERT(!data->easy_conn);
|
||||||
|
}
|
||||||
multistate(data, CURLM_STATE_MSGSENT);
|
multistate(data, CURLM_STATE_MSGSENT);
|
||||||
}
|
}
|
||||||
} while((rc == CURLM_CALL_MULTI_PERFORM) || multi_ischanged(multi, FALSE));
|
} while((rc == CURLM_CALL_MULTI_PERFORM) || multi_ischanged(multi, FALSE));
|
||||||
|
@ -2603,6 +2603,11 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
|
|||||||
data->set.disallow_username_in_url =
|
data->set.disallow_username_in_url =
|
||||||
(0 != va_arg(param, long)) ? TRUE : FALSE;
|
(0 != va_arg(param, long)) ? TRUE : FALSE;
|
||||||
break;
|
break;
|
||||||
|
case CURLOPT_DOH_URL:
|
||||||
|
result = Curl_setstropt(&data->set.str[STRING_DOH],
|
||||||
|
va_arg(param, char *));
|
||||||
|
data->set.doh = data->set.str[STRING_DOH]?TRUE:FALSE;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
/* unknown tag and its companion, just ignore: */
|
/* unknown tag and its companion, just ignore: */
|
||||||
result = CURLE_UNKNOWN_OPTION;
|
result = CURLE_UNKNOWN_OPTION;
|
||||||
|
@ -476,7 +476,6 @@ struct hostname {
|
|||||||
#define KEEP_SENDBITS (KEEP_SEND | KEEP_SEND_HOLD | KEEP_SEND_PAUSE)
|
#define KEEP_SENDBITS (KEEP_SEND | KEEP_SEND_HOLD | KEEP_SEND_PAUSE)
|
||||||
|
|
||||||
|
|
||||||
#ifdef CURLRES_ASYNCH
|
|
||||||
struct Curl_async {
|
struct Curl_async {
|
||||||
char *hostname;
|
char *hostname;
|
||||||
int port;
|
int port;
|
||||||
@ -485,7 +484,6 @@ struct Curl_async {
|
|||||||
int status; /* if done is TRUE, this is the status from the callback */
|
int status; /* if done is TRUE, this is the status from the callback */
|
||||||
void *os_specific; /* 'struct thread_data' for Windows */
|
void *os_specific; /* 'struct thread_data' for Windows */
|
||||||
};
|
};
|
||||||
#endif
|
|
||||||
|
|
||||||
#define FIRSTSOCKET 0
|
#define FIRSTSOCKET 0
|
||||||
#define SECONDARYSOCKET 1
|
#define SECONDARYSOCKET 1
|
||||||
@ -511,6 +509,28 @@ enum upgrade101 {
|
|||||||
UPGR101_WORKING /* talking upgraded protocol */
|
UPGR101_WORKING /* talking upgraded protocol */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct dohresponse {
|
||||||
|
unsigned char *memory;
|
||||||
|
size_t size;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* one of these for each DoH request */
|
||||||
|
struct dnsprobe {
|
||||||
|
CURL *easy;
|
||||||
|
int dnstype;
|
||||||
|
unsigned char dohbuffer[512];
|
||||||
|
size_t dohlen;
|
||||||
|
struct dohresponse serverdoh;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct dohdata {
|
||||||
|
struct curl_slist *headers;
|
||||||
|
struct dnsprobe probe[2];
|
||||||
|
unsigned int pending; /* still outstanding requests */
|
||||||
|
const char *host;
|
||||||
|
int port;
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Request specific data in the easy handle (Curl_easy). Previously,
|
* Request specific data in the easy handle (Curl_easy). Previously,
|
||||||
* these members were on the connectdata struct but since a conn struct may
|
* these members were on the connectdata struct but since a conn struct may
|
||||||
@ -606,6 +626,7 @@ struct SingleRequest {
|
|||||||
|
|
||||||
void *protop; /* Allocated protocol-specific data. Each protocol
|
void *protop; /* Allocated protocol-specific data. Each protocol
|
||||||
handler makes sure this points to data it needs. */
|
handler makes sure this points to data it needs. */
|
||||||
|
struct dohdata doh; /* DoH specific data for this request */
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -969,11 +990,8 @@ struct connectdata {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
char syserr_buf [256]; /* buffer for Curl_strerror() */
|
char syserr_buf [256]; /* buffer for Curl_strerror() */
|
||||||
|
|
||||||
#ifdef CURLRES_ASYNCH
|
|
||||||
/* data used for the asynch name resolve callback */
|
/* data used for the asynch name resolve callback */
|
||||||
struct Curl_async async;
|
struct Curl_async async;
|
||||||
#endif
|
|
||||||
|
|
||||||
/* These three are used for chunked-encoding trailer support */
|
/* These three are used for chunked-encoding trailer support */
|
||||||
char *trailer; /* allocated buffer to store trailer in */
|
char *trailer; /* allocated buffer to store trailer in */
|
||||||
@ -1442,6 +1460,7 @@ enum dupstring {
|
|||||||
STRING_UNIX_SOCKET_PATH, /* path to Unix socket, if used */
|
STRING_UNIX_SOCKET_PATH, /* path to Unix socket, if used */
|
||||||
#endif
|
#endif
|
||||||
STRING_TARGET, /* CURLOPT_REQUEST_TARGET */
|
STRING_TARGET, /* CURLOPT_REQUEST_TARGET */
|
||||||
|
STRING_DOH, /* CURLOPT_DOH_URL */
|
||||||
/* -- end of zero-terminated strings -- */
|
/* -- end of zero-terminated strings -- */
|
||||||
|
|
||||||
STRING_LASTZEROTERMINATED,
|
STRING_LASTZEROTERMINATED,
|
||||||
@ -1453,6 +1472,11 @@ enum dupstring {
|
|||||||
STRING_LAST /* not used, just an end-of-list marker */
|
STRING_LAST /* not used, just an end-of-list marker */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* callback that gets called when this easy handle is completed within a multi
|
||||||
|
handle. Only used for internally created transfers, like for example
|
||||||
|
DoH. */
|
||||||
|
typedef int (*multidone_func)(struct Curl_easy *easy, CURLcode result);
|
||||||
|
|
||||||
struct UserDefined {
|
struct UserDefined {
|
||||||
FILE *err; /* the stderr user data goes here */
|
FILE *err; /* the stderr user data goes here */
|
||||||
void *debugdata; /* the data that will be passed to fdebug */
|
void *debugdata; /* the data that will be passed to fdebug */
|
||||||
@ -1688,6 +1712,10 @@ struct UserDefined {
|
|||||||
before resolver start */
|
before resolver start */
|
||||||
void *resolver_start_client; /* pointer to pass to resolver start callback */
|
void *resolver_start_client; /* pointer to pass to resolver start callback */
|
||||||
bool disallow_username_in_url; /* disallow username in url */
|
bool disallow_username_in_url; /* disallow username in url */
|
||||||
|
bool doh; /* DNS-over-HTTPS enabled */
|
||||||
|
bool doh_get; /* use GET for DoH requests, instead of POST */
|
||||||
|
multidone_func fmultidone;
|
||||||
|
struct Curl_easy *dohfor; /* this is a DoH request for that transfer */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Names {
|
struct Names {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user