113498266Sopenharmony_ci/*************************************************************************** 213498266Sopenharmony_ci * _ _ ____ _ 313498266Sopenharmony_ci * Project ___| | | | _ \| | 413498266Sopenharmony_ci * / __| | | | |_) | | 513498266Sopenharmony_ci * | (__| |_| | _ <| |___ 613498266Sopenharmony_ci * \___|\___/|_| \_\_____| 713498266Sopenharmony_ci * 813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 913498266Sopenharmony_ci * 1013498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which 1113498266Sopenharmony_ci * you should have received as part of this distribution. The terms 1213498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html. 1313498266Sopenharmony_ci * 1413498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell 1513498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is 1613498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file. 1713498266Sopenharmony_ci * 1813498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 1913498266Sopenharmony_ci * KIND, either express or implied. 2013498266Sopenharmony_ci * 2113498266Sopenharmony_ci * SPDX-License-Identifier: curl 2213498266Sopenharmony_ci * 2313498266Sopenharmony_ci ***************************************************************************/ 2413498266Sopenharmony_ci 2513498266Sopenharmony_ci#include "curl_setup.h" 2613498266Sopenharmony_ci 2713498266Sopenharmony_ci#ifndef CURL_DISABLE_DOH 2813498266Sopenharmony_ci 2913498266Sopenharmony_ci#include "urldata.h" 3013498266Sopenharmony_ci#include "curl_addrinfo.h" 3113498266Sopenharmony_ci#include "doh.h" 3213498266Sopenharmony_ci 3313498266Sopenharmony_ci#include "sendf.h" 3413498266Sopenharmony_ci#include "multiif.h" 3513498266Sopenharmony_ci#include "url.h" 3613498266Sopenharmony_ci#include "share.h" 3713498266Sopenharmony_ci#include "curl_base64.h" 3813498266Sopenharmony_ci#include "connect.h" 3913498266Sopenharmony_ci#include "strdup.h" 4013498266Sopenharmony_ci#include "dynbuf.h" 4113498266Sopenharmony_ci/* The last 3 #include files should be in this order */ 4213498266Sopenharmony_ci#include "curl_printf.h" 4313498266Sopenharmony_ci#include "curl_memory.h" 4413498266Sopenharmony_ci#include "memdebug.h" 4513498266Sopenharmony_ci 4613498266Sopenharmony_ci#define DNS_CLASS_IN 0x01 4713498266Sopenharmony_ci 4813498266Sopenharmony_ci#ifndef CURL_DISABLE_VERBOSE_STRINGS 4913498266Sopenharmony_cistatic const char * const errors[]={ 5013498266Sopenharmony_ci "", 5113498266Sopenharmony_ci "Bad label", 5213498266Sopenharmony_ci "Out of range", 5313498266Sopenharmony_ci "Label loop", 5413498266Sopenharmony_ci "Too small", 5513498266Sopenharmony_ci "Out of memory", 5613498266Sopenharmony_ci "RDATA length", 5713498266Sopenharmony_ci "Malformat", 5813498266Sopenharmony_ci "Bad RCODE", 5913498266Sopenharmony_ci "Unexpected TYPE", 6013498266Sopenharmony_ci "Unexpected CLASS", 6113498266Sopenharmony_ci "No content", 6213498266Sopenharmony_ci "Bad ID", 6313498266Sopenharmony_ci "Name too long" 6413498266Sopenharmony_ci}; 6513498266Sopenharmony_ci 6613498266Sopenharmony_cistatic const char *doh_strerror(DOHcode code) 6713498266Sopenharmony_ci{ 6813498266Sopenharmony_ci if((code >= DOH_OK) && (code <= DOH_DNS_NAME_TOO_LONG)) 6913498266Sopenharmony_ci return errors[code]; 7013498266Sopenharmony_ci return "bad error code"; 7113498266Sopenharmony_ci} 7213498266Sopenharmony_ci#endif 7313498266Sopenharmony_ci 7413498266Sopenharmony_ci/* @unittest 1655 7513498266Sopenharmony_ci */ 7613498266Sopenharmony_ciUNITTEST DOHcode doh_encode(const char *host, 7713498266Sopenharmony_ci DNStype dnstype, 7813498266Sopenharmony_ci unsigned char *dnsp, /* buffer */ 7913498266Sopenharmony_ci size_t len, /* buffer size */ 8013498266Sopenharmony_ci size_t *olen) /* output length */ 8113498266Sopenharmony_ci{ 8213498266Sopenharmony_ci const size_t hostlen = strlen(host); 8313498266Sopenharmony_ci unsigned char *orig = dnsp; 8413498266Sopenharmony_ci const char *hostp = host; 8513498266Sopenharmony_ci 8613498266Sopenharmony_ci /* The expected output length is 16 bytes more than the length of 8713498266Sopenharmony_ci * the QNAME-encoding of the host name. 8813498266Sopenharmony_ci * 8913498266Sopenharmony_ci * A valid DNS name may not contain a zero-length label, except at 9013498266Sopenharmony_ci * the end. For this reason, a name beginning with a dot, or 9113498266Sopenharmony_ci * containing a sequence of two or more consecutive dots, is invalid 9213498266Sopenharmony_ci * and cannot be encoded as a QNAME. 9313498266Sopenharmony_ci * 9413498266Sopenharmony_ci * If the host name ends with a trailing dot, the corresponding 9513498266Sopenharmony_ci * QNAME-encoding is one byte longer than the host name. If (as is 9613498266Sopenharmony_ci * also valid) the hostname is shortened by the omission of the 9713498266Sopenharmony_ci * trailing dot, then its QNAME-encoding will be two bytes longer 9813498266Sopenharmony_ci * than the host name. 9913498266Sopenharmony_ci * 10013498266Sopenharmony_ci * Each [ label, dot ] pair is encoded as [ length, label ], 10113498266Sopenharmony_ci * preserving overall length. A final [ label ] without a dot is 10213498266Sopenharmony_ci * also encoded as [ length, label ], increasing overall length 10313498266Sopenharmony_ci * by one. The encoding is completed by appending a zero byte, 10413498266Sopenharmony_ci * representing the zero-length root label, again increasing 10513498266Sopenharmony_ci * the overall length by one. 10613498266Sopenharmony_ci */ 10713498266Sopenharmony_ci 10813498266Sopenharmony_ci size_t expected_len; 10913498266Sopenharmony_ci DEBUGASSERT(hostlen); 11013498266Sopenharmony_ci expected_len = 12 + 1 + hostlen + 4; 11113498266Sopenharmony_ci if(host[hostlen-1]!='.') 11213498266Sopenharmony_ci expected_len++; 11313498266Sopenharmony_ci 11413498266Sopenharmony_ci if(expected_len > (256 + 16)) /* RFCs 1034, 1035 */ 11513498266Sopenharmony_ci return DOH_DNS_NAME_TOO_LONG; 11613498266Sopenharmony_ci 11713498266Sopenharmony_ci if(len < expected_len) 11813498266Sopenharmony_ci return DOH_TOO_SMALL_BUFFER; 11913498266Sopenharmony_ci 12013498266Sopenharmony_ci *dnsp++ = 0; /* 16 bit id */ 12113498266Sopenharmony_ci *dnsp++ = 0; 12213498266Sopenharmony_ci *dnsp++ = 0x01; /* |QR| Opcode |AA|TC|RD| Set the RD bit */ 12313498266Sopenharmony_ci *dnsp++ = '\0'; /* |RA| Z | RCODE | */ 12413498266Sopenharmony_ci *dnsp++ = '\0'; 12513498266Sopenharmony_ci *dnsp++ = 1; /* QDCOUNT (number of entries in the question section) */ 12613498266Sopenharmony_ci *dnsp++ = '\0'; 12713498266Sopenharmony_ci *dnsp++ = '\0'; /* ANCOUNT */ 12813498266Sopenharmony_ci *dnsp++ = '\0'; 12913498266Sopenharmony_ci *dnsp++ = '\0'; /* NSCOUNT */ 13013498266Sopenharmony_ci *dnsp++ = '\0'; 13113498266Sopenharmony_ci *dnsp++ = '\0'; /* ARCOUNT */ 13213498266Sopenharmony_ci 13313498266Sopenharmony_ci /* encode each label and store it in the QNAME */ 13413498266Sopenharmony_ci while(*hostp) { 13513498266Sopenharmony_ci size_t labellen; 13613498266Sopenharmony_ci char *dot = strchr(hostp, '.'); 13713498266Sopenharmony_ci if(dot) 13813498266Sopenharmony_ci labellen = dot - hostp; 13913498266Sopenharmony_ci else 14013498266Sopenharmony_ci labellen = strlen(hostp); 14113498266Sopenharmony_ci if((labellen > 63) || (!labellen)) { 14213498266Sopenharmony_ci /* label is too long or too short, error out */ 14313498266Sopenharmony_ci *olen = 0; 14413498266Sopenharmony_ci return DOH_DNS_BAD_LABEL; 14513498266Sopenharmony_ci } 14613498266Sopenharmony_ci /* label is non-empty, process it */ 14713498266Sopenharmony_ci *dnsp++ = (unsigned char)labellen; 14813498266Sopenharmony_ci memcpy(dnsp, hostp, labellen); 14913498266Sopenharmony_ci dnsp += labellen; 15013498266Sopenharmony_ci hostp += labellen; 15113498266Sopenharmony_ci /* advance past dot, but only if there is one */ 15213498266Sopenharmony_ci if(dot) 15313498266Sopenharmony_ci hostp++; 15413498266Sopenharmony_ci } /* next label */ 15513498266Sopenharmony_ci 15613498266Sopenharmony_ci *dnsp++ = 0; /* append zero-length label for root */ 15713498266Sopenharmony_ci 15813498266Sopenharmony_ci /* There are assigned TYPE codes beyond 255: use range [1..65535] */ 15913498266Sopenharmony_ci *dnsp++ = (unsigned char)(255 & (dnstype>>8)); /* upper 8 bit TYPE */ 16013498266Sopenharmony_ci *dnsp++ = (unsigned char)(255 & dnstype); /* lower 8 bit TYPE */ 16113498266Sopenharmony_ci 16213498266Sopenharmony_ci *dnsp++ = '\0'; /* upper 8 bit CLASS */ 16313498266Sopenharmony_ci *dnsp++ = DNS_CLASS_IN; /* IN - "the Internet" */ 16413498266Sopenharmony_ci 16513498266Sopenharmony_ci *olen = dnsp - orig; 16613498266Sopenharmony_ci 16713498266Sopenharmony_ci /* verify that our estimation of length is valid, since 16813498266Sopenharmony_ci * this has led to buffer overflows in this function */ 16913498266Sopenharmony_ci DEBUGASSERT(*olen == expected_len); 17013498266Sopenharmony_ci return DOH_OK; 17113498266Sopenharmony_ci} 17213498266Sopenharmony_ci 17313498266Sopenharmony_cistatic size_t 17413498266Sopenharmony_cidoh_write_cb(const void *contents, size_t size, size_t nmemb, void *userp) 17513498266Sopenharmony_ci{ 17613498266Sopenharmony_ci size_t realsize = size * nmemb; 17713498266Sopenharmony_ci struct dynbuf *mem = (struct dynbuf *)userp; 17813498266Sopenharmony_ci 17913498266Sopenharmony_ci if(Curl_dyn_addn(mem, contents, realsize)) 18013498266Sopenharmony_ci return 0; 18113498266Sopenharmony_ci 18213498266Sopenharmony_ci return realsize; 18313498266Sopenharmony_ci} 18413498266Sopenharmony_ci 18513498266Sopenharmony_ci/* called from multi.c when this DoH transfer is complete */ 18613498266Sopenharmony_cistatic int doh_done(struct Curl_easy *doh, CURLcode result) 18713498266Sopenharmony_ci{ 18813498266Sopenharmony_ci struct Curl_easy *data = doh->set.dohfor; 18913498266Sopenharmony_ci struct dohdata *dohp = data->req.doh; 19013498266Sopenharmony_ci /* so one of the DoH request done for the 'data' transfer is now complete! */ 19113498266Sopenharmony_ci dohp->pending--; 19213498266Sopenharmony_ci infof(data, "a DoH request is completed, %u to go", dohp->pending); 19313498266Sopenharmony_ci if(result) 19413498266Sopenharmony_ci infof(data, "DoH request %s", curl_easy_strerror(result)); 19513498266Sopenharmony_ci 19613498266Sopenharmony_ci if(!dohp->pending) { 19713498266Sopenharmony_ci /* DoH completed */ 19813498266Sopenharmony_ci curl_slist_free_all(dohp->headers); 19913498266Sopenharmony_ci dohp->headers = NULL; 20013498266Sopenharmony_ci Curl_expire(data, 0, EXPIRE_RUN_NOW); 20113498266Sopenharmony_ci } 20213498266Sopenharmony_ci return 0; 20313498266Sopenharmony_ci} 20413498266Sopenharmony_ci 20513498266Sopenharmony_ci#define ERROR_CHECK_SETOPT(x,y) \ 20613498266Sopenharmony_cido { \ 20713498266Sopenharmony_ci result = curl_easy_setopt(doh, x, y); \ 20813498266Sopenharmony_ci if(result && \ 20913498266Sopenharmony_ci result != CURLE_NOT_BUILT_IN && \ 21013498266Sopenharmony_ci result != CURLE_UNKNOWN_OPTION) \ 21113498266Sopenharmony_ci goto error; \ 21213498266Sopenharmony_ci} while(0) 21313498266Sopenharmony_ci 21413498266Sopenharmony_cistatic CURLcode dohprobe(struct Curl_easy *data, 21513498266Sopenharmony_ci struct dnsprobe *p, DNStype dnstype, 21613498266Sopenharmony_ci const char *host, 21713498266Sopenharmony_ci const char *url, CURLM *multi, 21813498266Sopenharmony_ci struct curl_slist *headers) 21913498266Sopenharmony_ci{ 22013498266Sopenharmony_ci struct Curl_easy *doh = NULL; 22113498266Sopenharmony_ci CURLcode result = CURLE_OK; 22213498266Sopenharmony_ci timediff_t timeout_ms; 22313498266Sopenharmony_ci DOHcode d = doh_encode(host, dnstype, p->dohbuffer, sizeof(p->dohbuffer), 22413498266Sopenharmony_ci &p->dohlen); 22513498266Sopenharmony_ci if(d) { 22613498266Sopenharmony_ci failf(data, "Failed to encode DoH packet [%d]", d); 22713498266Sopenharmony_ci return CURLE_OUT_OF_MEMORY; 22813498266Sopenharmony_ci } 22913498266Sopenharmony_ci 23013498266Sopenharmony_ci p->dnstype = dnstype; 23113498266Sopenharmony_ci Curl_dyn_init(&p->serverdoh, DYN_DOH_RESPONSE); 23213498266Sopenharmony_ci 23313498266Sopenharmony_ci timeout_ms = Curl_timeleft(data, NULL, TRUE); 23413498266Sopenharmony_ci if(timeout_ms <= 0) { 23513498266Sopenharmony_ci result = CURLE_OPERATION_TIMEDOUT; 23613498266Sopenharmony_ci goto error; 23713498266Sopenharmony_ci } 23813498266Sopenharmony_ci /* Curl_open() is the internal version of curl_easy_init() */ 23913498266Sopenharmony_ci result = Curl_open(&doh); 24013498266Sopenharmony_ci if(!result) { 24113498266Sopenharmony_ci /* pass in the struct pointer via a local variable to please coverity and 24213498266Sopenharmony_ci the gcc typecheck helpers */ 24313498266Sopenharmony_ci struct dynbuf *resp = &p->serverdoh; 24413498266Sopenharmony_ci doh->state.internal = true; 24513498266Sopenharmony_ci ERROR_CHECK_SETOPT(CURLOPT_URL, url); 24613498266Sopenharmony_ci ERROR_CHECK_SETOPT(CURLOPT_DEFAULT_PROTOCOL, "https"); 24713498266Sopenharmony_ci ERROR_CHECK_SETOPT(CURLOPT_WRITEFUNCTION, doh_write_cb); 24813498266Sopenharmony_ci ERROR_CHECK_SETOPT(CURLOPT_WRITEDATA, resp); 24913498266Sopenharmony_ci ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDS, p->dohbuffer); 25013498266Sopenharmony_ci ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDSIZE, (long)p->dohlen); 25113498266Sopenharmony_ci ERROR_CHECK_SETOPT(CURLOPT_HTTPHEADER, headers); 25213498266Sopenharmony_ci#ifdef USE_HTTP2 25313498266Sopenharmony_ci ERROR_CHECK_SETOPT(CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS); 25413498266Sopenharmony_ci ERROR_CHECK_SETOPT(CURLOPT_PIPEWAIT, 1L); 25513498266Sopenharmony_ci#endif 25613498266Sopenharmony_ci#ifndef CURLDEBUG 25713498266Sopenharmony_ci /* enforce HTTPS if not debug */ 25813498266Sopenharmony_ci ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTPS); 25913498266Sopenharmony_ci#else 26013498266Sopenharmony_ci /* in debug mode, also allow http */ 26113498266Sopenharmony_ci ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTP|CURLPROTO_HTTPS); 26213498266Sopenharmony_ci#endif 26313498266Sopenharmony_ci ERROR_CHECK_SETOPT(CURLOPT_TIMEOUT_MS, (long)timeout_ms); 26413498266Sopenharmony_ci ERROR_CHECK_SETOPT(CURLOPT_SHARE, data->share); 26513498266Sopenharmony_ci if(data->set.err && data->set.err != stderr) 26613498266Sopenharmony_ci ERROR_CHECK_SETOPT(CURLOPT_STDERR, data->set.err); 26713498266Sopenharmony_ci if(data->set.verbose) 26813498266Sopenharmony_ci ERROR_CHECK_SETOPT(CURLOPT_VERBOSE, 1L); 26913498266Sopenharmony_ci if(data->set.no_signal) 27013498266Sopenharmony_ci ERROR_CHECK_SETOPT(CURLOPT_NOSIGNAL, 1L); 27113498266Sopenharmony_ci 27213498266Sopenharmony_ci ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYHOST, 27313498266Sopenharmony_ci data->set.doh_verifyhost ? 2L : 0L); 27413498266Sopenharmony_ci ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYPEER, 27513498266Sopenharmony_ci data->set.doh_verifypeer ? 1L : 0L); 27613498266Sopenharmony_ci ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYSTATUS, 27713498266Sopenharmony_ci data->set.doh_verifystatus ? 1L : 0L); 27813498266Sopenharmony_ci 27913498266Sopenharmony_ci /* Inherit *some* SSL options from the user's transfer. This is a 28013498266Sopenharmony_ci best-guess as to which options are needed for compatibility. #3661 28113498266Sopenharmony_ci 28213498266Sopenharmony_ci Note DoH does not inherit the user's proxy server so proxy SSL settings 28313498266Sopenharmony_ci have no effect and are not inherited. If that changes then two new 28413498266Sopenharmony_ci options should be added to check doh proxy insecure separately, 28513498266Sopenharmony_ci CURLOPT_DOH_PROXY_SSL_VERIFYHOST and CURLOPT_DOH_PROXY_SSL_VERIFYPEER. 28613498266Sopenharmony_ci */ 28713498266Sopenharmony_ci if(data->set.ssl.falsestart) 28813498266Sopenharmony_ci ERROR_CHECK_SETOPT(CURLOPT_SSL_FALSESTART, 1L); 28913498266Sopenharmony_ci if(data->set.str[STRING_SSL_CAFILE]) { 29013498266Sopenharmony_ci ERROR_CHECK_SETOPT(CURLOPT_CAINFO, 29113498266Sopenharmony_ci data->set.str[STRING_SSL_CAFILE]); 29213498266Sopenharmony_ci } 29313498266Sopenharmony_ci if(data->set.blobs[BLOB_CAINFO]) { 29413498266Sopenharmony_ci ERROR_CHECK_SETOPT(CURLOPT_CAINFO_BLOB, 29513498266Sopenharmony_ci data->set.blobs[BLOB_CAINFO]); 29613498266Sopenharmony_ci } 29713498266Sopenharmony_ci if(data->set.str[STRING_SSL_CAPATH]) { 29813498266Sopenharmony_ci ERROR_CHECK_SETOPT(CURLOPT_CAPATH, 29913498266Sopenharmony_ci data->set.str[STRING_SSL_CAPATH]); 30013498266Sopenharmony_ci } 30113498266Sopenharmony_ci if(data->set.str[STRING_SSL_CRLFILE]) { 30213498266Sopenharmony_ci ERROR_CHECK_SETOPT(CURLOPT_CRLFILE, 30313498266Sopenharmony_ci data->set.str[STRING_SSL_CRLFILE]); 30413498266Sopenharmony_ci } 30513498266Sopenharmony_ci if(data->set.ssl.certinfo) 30613498266Sopenharmony_ci ERROR_CHECK_SETOPT(CURLOPT_CERTINFO, 1L); 30713498266Sopenharmony_ci if(data->set.ssl.fsslctx) 30813498266Sopenharmony_ci ERROR_CHECK_SETOPT(CURLOPT_SSL_CTX_FUNCTION, data->set.ssl.fsslctx); 30913498266Sopenharmony_ci if(data->set.ssl.fsslctxp) 31013498266Sopenharmony_ci ERROR_CHECK_SETOPT(CURLOPT_SSL_CTX_DATA, data->set.ssl.fsslctxp); 31113498266Sopenharmony_ci if(data->set.fdebug) 31213498266Sopenharmony_ci ERROR_CHECK_SETOPT(CURLOPT_DEBUGFUNCTION, data->set.fdebug); 31313498266Sopenharmony_ci if(data->set.debugdata) 31413498266Sopenharmony_ci ERROR_CHECK_SETOPT(CURLOPT_DEBUGDATA, data->set.debugdata); 31513498266Sopenharmony_ci if(data->set.str[STRING_SSL_EC_CURVES]) { 31613498266Sopenharmony_ci ERROR_CHECK_SETOPT(CURLOPT_SSL_EC_CURVES, 31713498266Sopenharmony_ci data->set.str[STRING_SSL_EC_CURVES]); 31813498266Sopenharmony_ci } 31913498266Sopenharmony_ci 32013498266Sopenharmony_ci { 32113498266Sopenharmony_ci long mask = 32213498266Sopenharmony_ci (data->set.ssl.enable_beast ? 32313498266Sopenharmony_ci CURLSSLOPT_ALLOW_BEAST : 0) | 32413498266Sopenharmony_ci (data->set.ssl.no_revoke ? 32513498266Sopenharmony_ci CURLSSLOPT_NO_REVOKE : 0) | 32613498266Sopenharmony_ci (data->set.ssl.no_partialchain ? 32713498266Sopenharmony_ci CURLSSLOPT_NO_PARTIALCHAIN : 0) | 32813498266Sopenharmony_ci (data->set.ssl.revoke_best_effort ? 32913498266Sopenharmony_ci CURLSSLOPT_REVOKE_BEST_EFFORT : 0) | 33013498266Sopenharmony_ci (data->set.ssl.native_ca_store ? 33113498266Sopenharmony_ci CURLSSLOPT_NATIVE_CA : 0) | 33213498266Sopenharmony_ci (data->set.ssl.auto_client_cert ? 33313498266Sopenharmony_ci CURLSSLOPT_AUTO_CLIENT_CERT : 0); 33413498266Sopenharmony_ci 33513498266Sopenharmony_ci (void)curl_easy_setopt(doh, CURLOPT_SSL_OPTIONS, mask); 33613498266Sopenharmony_ci } 33713498266Sopenharmony_ci 33813498266Sopenharmony_ci doh->set.fmultidone = doh_done; 33913498266Sopenharmony_ci doh->set.dohfor = data; /* identify for which transfer this is done */ 34013498266Sopenharmony_ci p->easy = doh; 34113498266Sopenharmony_ci 34213498266Sopenharmony_ci /* DoH handles must not inherit private_data. The handles may be passed to 34313498266Sopenharmony_ci the user via callbacks and the user will be able to identify them as 34413498266Sopenharmony_ci internal handles because private data is not set. The user can then set 34513498266Sopenharmony_ci private_data via CURLOPT_PRIVATE if they so choose. */ 34613498266Sopenharmony_ci DEBUGASSERT(!doh->set.private_data); 34713498266Sopenharmony_ci 34813498266Sopenharmony_ci if(curl_multi_add_handle(multi, doh)) 34913498266Sopenharmony_ci goto error; 35013498266Sopenharmony_ci } 35113498266Sopenharmony_ci else 35213498266Sopenharmony_ci goto error; 35313498266Sopenharmony_ci return CURLE_OK; 35413498266Sopenharmony_ci 35513498266Sopenharmony_cierror: 35613498266Sopenharmony_ci Curl_close(&doh); 35713498266Sopenharmony_ci return result; 35813498266Sopenharmony_ci} 35913498266Sopenharmony_ci 36013498266Sopenharmony_ci/* 36113498266Sopenharmony_ci * Curl_doh() resolves a name using DoH. It resolves a name and returns a 36213498266Sopenharmony_ci * 'Curl_addrinfo *' with the address information. 36313498266Sopenharmony_ci */ 36413498266Sopenharmony_ci 36513498266Sopenharmony_cistruct Curl_addrinfo *Curl_doh(struct Curl_easy *data, 36613498266Sopenharmony_ci const char *hostname, 36713498266Sopenharmony_ci int port, 36813498266Sopenharmony_ci int *waitp) 36913498266Sopenharmony_ci{ 37013498266Sopenharmony_ci CURLcode result = CURLE_OK; 37113498266Sopenharmony_ci int slot; 37213498266Sopenharmony_ci struct dohdata *dohp; 37313498266Sopenharmony_ci struct connectdata *conn = data->conn; 37413498266Sopenharmony_ci *waitp = FALSE; 37513498266Sopenharmony_ci (void)hostname; 37613498266Sopenharmony_ci (void)port; 37713498266Sopenharmony_ci 37813498266Sopenharmony_ci DEBUGASSERT(!data->req.doh); 37913498266Sopenharmony_ci DEBUGASSERT(conn); 38013498266Sopenharmony_ci 38113498266Sopenharmony_ci /* start clean, consider allocating this struct on demand */ 38213498266Sopenharmony_ci dohp = data->req.doh = calloc(1, sizeof(struct dohdata)); 38313498266Sopenharmony_ci if(!dohp) 38413498266Sopenharmony_ci return NULL; 38513498266Sopenharmony_ci 38613498266Sopenharmony_ci conn->bits.doh = TRUE; 38713498266Sopenharmony_ci dohp->host = hostname; 38813498266Sopenharmony_ci dohp->port = port; 38913498266Sopenharmony_ci dohp->headers = 39013498266Sopenharmony_ci curl_slist_append(NULL, 39113498266Sopenharmony_ci "Content-Type: application/dns-message"); 39213498266Sopenharmony_ci if(!dohp->headers) 39313498266Sopenharmony_ci goto error; 39413498266Sopenharmony_ci 39513498266Sopenharmony_ci /* create IPv4 DoH request */ 39613498266Sopenharmony_ci result = dohprobe(data, &dohp->probe[DOH_PROBE_SLOT_IPADDR_V4], 39713498266Sopenharmony_ci DNS_TYPE_A, hostname, data->set.str[STRING_DOH], 39813498266Sopenharmony_ci data->multi, dohp->headers); 39913498266Sopenharmony_ci if(result) 40013498266Sopenharmony_ci goto error; 40113498266Sopenharmony_ci dohp->pending++; 40213498266Sopenharmony_ci 40313498266Sopenharmony_ci#ifdef ENABLE_IPV6 40413498266Sopenharmony_ci if((conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) { 40513498266Sopenharmony_ci /* create IPv6 DoH request */ 40613498266Sopenharmony_ci result = dohprobe(data, &dohp->probe[DOH_PROBE_SLOT_IPADDR_V6], 40713498266Sopenharmony_ci DNS_TYPE_AAAA, hostname, data->set.str[STRING_DOH], 40813498266Sopenharmony_ci data->multi, dohp->headers); 40913498266Sopenharmony_ci if(result) 41013498266Sopenharmony_ci goto error; 41113498266Sopenharmony_ci dohp->pending++; 41213498266Sopenharmony_ci } 41313498266Sopenharmony_ci#endif 41413498266Sopenharmony_ci *waitp = TRUE; /* this never returns synchronously */ 41513498266Sopenharmony_ci return NULL; 41613498266Sopenharmony_ci 41713498266Sopenharmony_cierror: 41813498266Sopenharmony_ci curl_slist_free_all(dohp->headers); 41913498266Sopenharmony_ci data->req.doh->headers = NULL; 42013498266Sopenharmony_ci for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) { 42113498266Sopenharmony_ci (void)curl_multi_remove_handle(data->multi, dohp->probe[slot].easy); 42213498266Sopenharmony_ci Curl_close(&dohp->probe[slot].easy); 42313498266Sopenharmony_ci } 42413498266Sopenharmony_ci Curl_safefree(data->req.doh); 42513498266Sopenharmony_ci return NULL; 42613498266Sopenharmony_ci} 42713498266Sopenharmony_ci 42813498266Sopenharmony_cistatic DOHcode skipqname(const unsigned char *doh, size_t dohlen, 42913498266Sopenharmony_ci unsigned int *indexp) 43013498266Sopenharmony_ci{ 43113498266Sopenharmony_ci unsigned char length; 43213498266Sopenharmony_ci do { 43313498266Sopenharmony_ci if(dohlen < (*indexp + 1)) 43413498266Sopenharmony_ci return DOH_DNS_OUT_OF_RANGE; 43513498266Sopenharmony_ci length = doh[*indexp]; 43613498266Sopenharmony_ci if((length & 0xc0) == 0xc0) { 43713498266Sopenharmony_ci /* name pointer, advance over it and be done */ 43813498266Sopenharmony_ci if(dohlen < (*indexp + 2)) 43913498266Sopenharmony_ci return DOH_DNS_OUT_OF_RANGE; 44013498266Sopenharmony_ci *indexp += 2; 44113498266Sopenharmony_ci break; 44213498266Sopenharmony_ci } 44313498266Sopenharmony_ci if(length & 0xc0) 44413498266Sopenharmony_ci return DOH_DNS_BAD_LABEL; 44513498266Sopenharmony_ci if(dohlen < (*indexp + 1 + length)) 44613498266Sopenharmony_ci return DOH_DNS_OUT_OF_RANGE; 44713498266Sopenharmony_ci *indexp += (unsigned int)(1 + length); 44813498266Sopenharmony_ci } while(length); 44913498266Sopenharmony_ci return DOH_OK; 45013498266Sopenharmony_ci} 45113498266Sopenharmony_ci 45213498266Sopenharmony_cistatic unsigned short get16bit(const unsigned char *doh, int index) 45313498266Sopenharmony_ci{ 45413498266Sopenharmony_ci return (unsigned short)((doh[index] << 8) | doh[index + 1]); 45513498266Sopenharmony_ci} 45613498266Sopenharmony_ci 45713498266Sopenharmony_cistatic unsigned int get32bit(const unsigned char *doh, int index) 45813498266Sopenharmony_ci{ 45913498266Sopenharmony_ci /* make clang and gcc optimize this to bswap by incrementing 46013498266Sopenharmony_ci the pointer first. */ 46113498266Sopenharmony_ci doh += index; 46213498266Sopenharmony_ci 46313498266Sopenharmony_ci /* avoid undefined behavior by casting to unsigned before shifting 46413498266Sopenharmony_ci 24 bits, possibly into the sign bit. codegen is same, but 46513498266Sopenharmony_ci ub sanitizer won't be upset */ 46613498266Sopenharmony_ci return ((unsigned)doh[0] << 24) | ((unsigned)doh[1] << 16) | 46713498266Sopenharmony_ci ((unsigned)doh[2] << 8) | doh[3]; 46813498266Sopenharmony_ci} 46913498266Sopenharmony_ci 47013498266Sopenharmony_cistatic DOHcode store_a(const unsigned char *doh, int index, struct dohentry *d) 47113498266Sopenharmony_ci{ 47213498266Sopenharmony_ci /* silently ignore addresses over the limit */ 47313498266Sopenharmony_ci if(d->numaddr < DOH_MAX_ADDR) { 47413498266Sopenharmony_ci struct dohaddr *a = &d->addr[d->numaddr]; 47513498266Sopenharmony_ci a->type = DNS_TYPE_A; 47613498266Sopenharmony_ci memcpy(&a->ip.v4, &doh[index], 4); 47713498266Sopenharmony_ci d->numaddr++; 47813498266Sopenharmony_ci } 47913498266Sopenharmony_ci return DOH_OK; 48013498266Sopenharmony_ci} 48113498266Sopenharmony_ci 48213498266Sopenharmony_cistatic DOHcode store_aaaa(const unsigned char *doh, 48313498266Sopenharmony_ci int index, 48413498266Sopenharmony_ci struct dohentry *d) 48513498266Sopenharmony_ci{ 48613498266Sopenharmony_ci /* silently ignore addresses over the limit */ 48713498266Sopenharmony_ci if(d->numaddr < DOH_MAX_ADDR) { 48813498266Sopenharmony_ci struct dohaddr *a = &d->addr[d->numaddr]; 48913498266Sopenharmony_ci a->type = DNS_TYPE_AAAA; 49013498266Sopenharmony_ci memcpy(&a->ip.v6, &doh[index], 16); 49113498266Sopenharmony_ci d->numaddr++; 49213498266Sopenharmony_ci } 49313498266Sopenharmony_ci return DOH_OK; 49413498266Sopenharmony_ci} 49513498266Sopenharmony_ci 49613498266Sopenharmony_cistatic DOHcode store_cname(const unsigned char *doh, 49713498266Sopenharmony_ci size_t dohlen, 49813498266Sopenharmony_ci unsigned int index, 49913498266Sopenharmony_ci struct dohentry *d) 50013498266Sopenharmony_ci{ 50113498266Sopenharmony_ci struct dynbuf *c; 50213498266Sopenharmony_ci unsigned int loop = 128; /* a valid DNS name can never loop this much */ 50313498266Sopenharmony_ci unsigned char length; 50413498266Sopenharmony_ci 50513498266Sopenharmony_ci if(d->numcname == DOH_MAX_CNAME) 50613498266Sopenharmony_ci return DOH_OK; /* skip! */ 50713498266Sopenharmony_ci 50813498266Sopenharmony_ci c = &d->cname[d->numcname++]; 50913498266Sopenharmony_ci do { 51013498266Sopenharmony_ci if(index >= dohlen) 51113498266Sopenharmony_ci return DOH_DNS_OUT_OF_RANGE; 51213498266Sopenharmony_ci length = doh[index]; 51313498266Sopenharmony_ci if((length & 0xc0) == 0xc0) { 51413498266Sopenharmony_ci int newpos; 51513498266Sopenharmony_ci /* name pointer, get the new offset (14 bits) */ 51613498266Sopenharmony_ci if((index + 1) >= dohlen) 51713498266Sopenharmony_ci return DOH_DNS_OUT_OF_RANGE; 51813498266Sopenharmony_ci 51913498266Sopenharmony_ci /* move to the new index */ 52013498266Sopenharmony_ci newpos = (length & 0x3f) << 8 | doh[index + 1]; 52113498266Sopenharmony_ci index = newpos; 52213498266Sopenharmony_ci continue; 52313498266Sopenharmony_ci } 52413498266Sopenharmony_ci else if(length & 0xc0) 52513498266Sopenharmony_ci return DOH_DNS_BAD_LABEL; /* bad input */ 52613498266Sopenharmony_ci else 52713498266Sopenharmony_ci index++; 52813498266Sopenharmony_ci 52913498266Sopenharmony_ci if(length) { 53013498266Sopenharmony_ci if(Curl_dyn_len(c)) { 53113498266Sopenharmony_ci if(Curl_dyn_addn(c, STRCONST("."))) 53213498266Sopenharmony_ci return DOH_OUT_OF_MEM; 53313498266Sopenharmony_ci } 53413498266Sopenharmony_ci if((index + length) > dohlen) 53513498266Sopenharmony_ci return DOH_DNS_BAD_LABEL; 53613498266Sopenharmony_ci 53713498266Sopenharmony_ci if(Curl_dyn_addn(c, &doh[index], length)) 53813498266Sopenharmony_ci return DOH_OUT_OF_MEM; 53913498266Sopenharmony_ci index += length; 54013498266Sopenharmony_ci } 54113498266Sopenharmony_ci } while(length && --loop); 54213498266Sopenharmony_ci 54313498266Sopenharmony_ci if(!loop) 54413498266Sopenharmony_ci return DOH_DNS_LABEL_LOOP; 54513498266Sopenharmony_ci return DOH_OK; 54613498266Sopenharmony_ci} 54713498266Sopenharmony_ci 54813498266Sopenharmony_cistatic DOHcode rdata(const unsigned char *doh, 54913498266Sopenharmony_ci size_t dohlen, 55013498266Sopenharmony_ci unsigned short rdlength, 55113498266Sopenharmony_ci unsigned short type, 55213498266Sopenharmony_ci int index, 55313498266Sopenharmony_ci struct dohentry *d) 55413498266Sopenharmony_ci{ 55513498266Sopenharmony_ci /* RDATA 55613498266Sopenharmony_ci - A (TYPE 1): 4 bytes 55713498266Sopenharmony_ci - AAAA (TYPE 28): 16 bytes 55813498266Sopenharmony_ci - NS (TYPE 2): N bytes */ 55913498266Sopenharmony_ci DOHcode rc; 56013498266Sopenharmony_ci 56113498266Sopenharmony_ci switch(type) { 56213498266Sopenharmony_ci case DNS_TYPE_A: 56313498266Sopenharmony_ci if(rdlength != 4) 56413498266Sopenharmony_ci return DOH_DNS_RDATA_LEN; 56513498266Sopenharmony_ci rc = store_a(doh, index, d); 56613498266Sopenharmony_ci if(rc) 56713498266Sopenharmony_ci return rc; 56813498266Sopenharmony_ci break; 56913498266Sopenharmony_ci case DNS_TYPE_AAAA: 57013498266Sopenharmony_ci if(rdlength != 16) 57113498266Sopenharmony_ci return DOH_DNS_RDATA_LEN; 57213498266Sopenharmony_ci rc = store_aaaa(doh, index, d); 57313498266Sopenharmony_ci if(rc) 57413498266Sopenharmony_ci return rc; 57513498266Sopenharmony_ci break; 57613498266Sopenharmony_ci case DNS_TYPE_CNAME: 57713498266Sopenharmony_ci rc = store_cname(doh, dohlen, index, d); 57813498266Sopenharmony_ci if(rc) 57913498266Sopenharmony_ci return rc; 58013498266Sopenharmony_ci break; 58113498266Sopenharmony_ci case DNS_TYPE_DNAME: 58213498266Sopenharmony_ci /* explicit for clarity; just skip; rely on synthesized CNAME */ 58313498266Sopenharmony_ci break; 58413498266Sopenharmony_ci default: 58513498266Sopenharmony_ci /* unsupported type, just skip it */ 58613498266Sopenharmony_ci break; 58713498266Sopenharmony_ci } 58813498266Sopenharmony_ci return DOH_OK; 58913498266Sopenharmony_ci} 59013498266Sopenharmony_ci 59113498266Sopenharmony_ciUNITTEST void de_init(struct dohentry *de) 59213498266Sopenharmony_ci{ 59313498266Sopenharmony_ci int i; 59413498266Sopenharmony_ci memset(de, 0, sizeof(*de)); 59513498266Sopenharmony_ci de->ttl = INT_MAX; 59613498266Sopenharmony_ci for(i = 0; i < DOH_MAX_CNAME; i++) 59713498266Sopenharmony_ci Curl_dyn_init(&de->cname[i], DYN_DOH_CNAME); 59813498266Sopenharmony_ci} 59913498266Sopenharmony_ci 60013498266Sopenharmony_ci 60113498266Sopenharmony_ciUNITTEST DOHcode doh_decode(const unsigned char *doh, 60213498266Sopenharmony_ci size_t dohlen, 60313498266Sopenharmony_ci DNStype dnstype, 60413498266Sopenharmony_ci struct dohentry *d) 60513498266Sopenharmony_ci{ 60613498266Sopenharmony_ci unsigned char rcode; 60713498266Sopenharmony_ci unsigned short qdcount; 60813498266Sopenharmony_ci unsigned short ancount; 60913498266Sopenharmony_ci unsigned short type = 0; 61013498266Sopenharmony_ci unsigned short rdlength; 61113498266Sopenharmony_ci unsigned short nscount; 61213498266Sopenharmony_ci unsigned short arcount; 61313498266Sopenharmony_ci unsigned int index = 12; 61413498266Sopenharmony_ci DOHcode rc; 61513498266Sopenharmony_ci 61613498266Sopenharmony_ci if(dohlen < 12) 61713498266Sopenharmony_ci return DOH_TOO_SMALL_BUFFER; /* too small */ 61813498266Sopenharmony_ci if(!doh || doh[0] || doh[1]) 61913498266Sopenharmony_ci return DOH_DNS_BAD_ID; /* bad ID */ 62013498266Sopenharmony_ci rcode = doh[3] & 0x0f; 62113498266Sopenharmony_ci if(rcode) 62213498266Sopenharmony_ci return DOH_DNS_BAD_RCODE; /* bad rcode */ 62313498266Sopenharmony_ci 62413498266Sopenharmony_ci qdcount = get16bit(doh, 4); 62513498266Sopenharmony_ci while(qdcount) { 62613498266Sopenharmony_ci rc = skipqname(doh, dohlen, &index); 62713498266Sopenharmony_ci if(rc) 62813498266Sopenharmony_ci return rc; /* bad qname */ 62913498266Sopenharmony_ci if(dohlen < (index + 4)) 63013498266Sopenharmony_ci return DOH_DNS_OUT_OF_RANGE; 63113498266Sopenharmony_ci index += 4; /* skip question's type and class */ 63213498266Sopenharmony_ci qdcount--; 63313498266Sopenharmony_ci } 63413498266Sopenharmony_ci 63513498266Sopenharmony_ci ancount = get16bit(doh, 6); 63613498266Sopenharmony_ci while(ancount) { 63713498266Sopenharmony_ci unsigned short class; 63813498266Sopenharmony_ci unsigned int ttl; 63913498266Sopenharmony_ci 64013498266Sopenharmony_ci rc = skipqname(doh, dohlen, &index); 64113498266Sopenharmony_ci if(rc) 64213498266Sopenharmony_ci return rc; /* bad qname */ 64313498266Sopenharmony_ci 64413498266Sopenharmony_ci if(dohlen < (index + 2)) 64513498266Sopenharmony_ci return DOH_DNS_OUT_OF_RANGE; 64613498266Sopenharmony_ci 64713498266Sopenharmony_ci type = get16bit(doh, index); 64813498266Sopenharmony_ci if((type != DNS_TYPE_CNAME) /* may be synthesized from DNAME */ 64913498266Sopenharmony_ci && (type != DNS_TYPE_DNAME) /* if present, accept and ignore */ 65013498266Sopenharmony_ci && (type != dnstype)) 65113498266Sopenharmony_ci /* Not the same type as was asked for nor CNAME nor DNAME */ 65213498266Sopenharmony_ci return DOH_DNS_UNEXPECTED_TYPE; 65313498266Sopenharmony_ci index += 2; 65413498266Sopenharmony_ci 65513498266Sopenharmony_ci if(dohlen < (index + 2)) 65613498266Sopenharmony_ci return DOH_DNS_OUT_OF_RANGE; 65713498266Sopenharmony_ci class = get16bit(doh, index); 65813498266Sopenharmony_ci if(DNS_CLASS_IN != class) 65913498266Sopenharmony_ci return DOH_DNS_UNEXPECTED_CLASS; /* unsupported */ 66013498266Sopenharmony_ci index += 2; 66113498266Sopenharmony_ci 66213498266Sopenharmony_ci if(dohlen < (index + 4)) 66313498266Sopenharmony_ci return DOH_DNS_OUT_OF_RANGE; 66413498266Sopenharmony_ci 66513498266Sopenharmony_ci ttl = get32bit(doh, index); 66613498266Sopenharmony_ci if(ttl < d->ttl) 66713498266Sopenharmony_ci d->ttl = ttl; 66813498266Sopenharmony_ci index += 4; 66913498266Sopenharmony_ci 67013498266Sopenharmony_ci if(dohlen < (index + 2)) 67113498266Sopenharmony_ci return DOH_DNS_OUT_OF_RANGE; 67213498266Sopenharmony_ci 67313498266Sopenharmony_ci rdlength = get16bit(doh, index); 67413498266Sopenharmony_ci index += 2; 67513498266Sopenharmony_ci if(dohlen < (index + rdlength)) 67613498266Sopenharmony_ci return DOH_DNS_OUT_OF_RANGE; 67713498266Sopenharmony_ci 67813498266Sopenharmony_ci rc = rdata(doh, dohlen, rdlength, type, index, d); 67913498266Sopenharmony_ci if(rc) 68013498266Sopenharmony_ci return rc; /* bad rdata */ 68113498266Sopenharmony_ci index += rdlength; 68213498266Sopenharmony_ci ancount--; 68313498266Sopenharmony_ci } 68413498266Sopenharmony_ci 68513498266Sopenharmony_ci nscount = get16bit(doh, 8); 68613498266Sopenharmony_ci while(nscount) { 68713498266Sopenharmony_ci rc = skipqname(doh, dohlen, &index); 68813498266Sopenharmony_ci if(rc) 68913498266Sopenharmony_ci return rc; /* bad qname */ 69013498266Sopenharmony_ci 69113498266Sopenharmony_ci if(dohlen < (index + 8)) 69213498266Sopenharmony_ci return DOH_DNS_OUT_OF_RANGE; 69313498266Sopenharmony_ci 69413498266Sopenharmony_ci index += 2 + 2 + 4; /* type, class and ttl */ 69513498266Sopenharmony_ci 69613498266Sopenharmony_ci if(dohlen < (index + 2)) 69713498266Sopenharmony_ci return DOH_DNS_OUT_OF_RANGE; 69813498266Sopenharmony_ci 69913498266Sopenharmony_ci rdlength = get16bit(doh, index); 70013498266Sopenharmony_ci index += 2; 70113498266Sopenharmony_ci if(dohlen < (index + rdlength)) 70213498266Sopenharmony_ci return DOH_DNS_OUT_OF_RANGE; 70313498266Sopenharmony_ci index += rdlength; 70413498266Sopenharmony_ci nscount--; 70513498266Sopenharmony_ci } 70613498266Sopenharmony_ci 70713498266Sopenharmony_ci arcount = get16bit(doh, 10); 70813498266Sopenharmony_ci while(arcount) { 70913498266Sopenharmony_ci rc = skipqname(doh, dohlen, &index); 71013498266Sopenharmony_ci if(rc) 71113498266Sopenharmony_ci return rc; /* bad qname */ 71213498266Sopenharmony_ci 71313498266Sopenharmony_ci if(dohlen < (index + 8)) 71413498266Sopenharmony_ci return DOH_DNS_OUT_OF_RANGE; 71513498266Sopenharmony_ci 71613498266Sopenharmony_ci index += 2 + 2 + 4; /* type, class and ttl */ 71713498266Sopenharmony_ci 71813498266Sopenharmony_ci if(dohlen < (index + 2)) 71913498266Sopenharmony_ci return DOH_DNS_OUT_OF_RANGE; 72013498266Sopenharmony_ci 72113498266Sopenharmony_ci rdlength = get16bit(doh, index); 72213498266Sopenharmony_ci index += 2; 72313498266Sopenharmony_ci if(dohlen < (index + rdlength)) 72413498266Sopenharmony_ci return DOH_DNS_OUT_OF_RANGE; 72513498266Sopenharmony_ci index += rdlength; 72613498266Sopenharmony_ci arcount--; 72713498266Sopenharmony_ci } 72813498266Sopenharmony_ci 72913498266Sopenharmony_ci if(index != dohlen) 73013498266Sopenharmony_ci return DOH_DNS_MALFORMAT; /* something is wrong */ 73113498266Sopenharmony_ci 73213498266Sopenharmony_ci if((type != DNS_TYPE_NS) && !d->numcname && !d->numaddr) 73313498266Sopenharmony_ci /* nothing stored! */ 73413498266Sopenharmony_ci return DOH_NO_CONTENT; 73513498266Sopenharmony_ci 73613498266Sopenharmony_ci return DOH_OK; /* ok */ 73713498266Sopenharmony_ci} 73813498266Sopenharmony_ci 73913498266Sopenharmony_ci#ifndef CURL_DISABLE_VERBOSE_STRINGS 74013498266Sopenharmony_cistatic void showdoh(struct Curl_easy *data, 74113498266Sopenharmony_ci const struct dohentry *d) 74213498266Sopenharmony_ci{ 74313498266Sopenharmony_ci int i; 74413498266Sopenharmony_ci infof(data, "TTL: %u seconds", d->ttl); 74513498266Sopenharmony_ci for(i = 0; i < d->numaddr; i++) { 74613498266Sopenharmony_ci const struct dohaddr *a = &d->addr[i]; 74713498266Sopenharmony_ci if(a->type == DNS_TYPE_A) { 74813498266Sopenharmony_ci infof(data, "DoH A: %u.%u.%u.%u", 74913498266Sopenharmony_ci a->ip.v4[0], a->ip.v4[1], 75013498266Sopenharmony_ci a->ip.v4[2], a->ip.v4[3]); 75113498266Sopenharmony_ci } 75213498266Sopenharmony_ci else if(a->type == DNS_TYPE_AAAA) { 75313498266Sopenharmony_ci int j; 75413498266Sopenharmony_ci char buffer[128]; 75513498266Sopenharmony_ci char *ptr; 75613498266Sopenharmony_ci size_t len; 75713498266Sopenharmony_ci msnprintf(buffer, 128, "DoH AAAA: "); 75813498266Sopenharmony_ci ptr = &buffer[10]; 75913498266Sopenharmony_ci len = 118; 76013498266Sopenharmony_ci for(j = 0; j < 16; j += 2) { 76113498266Sopenharmony_ci size_t l; 76213498266Sopenharmony_ci msnprintf(ptr, len, "%s%02x%02x", j?":":"", d->addr[i].ip.v6[j], 76313498266Sopenharmony_ci d->addr[i].ip.v6[j + 1]); 76413498266Sopenharmony_ci l = strlen(ptr); 76513498266Sopenharmony_ci len -= l; 76613498266Sopenharmony_ci ptr += l; 76713498266Sopenharmony_ci } 76813498266Sopenharmony_ci infof(data, "%s", buffer); 76913498266Sopenharmony_ci } 77013498266Sopenharmony_ci } 77113498266Sopenharmony_ci for(i = 0; i < d->numcname; i++) { 77213498266Sopenharmony_ci infof(data, "CNAME: %s", Curl_dyn_ptr(&d->cname[i])); 77313498266Sopenharmony_ci } 77413498266Sopenharmony_ci} 77513498266Sopenharmony_ci#else 77613498266Sopenharmony_ci#define showdoh(x,y) 77713498266Sopenharmony_ci#endif 77813498266Sopenharmony_ci 77913498266Sopenharmony_ci/* 78013498266Sopenharmony_ci * doh2ai() 78113498266Sopenharmony_ci * 78213498266Sopenharmony_ci * This function returns a pointer to the first element of a newly allocated 78313498266Sopenharmony_ci * Curl_addrinfo struct linked list filled with the data from a set of DoH 78413498266Sopenharmony_ci * lookups. Curl_addrinfo is meant to work like the addrinfo struct does for 78513498266Sopenharmony_ci * a IPv6 stack, but usable also for IPv4, all hosts and environments. 78613498266Sopenharmony_ci * 78713498266Sopenharmony_ci * The memory allocated by this function *MUST* be free'd later on calling 78813498266Sopenharmony_ci * Curl_freeaddrinfo(). For each successful call to this function there 78913498266Sopenharmony_ci * must be an associated call later to Curl_freeaddrinfo(). 79013498266Sopenharmony_ci */ 79113498266Sopenharmony_ci 79213498266Sopenharmony_cistatic CURLcode doh2ai(const struct dohentry *de, const char *hostname, 79313498266Sopenharmony_ci int port, struct Curl_addrinfo **aip) 79413498266Sopenharmony_ci{ 79513498266Sopenharmony_ci struct Curl_addrinfo *ai; 79613498266Sopenharmony_ci struct Curl_addrinfo *prevai = NULL; 79713498266Sopenharmony_ci struct Curl_addrinfo *firstai = NULL; 79813498266Sopenharmony_ci struct sockaddr_in *addr; 79913498266Sopenharmony_ci#ifdef ENABLE_IPV6 80013498266Sopenharmony_ci struct sockaddr_in6 *addr6; 80113498266Sopenharmony_ci#endif 80213498266Sopenharmony_ci CURLcode result = CURLE_OK; 80313498266Sopenharmony_ci int i; 80413498266Sopenharmony_ci size_t hostlen = strlen(hostname) + 1; /* include null-terminator */ 80513498266Sopenharmony_ci 80613498266Sopenharmony_ci DEBUGASSERT(de); 80713498266Sopenharmony_ci 80813498266Sopenharmony_ci if(!de->numaddr) 80913498266Sopenharmony_ci return CURLE_COULDNT_RESOLVE_HOST; 81013498266Sopenharmony_ci 81113498266Sopenharmony_ci for(i = 0; i < de->numaddr; i++) { 81213498266Sopenharmony_ci size_t ss_size; 81313498266Sopenharmony_ci CURL_SA_FAMILY_T addrtype; 81413498266Sopenharmony_ci if(de->addr[i].type == DNS_TYPE_AAAA) { 81513498266Sopenharmony_ci#ifndef ENABLE_IPV6 81613498266Sopenharmony_ci /* we can't handle IPv6 addresses */ 81713498266Sopenharmony_ci continue; 81813498266Sopenharmony_ci#else 81913498266Sopenharmony_ci ss_size = sizeof(struct sockaddr_in6); 82013498266Sopenharmony_ci addrtype = AF_INET6; 82113498266Sopenharmony_ci#endif 82213498266Sopenharmony_ci } 82313498266Sopenharmony_ci else { 82413498266Sopenharmony_ci ss_size = sizeof(struct sockaddr_in); 82513498266Sopenharmony_ci addrtype = AF_INET; 82613498266Sopenharmony_ci } 82713498266Sopenharmony_ci 82813498266Sopenharmony_ci ai = calloc(1, sizeof(struct Curl_addrinfo) + ss_size + hostlen); 82913498266Sopenharmony_ci if(!ai) { 83013498266Sopenharmony_ci result = CURLE_OUT_OF_MEMORY; 83113498266Sopenharmony_ci break; 83213498266Sopenharmony_ci } 83313498266Sopenharmony_ci ai->ai_addr = (void *)((char *)ai + sizeof(struct Curl_addrinfo)); 83413498266Sopenharmony_ci ai->ai_canonname = (void *)((char *)ai->ai_addr + ss_size); 83513498266Sopenharmony_ci memcpy(ai->ai_canonname, hostname, hostlen); 83613498266Sopenharmony_ci 83713498266Sopenharmony_ci if(!firstai) 83813498266Sopenharmony_ci /* store the pointer we want to return from this function */ 83913498266Sopenharmony_ci firstai = ai; 84013498266Sopenharmony_ci 84113498266Sopenharmony_ci if(prevai) 84213498266Sopenharmony_ci /* make the previous entry point to this */ 84313498266Sopenharmony_ci prevai->ai_next = ai; 84413498266Sopenharmony_ci 84513498266Sopenharmony_ci ai->ai_family = addrtype; 84613498266Sopenharmony_ci 84713498266Sopenharmony_ci /* we return all names as STREAM, so when using this address for TFTP 84813498266Sopenharmony_ci the type must be ignored and conn->socktype be used instead! */ 84913498266Sopenharmony_ci ai->ai_socktype = SOCK_STREAM; 85013498266Sopenharmony_ci 85113498266Sopenharmony_ci ai->ai_addrlen = (curl_socklen_t)ss_size; 85213498266Sopenharmony_ci 85313498266Sopenharmony_ci /* leave the rest of the struct filled with zero */ 85413498266Sopenharmony_ci 85513498266Sopenharmony_ci switch(ai->ai_family) { 85613498266Sopenharmony_ci case AF_INET: 85713498266Sopenharmony_ci addr = (void *)ai->ai_addr; /* storage area for this info */ 85813498266Sopenharmony_ci DEBUGASSERT(sizeof(struct in_addr) == sizeof(de->addr[i].ip.v4)); 85913498266Sopenharmony_ci memcpy(&addr->sin_addr, &de->addr[i].ip.v4, sizeof(struct in_addr)); 86013498266Sopenharmony_ci addr->sin_family = addrtype; 86113498266Sopenharmony_ci addr->sin_port = htons((unsigned short)port); 86213498266Sopenharmony_ci break; 86313498266Sopenharmony_ci 86413498266Sopenharmony_ci#ifdef ENABLE_IPV6 86513498266Sopenharmony_ci case AF_INET6: 86613498266Sopenharmony_ci addr6 = (void *)ai->ai_addr; /* storage area for this info */ 86713498266Sopenharmony_ci DEBUGASSERT(sizeof(struct in6_addr) == sizeof(de->addr[i].ip.v6)); 86813498266Sopenharmony_ci memcpy(&addr6->sin6_addr, &de->addr[i].ip.v6, sizeof(struct in6_addr)); 86913498266Sopenharmony_ci addr6->sin6_family = addrtype; 87013498266Sopenharmony_ci addr6->sin6_port = htons((unsigned short)port); 87113498266Sopenharmony_ci break; 87213498266Sopenharmony_ci#endif 87313498266Sopenharmony_ci } 87413498266Sopenharmony_ci 87513498266Sopenharmony_ci prevai = ai; 87613498266Sopenharmony_ci } 87713498266Sopenharmony_ci 87813498266Sopenharmony_ci if(result) { 87913498266Sopenharmony_ci Curl_freeaddrinfo(firstai); 88013498266Sopenharmony_ci firstai = NULL; 88113498266Sopenharmony_ci } 88213498266Sopenharmony_ci *aip = firstai; 88313498266Sopenharmony_ci 88413498266Sopenharmony_ci return result; 88513498266Sopenharmony_ci} 88613498266Sopenharmony_ci 88713498266Sopenharmony_ci#ifndef CURL_DISABLE_VERBOSE_STRINGS 88813498266Sopenharmony_cistatic const char *type2name(DNStype dnstype) 88913498266Sopenharmony_ci{ 89013498266Sopenharmony_ci return (dnstype == DNS_TYPE_A)?"A":"AAAA"; 89113498266Sopenharmony_ci} 89213498266Sopenharmony_ci#endif 89313498266Sopenharmony_ci 89413498266Sopenharmony_ciUNITTEST void de_cleanup(struct dohentry *d) 89513498266Sopenharmony_ci{ 89613498266Sopenharmony_ci int i = 0; 89713498266Sopenharmony_ci for(i = 0; i < d->numcname; i++) { 89813498266Sopenharmony_ci Curl_dyn_free(&d->cname[i]); 89913498266Sopenharmony_ci } 90013498266Sopenharmony_ci} 90113498266Sopenharmony_ci 90213498266Sopenharmony_ciCURLcode Curl_doh_is_resolved(struct Curl_easy *data, 90313498266Sopenharmony_ci struct Curl_dns_entry **dnsp) 90413498266Sopenharmony_ci{ 90513498266Sopenharmony_ci CURLcode result; 90613498266Sopenharmony_ci struct dohdata *dohp = data->req.doh; 90713498266Sopenharmony_ci *dnsp = NULL; /* defaults to no response */ 90813498266Sopenharmony_ci if(!dohp) 90913498266Sopenharmony_ci return CURLE_OUT_OF_MEMORY; 91013498266Sopenharmony_ci 91113498266Sopenharmony_ci if(!dohp->probe[DOH_PROBE_SLOT_IPADDR_V4].easy && 91213498266Sopenharmony_ci !dohp->probe[DOH_PROBE_SLOT_IPADDR_V6].easy) { 91313498266Sopenharmony_ci failf(data, "Could not DoH-resolve: %s", data->state.async.hostname); 91413498266Sopenharmony_ci return CONN_IS_PROXIED(data->conn)?CURLE_COULDNT_RESOLVE_PROXY: 91513498266Sopenharmony_ci CURLE_COULDNT_RESOLVE_HOST; 91613498266Sopenharmony_ci } 91713498266Sopenharmony_ci else if(!dohp->pending) { 91813498266Sopenharmony_ci DOHcode rc[DOH_PROBE_SLOTS] = { 91913498266Sopenharmony_ci DOH_OK, DOH_OK 92013498266Sopenharmony_ci }; 92113498266Sopenharmony_ci struct dohentry de; 92213498266Sopenharmony_ci int slot; 92313498266Sopenharmony_ci /* remove DoH handles from multi handle and close them */ 92413498266Sopenharmony_ci for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) { 92513498266Sopenharmony_ci curl_multi_remove_handle(data->multi, dohp->probe[slot].easy); 92613498266Sopenharmony_ci Curl_close(&dohp->probe[slot].easy); 92713498266Sopenharmony_ci } 92813498266Sopenharmony_ci /* parse the responses, create the struct and return it! */ 92913498266Sopenharmony_ci de_init(&de); 93013498266Sopenharmony_ci for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) { 93113498266Sopenharmony_ci struct dnsprobe *p = &dohp->probe[slot]; 93213498266Sopenharmony_ci if(!p->dnstype) 93313498266Sopenharmony_ci continue; 93413498266Sopenharmony_ci rc[slot] = doh_decode(Curl_dyn_uptr(&p->serverdoh), 93513498266Sopenharmony_ci Curl_dyn_len(&p->serverdoh), 93613498266Sopenharmony_ci p->dnstype, 93713498266Sopenharmony_ci &de); 93813498266Sopenharmony_ci Curl_dyn_free(&p->serverdoh); 93913498266Sopenharmony_ci#ifndef CURL_DISABLE_VERBOSE_STRINGS 94013498266Sopenharmony_ci if(rc[slot]) { 94113498266Sopenharmony_ci infof(data, "DoH: %s type %s for %s", doh_strerror(rc[slot]), 94213498266Sopenharmony_ci type2name(p->dnstype), dohp->host); 94313498266Sopenharmony_ci } 94413498266Sopenharmony_ci#endif 94513498266Sopenharmony_ci } /* next slot */ 94613498266Sopenharmony_ci 94713498266Sopenharmony_ci result = CURLE_COULDNT_RESOLVE_HOST; /* until we know better */ 94813498266Sopenharmony_ci if(!rc[DOH_PROBE_SLOT_IPADDR_V4] || !rc[DOH_PROBE_SLOT_IPADDR_V6]) { 94913498266Sopenharmony_ci /* we have an address, of one kind or other */ 95013498266Sopenharmony_ci struct Curl_dns_entry *dns; 95113498266Sopenharmony_ci struct Curl_addrinfo *ai; 95213498266Sopenharmony_ci 95313498266Sopenharmony_ci infof(data, "DoH Host name: %s", dohp->host); 95413498266Sopenharmony_ci showdoh(data, &de); 95513498266Sopenharmony_ci 95613498266Sopenharmony_ci result = doh2ai(&de, dohp->host, dohp->port, &ai); 95713498266Sopenharmony_ci if(result) { 95813498266Sopenharmony_ci de_cleanup(&de); 95913498266Sopenharmony_ci return result; 96013498266Sopenharmony_ci } 96113498266Sopenharmony_ci 96213498266Sopenharmony_ci if(data->share) 96313498266Sopenharmony_ci Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); 96413498266Sopenharmony_ci 96513498266Sopenharmony_ci /* we got a response, store it in the cache */ 96613498266Sopenharmony_ci dns = Curl_cache_addr(data, ai, dohp->host, 0, dohp->port); 96713498266Sopenharmony_ci 96813498266Sopenharmony_ci if(data->share) 96913498266Sopenharmony_ci Curl_share_unlock(data, CURL_LOCK_DATA_DNS); 97013498266Sopenharmony_ci 97113498266Sopenharmony_ci if(!dns) { 97213498266Sopenharmony_ci /* returned failure, bail out nicely */ 97313498266Sopenharmony_ci Curl_freeaddrinfo(ai); 97413498266Sopenharmony_ci } 97513498266Sopenharmony_ci else { 97613498266Sopenharmony_ci data->state.async.dns = dns; 97713498266Sopenharmony_ci *dnsp = dns; 97813498266Sopenharmony_ci result = CURLE_OK; /* address resolution OK */ 97913498266Sopenharmony_ci } 98013498266Sopenharmony_ci } /* address processing done */ 98113498266Sopenharmony_ci 98213498266Sopenharmony_ci /* Now process any build-specific attributes retrieved from DNS */ 98313498266Sopenharmony_ci 98413498266Sopenharmony_ci /* All done */ 98513498266Sopenharmony_ci de_cleanup(&de); 98613498266Sopenharmony_ci Curl_safefree(data->req.doh); 98713498266Sopenharmony_ci return result; 98813498266Sopenharmony_ci 98913498266Sopenharmony_ci } /* !dohp->pending */ 99013498266Sopenharmony_ci 99113498266Sopenharmony_ci /* else wait for pending DoH transactions to complete */ 99213498266Sopenharmony_ci return CURLE_OK; 99313498266Sopenharmony_ci} 99413498266Sopenharmony_ci 99513498266Sopenharmony_ci#endif /* CURL_DISABLE_DOH */ 996