summaryrefslogtreecommitdiff
path: root/libs/libcurl/src/doh.c
diff options
context:
space:
mode:
Diffstat (limited to 'libs/libcurl/src/doh.c')
-rw-r--r--libs/libcurl/src/doh.c196
1 files changed, 115 insertions, 81 deletions
diff --git a/libs/libcurl/src/doh.c b/libs/libcurl/src/doh.c
index d1795789e5..7f4eee5d81 100644
--- a/libs/libcurl/src/doh.c
+++ b/libs/libcurl/src/doh.c
@@ -86,12 +86,36 @@ UNITTEST DOHcode doh_encode(const char *host,
unsigned char *orig = dnsp;
const char *hostp = host;
- /* The expected output length does not depend on the number of dots within
- * the host name. It will always be two more than the length of the host
- * name, one for the size and one trailing null. In case there are dots,
- * each dot adds one size but removes the need to store the dot, net zero.
+ /* The expected output length is 16 bytes more than the length of
+ * the QNAME-encoding of the host name.
+ *
+ * A valid DNS name may not contain a zero-length label, except at
+ * the end. For this reason, a name beginning with a dot, or
+ * containing a sequence of two or more consecutive dots, is invalid
+ * and cannot be encoded as a QNAME.
+ *
+ * If the host name ends with a trailing dot, the corresponding
+ * QNAME-encoding is one byte longer than the host name. If (as is
+ * also valid) the hostname is shortened by the omission of the
+ * trailing dot, then its QNAME-encoding will be two bytes longer
+ * than the host name.
+ *
+ * Each [ label, dot ] pair is encoded as [ length, label ],
+ * preserving overall length. A final [ label ] without a dot is
+ * also encoded as [ length, label ], increasing overall length
+ * by one. The encoding is completed by appending a zero byte,
+ * representing the zero-length root label, again increasing
+ * the overall length by one.
*/
- const size_t expected_len = 12 + ( 1 + hostlen + 1) + 4;
+
+ size_t expected_len;
+ DEBUGASSERT(hostlen);
+ expected_len = 12 + 1 + hostlen + 4;
+ if(host[hostlen-1]!='.')
+ expected_len++;
+
+ if(expected_len > (256 + 16)) /* RFCs 1034, 1035 */
+ return DOH_DNS_NAME_TOO_LONG;
if(len < expected_len)
return DOH_TOO_SMALL_BUFFER;
@@ -109,31 +133,30 @@ UNITTEST DOHcode doh_encode(const char *host,
*dnsp++ = '\0';
*dnsp++ = '\0'; /* ARCOUNT */
- /* store a QNAME */
- do {
- char *dot = strchr(hostp, '.');
+ /* encode each label and store it in the QNAME */
+ while(*hostp) {
size_t labellen;
- bool found = false;
- if(dot) {
- found = true;
+ char *dot = strchr(hostp, '.');
+ if(dot)
labellen = dot - hostp;
- }
else
labellen = strlen(hostp);
- if(labellen > 63) {
- /* too long label, error out */
+ if((labellen > 63) || (!labellen)) {
+ /* label is too long or too short, error out */
*olen = 0;
return DOH_DNS_BAD_LABEL;
}
+ /* label is non-empty, process it */
*dnsp++ = (unsigned char)labellen;
memcpy(dnsp, hostp, labellen);
dnsp += labellen;
- hostp += labellen + 1;
- if(!found) {
- *dnsp++ = 0; /* terminating zero */
- break;
- }
- } while(1);
+ hostp += labellen;
+ /* advance past dot, but only if there is one */
+ if(dot)
+ hostp++;
+ } /* next label */
+
+ *dnsp++ = 0; /* append zero-length label for root */
/* There are assigned TYPE codes beyond 255: use range [1..65535] */
*dnsp++ = (unsigned char)(255 & (dnstype>>8)); /* upper 8 bit TYPE */
@@ -144,8 +167,8 @@ UNITTEST DOHcode doh_encode(const char *host,
*olen = dnsp - orig;
- /* verify that our assumption of length is valid, since
- * this has lead to buffer overflows in this function */
+ /* verify that our estimation of length is valid, since
+ * this has led to buffer overflows in this function */
DEBUGASSERT(*olen == expected_len);
return DOH_OK;
}
@@ -195,7 +218,7 @@ do { \
result = curl_easy_setopt(doh, x, y); \
if(result) \
goto error; \
-} WHILE_FALSE
+} while(0)
static CURLcode dohprobe(struct Curl_easy *data,
struct dnsprobe *p, DNStype dnstype,
@@ -280,38 +303,42 @@ static CURLcode dohprobe(struct Curl_easy *data,
ERROR_CHECK_SETOPT(CURLOPT_SSL_FALSESTART, 1L);
if(data->set.ssl.primary.verifyhost)
ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYHOST, 2L);
+#ifndef CURL_DISABLE_PROXY
if(data->set.proxy_ssl.primary.verifyhost)
ERROR_CHECK_SETOPT(CURLOPT_PROXY_SSL_VERIFYHOST, 2L);
- if(data->set.ssl.primary.verifypeer)
- ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYPEER, 1L);
if(data->set.proxy_ssl.primary.verifypeer)
ERROR_CHECK_SETOPT(CURLOPT_PROXY_SSL_VERIFYPEER, 1L);
+ if(data->set.str[STRING_SSL_CAFILE_PROXY]) {
+ ERROR_CHECK_SETOPT(CURLOPT_PROXY_CAINFO,
+ data->set.str[STRING_SSL_CAFILE_PROXY]);
+ }
+ if(data->set.str[STRING_SSL_CRLFILE_PROXY]) {
+ ERROR_CHECK_SETOPT(CURLOPT_PROXY_CRLFILE,
+ data->set.str[STRING_SSL_CRLFILE_PROXY]);
+ }
+ if(data->set.proxy_ssl.no_revoke)
+ ERROR_CHECK_SETOPT(CURLOPT_PROXY_SSL_OPTIONS, CURLSSLOPT_NO_REVOKE);
+ if(data->set.str[STRING_SSL_CAPATH_PROXY]) {
+ ERROR_CHECK_SETOPT(CURLOPT_PROXY_CAPATH,
+ data->set.str[STRING_SSL_CAPATH_PROXY]);
+ }
+#endif
+ if(data->set.ssl.primary.verifypeer)
+ ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYPEER, 1L);
if(data->set.ssl.primary.verifystatus)
ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYSTATUS, 1L);
if(data->set.str[STRING_SSL_CAFILE_ORIG]) {
ERROR_CHECK_SETOPT(CURLOPT_CAINFO,
data->set.str[STRING_SSL_CAFILE_ORIG]);
}
- if(data->set.str[STRING_SSL_CAFILE_PROXY]) {
- ERROR_CHECK_SETOPT(CURLOPT_PROXY_CAINFO,
- data->set.str[STRING_SSL_CAFILE_PROXY]);
- }
if(data->set.str[STRING_SSL_CAPATH_ORIG]) {
ERROR_CHECK_SETOPT(CURLOPT_CAPATH,
data->set.str[STRING_SSL_CAPATH_ORIG]);
}
- if(data->set.str[STRING_SSL_CAPATH_PROXY]) {
- ERROR_CHECK_SETOPT(CURLOPT_PROXY_CAPATH,
- data->set.str[STRING_SSL_CAPATH_PROXY]);
- }
if(data->set.str[STRING_SSL_CRLFILE_ORIG]) {
ERROR_CHECK_SETOPT(CURLOPT_CRLFILE,
data->set.str[STRING_SSL_CRLFILE_ORIG]);
}
- if(data->set.str[STRING_SSL_CRLFILE_PROXY]) {
- ERROR_CHECK_SETOPT(CURLOPT_PROXY_CRLFILE,
- data->set.str[STRING_SSL_CRLFILE_PROXY]);
- }
if(data->set.ssl.certinfo)
ERROR_CHECK_SETOPT(CURLOPT_CERTINFO, 1L);
if(data->set.str[STRING_SSL_RANDOM_FILE]) {
@@ -324,8 +351,6 @@ static CURLcode dohprobe(struct Curl_easy *data,
}
if(data->set.ssl.no_revoke)
ERROR_CHECK_SETOPT(CURLOPT_SSL_OPTIONS, CURLSSLOPT_NO_REVOKE);
- if(data->set.proxy_ssl.no_revoke)
- ERROR_CHECK_SETOPT(CURLOPT_PROXY_SSL_OPTIONS, CURLSSLOPT_NO_REVOKE);
if(data->set.ssl.fsslctx)
ERROR_CHECK_SETOPT(CURLOPT_SSL_CTX_FUNCTION, data->set.ssl.fsslctx);
if(data->set.ssl.fsslctxp)
@@ -362,6 +387,7 @@ Curl_addrinfo *Curl_doh(struct connectdata *conn,
{
struct Curl_easy *data = conn->data;
CURLcode result = CURLE_OK;
+ int slot;
*waitp = TRUE; /* this never returns synchronously */
(void)conn;
(void)hostname;
@@ -380,8 +406,8 @@ Curl_addrinfo *Curl_doh(struct connectdata *conn,
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],
+ result = dohprobe(data, &data->req.doh.probe[DOH_PROBE_SLOT_IPADDR_V4],
+ DNS_TYPE_A, hostname, data->set.str[STRING_DOH],
data->multi, data->req.doh.headers);
if(result)
goto error;
@@ -390,8 +416,8 @@ Curl_addrinfo *Curl_doh(struct connectdata *conn,
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],
+ result = dohprobe(data, &data->req.doh.probe[DOH_PROBE_SLOT_IPADDR_V6],
+ DNS_TYPE_AAAA, hostname, data->set.str[STRING_DOH],
data->multi, data->req.doh.headers);
if(result)
goto error;
@@ -402,8 +428,9 @@ Curl_addrinfo *Curl_doh(struct connectdata *conn,
error:
curl_slist_free_all(data->req.doh.headers);
data->req.doh.headers = NULL;
- Curl_close(&data->req.doh.probe[0].easy);
- Curl_close(&data->req.doh.probe[1].easy);
+ for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) {
+ Curl_close(&data->req.doh.probe[slot].easy);
+ }
return NULL;
}
@@ -586,6 +613,9 @@ static DOHcode rdata(unsigned char *doh,
if(rc)
return rc;
break;
+ case DNS_TYPE_DNAME:
+ /* explicit for clarity; just skip; rely on synthesized CNAME */
+ break;
default:
/* unsupported type, just skip it */
break;
@@ -647,8 +677,10 @@ UNITTEST DOHcode doh_decode(unsigned char *doh,
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 */
+ if((type != DNS_TYPE_CNAME) /* may be synthesized from DNAME */
+ && (type != DNS_TYPE_DNAME) /* if present, accept and ignore */
+ && (type != dnstype))
+ /* Not the same type as was asked for nor CNAME nor DNAME */
return DOH_DNS_UNEXPECTED_TYPE;
index += 2;
@@ -909,46 +941,43 @@ UNITTEST void de_cleanup(struct dohentry *d)
CURLcode Curl_doh_is_resolved(struct connectdata *conn,
struct Curl_dns_entry **dnsp)
{
+ CURLcode result;
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) {
+ if(!data->req.doh.probe[DOH_PROBE_SLOT_IPADDR_V4].easy &&
+ !data->req.doh.probe[DOH_PROBE_SLOT_IPADDR_V6].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;
+ DOHcode rc[DOH_PROBE_SLOTS];
struct dohentry de;
+ int slot;
/* 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);
+ for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) {
+ curl_multi_remove_handle(data->multi, data->req.doh.probe[slot].easy);
+ Curl_close(&data->req.doh.probe[slot].easy);
+ }
/* parse the responses, create the struct and return it! */
init_dohentry(&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);
- Curl_safefree(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);
- Curl_safefree(data->req.doh.probe[1].serverdoh.memory);
- if(rc2) {
- infof(data, "DOH: %s type %s for %s\n", doh_strerror(rc2),
- type2name(data->req.doh.probe[1].dnstype),
- data->req.doh.host);
- }
- if(!rc || !rc2) {
+ for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) {
+ rc[slot] = doh_decode(data->req.doh.probe[slot].serverdoh.memory,
+ data->req.doh.probe[slot].serverdoh.size,
+ data->req.doh.probe[slot].dnstype,
+ &de);
+ Curl_safefree(data->req.doh.probe[slot].serverdoh.memory);
+ if(rc[slot]) {
+ infof(data, "DOH: %s type %s for %s\n", doh_strerror(rc[slot]),
+ type2name(data->req.doh.probe[slot].dnstype),
+ data->req.doh.host);
+ }
+ } /* next slot */
+
+ result = CURLE_COULDNT_RESOLVE_HOST; /* until we know better */
+ if(!rc[DOH_PROBE_SLOT_IPADDR_V4] || !rc[DOH_PROBE_SLOT_IPADDR_V6]) {
+ /* we have an address, of one kind or other */
struct Curl_dns_entry *dns;
struct Curl_addrinfo *ai;
@@ -970,21 +999,26 @@ CURLcode Curl_doh_is_resolved(struct connectdata *conn,
if(data->share)
Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
- de_cleanup(&de);
- if(!dns)
+ if(!dns) {
/* returned failure, bail out nicely */
Curl_freeaddrinfo(ai);
+ }
else {
conn->async.dns = dns;
*dnsp = dns;
- return CURLE_OK;
+ result = CURLE_OK; /* address resolution OK */
}
- }
+ } /* address processing done */
+
+ /* Now process any build-specific attributes retrieved from DNS */
+
+ /* All done */
de_cleanup(&de);
+ return result;
- return CURLE_COULDNT_RESOLVE_HOST;
- }
+ } /* !data->req.doh.pending */
+ /* else wait for pending DOH transactions to complete */
return CURLE_OK;
}