quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

doh.c (39721B)


      1 /***************************************************************************
      2  *                                  _   _ ____  _
      3  *  Project                     ___| | | |  _ \| |
      4  *                             / __| | | | |_) | |
      5  *                            | (__| |_| |  _ <| |___
      6  *                             \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
      9  *
     10  * This software is licensed as described in the file COPYING, which
     11  * you should have received as part of this distribution. The terms
     12  * are also available at https://curl.se/docs/copyright.html.
     13  *
     14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
     15  * copies of the Software, and permit persons to whom the Software is
     16  * furnished to do so, under the terms of the COPYING file.
     17  *
     18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
     19  * KIND, either express or implied.
     20  *
     21  * SPDX-License-Identifier: curl
     22  *
     23  ***************************************************************************/
     24 
     25 #include "curl_setup.h"
     26 
     27 #ifndef CURL_DISABLE_DOH
     28 
     29 #include "urldata.h"
     30 #include "curl_addrinfo.h"
     31 #include "doh.h"
     32 
     33 #include "sendf.h"
     34 #include "multiif.h"
     35 #include "url.h"
     36 #include "share.h"
     37 #include "curlx/base64.h"
     38 #include "connect.h"
     39 #include "strdup.h"
     40 #include "curlx/dynbuf.h"
     41 #include "escape.h"
     42 #include "urlapi-int.h"
     43 
     44 /* The last 3 #include files should be in this order */
     45 #include "curl_printf.h"
     46 #include "curl_memory.h"
     47 #include "memdebug.h"
     48 
     49 #define DNS_CLASS_IN 0x01
     50 
     51 #ifndef CURL_DISABLE_VERBOSE_STRINGS
     52 static const char * const errors[]={
     53   "",
     54   "Bad label",
     55   "Out of range",
     56   "Label loop",
     57   "Too small",
     58   "Out of memory",
     59   "RDATA length",
     60   "Malformat",
     61   "Bad RCODE",
     62   "Unexpected TYPE",
     63   "Unexpected CLASS",
     64   "No content",
     65   "Bad ID",
     66   "Name too long"
     67 };
     68 
     69 static const char *doh_strerror(DOHcode code)
     70 {
     71   if((code >= DOH_OK) && (code <= DOH_DNS_NAME_TOO_LONG))
     72     return errors[code];
     73   return "bad error code";
     74 }
     75 
     76 #endif /* !CURL_DISABLE_VERBOSE_STRINGS */
     77 
     78 /* @unittest 1655
     79  */
     80 UNITTEST DOHcode doh_req_encode(const char *host,
     81                                 DNStype dnstype,
     82                                 unsigned char *dnsp, /* buffer */
     83                                 size_t len,  /* buffer size */
     84                                 size_t *olen) /* output length */
     85 {
     86   const size_t hostlen = strlen(host);
     87   unsigned char *orig = dnsp;
     88   const char *hostp = host;
     89 
     90   /* The expected output length is 16 bytes more than the length of
     91    * the QNAME-encoding of the hostname.
     92    *
     93    * A valid DNS name may not contain a zero-length label, except at
     94    * the end. For this reason, a name beginning with a dot, or
     95    * containing a sequence of two or more consecutive dots, is invalid
     96    * and cannot be encoded as a QNAME.
     97    *
     98    * If the hostname ends with a trailing dot, the corresponding
     99    * QNAME-encoding is one byte longer than the hostname. If (as is
    100    * also valid) the hostname is shortened by the omission of the
    101    * trailing dot, then its QNAME-encoding will be two bytes longer
    102    * than the hostname.
    103    *
    104    * Each [ label, dot ] pair is encoded as [ length, label ],
    105    * preserving overall length. A final [ label ] without a dot is
    106    * also encoded as [ length, label ], increasing overall length
    107    * by one. The encoding is completed by appending a zero byte,
    108    * representing the zero-length root label, again increasing
    109    * the overall length by one.
    110    */
    111 
    112   size_t expected_len;
    113   DEBUGASSERT(hostlen);
    114   expected_len = 12 + 1 + hostlen + 4;
    115   if(host[hostlen-1]!='.')
    116     expected_len++;
    117 
    118   if(expected_len > DOH_MAX_DNSREQ_SIZE)
    119     return DOH_DNS_NAME_TOO_LONG;
    120 
    121   if(len < expected_len)
    122     return DOH_TOO_SMALL_BUFFER;
    123 
    124   *dnsp++ = 0; /* 16 bit id */
    125   *dnsp++ = 0;
    126   *dnsp++ = 0x01; /* |QR|   Opcode  |AA|TC|RD| Set the RD bit */
    127   *dnsp++ = '\0'; /* |RA|   Z    |   RCODE   |                */
    128   *dnsp++ = '\0';
    129   *dnsp++ = 1;    /* QDCOUNT (number of entries in the question section) */
    130   *dnsp++ = '\0';
    131   *dnsp++ = '\0'; /* ANCOUNT */
    132   *dnsp++ = '\0';
    133   *dnsp++ = '\0'; /* NSCOUNT */
    134   *dnsp++ = '\0';
    135   *dnsp++ = '\0'; /* ARCOUNT */
    136 
    137   /* encode each label and store it in the QNAME */
    138   while(*hostp) {
    139     size_t labellen;
    140     char *dot = strchr(hostp, '.');
    141     if(dot)
    142       labellen = dot - hostp;
    143     else
    144       labellen = strlen(hostp);
    145     if((labellen > 63) || (!labellen)) {
    146       /* label is too long or too short, error out */
    147       *olen = 0;
    148       return DOH_DNS_BAD_LABEL;
    149     }
    150     /* label is non-empty, process it */
    151     *dnsp++ = (unsigned char)labellen;
    152     memcpy(dnsp, hostp, labellen);
    153     dnsp += labellen;
    154     hostp += labellen;
    155     /* advance past dot, but only if there is one */
    156     if(dot)
    157       hostp++;
    158   } /* next label */
    159 
    160   *dnsp++ = 0; /* append zero-length label for root */
    161 
    162   /* There are assigned TYPE codes beyond 255: use range [1..65535]  */
    163   *dnsp++ = (unsigned char)(255 & (dnstype >> 8)); /* upper 8 bit TYPE */
    164   *dnsp++ = (unsigned char)(255 & dnstype);      /* lower 8 bit TYPE */
    165 
    166   *dnsp++ = '\0'; /* upper 8 bit CLASS */
    167   *dnsp++ = DNS_CLASS_IN; /* IN - "the Internet" */
    168 
    169   *olen = dnsp - orig;
    170 
    171   /* verify that our estimation of length is valid, since
    172    * this has led to buffer overflows in this function */
    173   DEBUGASSERT(*olen == expected_len);
    174   return DOH_OK;
    175 }
    176 
    177 static size_t
    178 doh_probe_write_cb(char *contents, size_t size, size_t nmemb, void *userp)
    179 {
    180   size_t realsize = size * nmemb;
    181   struct Curl_easy *data = userp;
    182   struct doh_request *doh_req = Curl_meta_get(data, CURL_EZM_DOH_PROBE);
    183   if(!doh_req)
    184     return CURL_WRITEFUNC_ERROR;
    185 
    186   if(curlx_dyn_addn(&doh_req->resp_body, contents, realsize))
    187     return 0;
    188 
    189   return realsize;
    190 }
    191 
    192 #if defined(USE_HTTPSRR) && defined(DEBUGBUILD)
    193 
    194 /* doh_print_buf truncates if the hex string will be more than this */
    195 #define LOCAL_PB_HEXMAX 400
    196 
    197 static void doh_print_buf(struct Curl_easy *data,
    198                           const char *prefix,
    199                           unsigned char *buf, size_t len)
    200 {
    201   unsigned char hexstr[LOCAL_PB_HEXMAX];
    202   size_t hlen = LOCAL_PB_HEXMAX;
    203   bool truncated = FALSE;
    204 
    205   if(len > (LOCAL_PB_HEXMAX / 2))
    206     truncated = TRUE;
    207   Curl_hexencode(buf, len, hexstr, hlen);
    208   if(!truncated)
    209     infof(data, "%s: len=%d, val=%s", prefix, (int)len, hexstr);
    210   else
    211     infof(data, "%s: len=%d (truncated)val=%s", prefix, (int)len, hexstr);
    212   return;
    213 }
    214 #endif
    215 
    216 /* called from multi when a sub transfer, e.g. doh probe, is done.
    217  * This looks up the the probe response at its meta CURL_EZM_DOH_PROBE
    218  * and copies the response body over to the struct at the master's
    219  * meta at CURL_EZM_DOH_MASTER. */
    220 static void doh_probe_done(struct Curl_easy *data,
    221                            struct Curl_easy *doh, CURLcode result)
    222 {
    223   struct doh_probes *dohp = data->state.async.doh;
    224   DEBUGASSERT(dohp);
    225   if(dohp) {
    226     struct doh_request *doh_req = Curl_meta_get(doh, CURL_EZM_DOH_PROBE);
    227     int i;
    228 
    229     for(i = 0; i < DOH_SLOT_COUNT; ++i) {
    230       if(dohp->probe_resp[i].probe_mid == doh->mid)
    231         break;
    232     }
    233     if(i >= DOH_SLOT_COUNT) {
    234       failf(data, "unknown sub request done");
    235       return;
    236     }
    237 
    238     dohp->pending--;
    239     infof(doh, "a DoH request is completed, %u to go", dohp->pending);
    240     dohp->probe_resp[i].result = result;
    241     /* We expect either the meta data still to exist or the sub request
    242      * to have already failed. */
    243     DEBUGASSERT(doh_req || result);
    244     if(doh_req) {
    245       if(!result) {
    246         dohp->probe_resp[i].dnstype = doh_req->dnstype;
    247         result = curlx_dyn_addn(&dohp->probe_resp[i].body,
    248                                 curlx_dyn_ptr(&doh_req->resp_body),
    249                                 curlx_dyn_len(&doh_req->resp_body));
    250         curlx_dyn_free(&doh_req->resp_body);
    251       }
    252       Curl_meta_remove(doh, CURL_EZM_DOH_PROBE);
    253     }
    254 
    255     if(result)
    256       infof(doh, "DoH request %s", curl_easy_strerror(result));
    257 
    258     if(!dohp->pending) {
    259       /* DoH completed, run the transfer picking up the results */
    260       Curl_multi_mark_dirty(data);
    261     }
    262   }
    263 }
    264 
    265 static void doh_probe_dtor(void *key, size_t klen, void *e)
    266 {
    267   (void)key;
    268   (void)klen;
    269   if(e) {
    270     struct doh_request *doh_req = e;
    271     curl_slist_free_all(doh_req->req_hds);
    272     curlx_dyn_free(&doh_req->resp_body);
    273     free(e);
    274   }
    275 }
    276 
    277 #define ERROR_CHECK_SETOPT(x,y)                         \
    278   do {                                                  \
    279     result = curl_easy_setopt((CURL *)doh, x, y);       \
    280     if(result &&                                        \
    281        result != CURLE_NOT_BUILT_IN &&                  \
    282        result != CURLE_UNKNOWN_OPTION)                  \
    283       goto error;                                       \
    284   } while(0)
    285 
    286 static CURLcode doh_probe_run(struct Curl_easy *data,
    287                               DNStype dnstype,
    288                               const char *host,
    289                               const char *url, CURLM *multi,
    290                               unsigned int *pmid)
    291 {
    292   struct Curl_easy *doh = NULL;
    293   CURLcode result = CURLE_OK;
    294   timediff_t timeout_ms;
    295   struct doh_request *doh_req;
    296   DOHcode d;
    297 
    298   *pmid = UINT_MAX;
    299 
    300   doh_req = calloc(1, sizeof(*doh_req));
    301   if(!doh_req)
    302     return CURLE_OUT_OF_MEMORY;
    303   doh_req->dnstype = dnstype;
    304   curlx_dyn_init(&doh_req->resp_body, DYN_DOH_RESPONSE);
    305 
    306   d = doh_req_encode(host, dnstype, doh_req->req_body,
    307                      sizeof(doh_req->req_body),
    308                      &doh_req->req_body_len);
    309   if(d) {
    310     failf(data, "Failed to encode DoH packet [%d]", d);
    311     result = CURLE_OUT_OF_MEMORY;
    312     goto error;
    313   }
    314 
    315   timeout_ms = Curl_timeleft(data, NULL, TRUE);
    316   if(timeout_ms <= 0) {
    317     result = CURLE_OPERATION_TIMEDOUT;
    318     goto error;
    319   }
    320 
    321   doh_req->req_hds =
    322     curl_slist_append(NULL, "Content-Type: application/dns-message");
    323   if(!doh_req->req_hds) {
    324     result = CURLE_OUT_OF_MEMORY;
    325     goto error;
    326   }
    327 
    328   /* Curl_open() is the internal version of curl_easy_init() */
    329   result = Curl_open(&doh);
    330   if(result)
    331     goto error;
    332 
    333   /* pass in the struct pointer via a local variable to please coverity and
    334      the gcc typecheck helpers */
    335 #ifndef CURL_DISABLE_VERBOSE_STRINGS
    336   doh->state.feat = &Curl_trc_feat_dns;
    337 #endif
    338   ERROR_CHECK_SETOPT(CURLOPT_URL, url);
    339   ERROR_CHECK_SETOPT(CURLOPT_DEFAULT_PROTOCOL, "https");
    340   ERROR_CHECK_SETOPT(CURLOPT_WRITEFUNCTION, doh_probe_write_cb);
    341   ERROR_CHECK_SETOPT(CURLOPT_WRITEDATA, doh);
    342   ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDS, doh_req->req_body);
    343   ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDSIZE, (long)doh_req->req_body_len);
    344   ERROR_CHECK_SETOPT(CURLOPT_HTTPHEADER, doh_req->req_hds);
    345 #ifdef USE_HTTP2
    346   ERROR_CHECK_SETOPT(CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS);
    347   ERROR_CHECK_SETOPT(CURLOPT_PIPEWAIT, 1L);
    348 #endif
    349 #ifndef DEBUGBUILD
    350   /* enforce HTTPS if not debug */
    351   ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, (long)CURLPROTO_HTTPS);
    352 #else
    353   /* in debug mode, also allow http */
    354   ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, (long)CURLPROTO_HTTP|CURLPROTO_HTTPS);
    355 #endif
    356   ERROR_CHECK_SETOPT(CURLOPT_TIMEOUT_MS, (long)timeout_ms);
    357   ERROR_CHECK_SETOPT(CURLOPT_SHARE, (CURLSH *)data->share);
    358   if(data->set.err && data->set.err != stderr)
    359     ERROR_CHECK_SETOPT(CURLOPT_STDERR, data->set.err);
    360   if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_dns))
    361     ERROR_CHECK_SETOPT(CURLOPT_VERBOSE, 1L);
    362   if(data->set.no_signal)
    363     ERROR_CHECK_SETOPT(CURLOPT_NOSIGNAL, 1L);
    364 
    365   ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYHOST,
    366     data->set.doh_verifyhost ? 2L : 0L);
    367   ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYPEER,
    368     data->set.doh_verifypeer ? 1L : 0L);
    369   ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYSTATUS,
    370     data->set.doh_verifystatus ? 1L : 0L);
    371 
    372   /* Inherit *some* SSL options from the user's transfer. This is a
    373      best-guess as to which options are needed for compatibility. #3661
    374 
    375      Note DoH does not inherit the user's proxy server so proxy SSL settings
    376      have no effect and are not inherited. If that changes then two new
    377      options should be added to check doh proxy insecure separately,
    378      CURLOPT_DOH_PROXY_SSL_VERIFYHOST and CURLOPT_DOH_PROXY_SSL_VERIFYPEER.
    379      */
    380   if(data->set.str[STRING_SSL_CAFILE]) {
    381     ERROR_CHECK_SETOPT(CURLOPT_CAINFO,
    382                        data->set.str[STRING_SSL_CAFILE]);
    383   }
    384   if(data->set.blobs[BLOB_CAINFO]) {
    385     ERROR_CHECK_SETOPT(CURLOPT_CAINFO_BLOB,
    386                        data->set.blobs[BLOB_CAINFO]);
    387   }
    388   if(data->set.str[STRING_SSL_CAPATH]) {
    389     ERROR_CHECK_SETOPT(CURLOPT_CAPATH,
    390                        data->set.str[STRING_SSL_CAPATH]);
    391   }
    392   if(data->set.str[STRING_SSL_CRLFILE]) {
    393     ERROR_CHECK_SETOPT(CURLOPT_CRLFILE,
    394                        data->set.str[STRING_SSL_CRLFILE]);
    395   }
    396   if(data->set.ssl.certinfo)
    397     ERROR_CHECK_SETOPT(CURLOPT_CERTINFO, 1L);
    398   if(data->set.ssl.fsslctx)
    399     ERROR_CHECK_SETOPT(CURLOPT_SSL_CTX_FUNCTION, data->set.ssl.fsslctx);
    400   if(data->set.ssl.fsslctxp)
    401     ERROR_CHECK_SETOPT(CURLOPT_SSL_CTX_DATA, data->set.ssl.fsslctxp);
    402   if(data->set.fdebug)
    403     ERROR_CHECK_SETOPT(CURLOPT_DEBUGFUNCTION, data->set.fdebug);
    404   if(data->set.debugdata)
    405     ERROR_CHECK_SETOPT(CURLOPT_DEBUGDATA, data->set.debugdata);
    406   if(data->set.str[STRING_SSL_EC_CURVES]) {
    407     ERROR_CHECK_SETOPT(CURLOPT_SSL_EC_CURVES,
    408                        data->set.str[STRING_SSL_EC_CURVES]);
    409   }
    410 
    411   (void)curl_easy_setopt(doh, CURLOPT_SSL_OPTIONS,
    412                          (long)data->set.ssl.primary.ssl_options);
    413 
    414   doh->state.internal = TRUE;
    415   doh->master_mid = data->mid; /* master transfer of this one */
    416 
    417   result = Curl_meta_set(doh, CURL_EZM_DOH_PROBE, doh_req, doh_probe_dtor);
    418   doh_req = NULL; /* call took ownership */
    419   if(result)
    420     goto error;
    421 
    422   /* DoH handles must not inherit private_data. The handles may be passed to
    423      the user via callbacks and the user will be able to identify them as
    424      internal handles because private data is not set. The user can then set
    425      private_data via CURLOPT_PRIVATE if they so choose. */
    426   DEBUGASSERT(!doh->set.private_data);
    427 
    428   if(curl_multi_add_handle(multi, doh))
    429     goto error;
    430 
    431   *pmid = doh->mid;
    432   return CURLE_OK;
    433 
    434 error:
    435   Curl_close(&doh);
    436   if(doh_req)
    437     doh_probe_dtor(NULL, 0, doh_req);
    438   return result;
    439 }
    440 
    441 /*
    442  * Curl_doh() resolves a name using DoH. It resolves a name and returns a
    443  * 'Curl_addrinfo *' with the address information.
    444  */
    445 
    446 struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
    447                                const char *hostname,
    448                                int port,
    449                                int ip_version,
    450                                int *waitp)
    451 {
    452   CURLcode result = CURLE_OK;
    453   struct doh_probes *dohp = NULL;
    454   struct connectdata *conn = data->conn;
    455   size_t i;
    456 
    457   DEBUGASSERT(conn);
    458   DEBUGASSERT(!data->state.async.doh);
    459   if(data->state.async.doh)
    460     Curl_doh_cleanup(data);
    461 
    462   data->state.async.done = FALSE;
    463   data->state.async.port = port;
    464   data->state.async.ip_version = ip_version;
    465   data->state.async.hostname = strdup(hostname);
    466   if(!data->state.async.hostname)
    467     return NULL;
    468 
    469   /* start clean, consider allocating this struct on demand */
    470   data->state.async.doh = dohp = calloc(1, sizeof(struct doh_probes));
    471   if(!dohp)
    472     return NULL;
    473 
    474   for(i = 0; i < DOH_SLOT_COUNT; ++i) {
    475     dohp->probe_resp[i].probe_mid = UINT_MAX;
    476     curlx_dyn_init(&dohp->probe_resp[i].body, DYN_DOH_RESPONSE);
    477   }
    478 
    479   conn->bits.doh = TRUE;
    480   dohp->host = data->state.async.hostname;
    481   dohp->port = data->state.async.port;
    482   /* We are making sub easy handles and want to be called back when
    483    * one is done. */
    484   data->sub_xfer_done = doh_probe_done;
    485 
    486   /* create IPv4 DoH request */
    487   result = doh_probe_run(data, DNS_TYPE_A,
    488                          hostname, data->set.str[STRING_DOH],
    489                          data->multi,
    490                          &dohp->probe_resp[DOH_SLOT_IPV4].probe_mid);
    491   if(result)
    492     goto error;
    493   dohp->pending++;
    494 
    495 #ifdef USE_IPV6
    496   if((ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
    497     /* create IPv6 DoH request */
    498     result = doh_probe_run(data, DNS_TYPE_AAAA,
    499                            hostname, data->set.str[STRING_DOH],
    500                            data->multi,
    501                            &dohp->probe_resp[DOH_SLOT_IPV6].probe_mid);
    502     if(result)
    503       goto error;
    504     dohp->pending++;
    505   }
    506 #endif
    507 
    508 #ifdef USE_HTTPSRR
    509   if(conn->handler->protocol & PROTO_FAMILY_HTTP) {
    510     /* Only use HTTPS RR for HTTP(S) transfers */
    511     char *qname = NULL;
    512     if(port != PORT_HTTPS) {
    513       qname = aprintf("_%d._https.%s", port, hostname);
    514       if(!qname)
    515         goto error;
    516     }
    517     result = doh_probe_run(data, DNS_TYPE_HTTPS,
    518                            qname ? qname : hostname, data->set.str[STRING_DOH],
    519                            data->multi,
    520                            &dohp->probe_resp[DOH_SLOT_HTTPS_RR].probe_mid);
    521     free(qname);
    522     if(result)
    523       goto error;
    524     dohp->pending++;
    525   }
    526 #endif
    527   *waitp = TRUE; /* this never returns synchronously */
    528   return NULL;
    529 
    530 error:
    531   Curl_doh_cleanup(data);
    532   return NULL;
    533 }
    534 
    535 static DOHcode doh_skipqname(const unsigned char *doh, size_t dohlen,
    536                              unsigned int *indexp)
    537 {
    538   unsigned char length;
    539   do {
    540     if(dohlen < (*indexp + 1))
    541       return DOH_DNS_OUT_OF_RANGE;
    542     length = doh[*indexp];
    543     if((length & 0xc0) == 0xc0) {
    544       /* name pointer, advance over it and be done */
    545       if(dohlen < (*indexp + 2))
    546         return DOH_DNS_OUT_OF_RANGE;
    547       *indexp += 2;
    548       break;
    549     }
    550     if(length & 0xc0)
    551       return DOH_DNS_BAD_LABEL;
    552     if(dohlen < (*indexp + 1 + length))
    553       return DOH_DNS_OUT_OF_RANGE;
    554     *indexp += (unsigned int)(1 + length);
    555   } while(length);
    556   return DOH_OK;
    557 }
    558 
    559 static unsigned short doh_get16bit(const unsigned char *doh,
    560                                    unsigned int index)
    561 {
    562   return (unsigned short)((doh[index] << 8) | doh[index + 1]);
    563 }
    564 
    565 static unsigned int doh_get32bit(const unsigned char *doh, unsigned int index)
    566 {
    567   /* make clang and gcc optimize this to bswap by incrementing
    568      the pointer first. */
    569   doh += index;
    570 
    571   /* avoid undefined behavior by casting to unsigned before shifting
    572      24 bits, possibly into the sign bit. codegen is same, but
    573      ub sanitizer will not be upset */
    574   return ((unsigned)doh[0] << 24) | ((unsigned)doh[1] << 16) |
    575          ((unsigned)doh[2] << 8) | doh[3];
    576 }
    577 
    578 static void doh_store_a(const unsigned char *doh, int index,
    579                         struct dohentry *d)
    580 {
    581   /* silently ignore addresses over the limit */
    582   if(d->numaddr < DOH_MAX_ADDR) {
    583     struct dohaddr *a = &d->addr[d->numaddr];
    584     a->type = DNS_TYPE_A;
    585     memcpy(&a->ip.v4, &doh[index], 4);
    586     d->numaddr++;
    587   }
    588 }
    589 
    590 static void doh_store_aaaa(const unsigned char *doh, int index,
    591                            struct dohentry *d)
    592 {
    593   /* silently ignore addresses over the limit */
    594   if(d->numaddr < DOH_MAX_ADDR) {
    595     struct dohaddr *a = &d->addr[d->numaddr];
    596     a->type = DNS_TYPE_AAAA;
    597     memcpy(&a->ip.v6, &doh[index], 16);
    598     d->numaddr++;
    599   }
    600 }
    601 
    602 #ifdef USE_HTTPSRR
    603 static DOHcode doh_store_https(const unsigned char *doh, int index,
    604                                struct dohentry *d, uint16_t len)
    605 {
    606   /* silently ignore RRs over the limit */
    607   if(d->numhttps_rrs < DOH_MAX_HTTPS) {
    608     struct dohhttps_rr *h = &d->https_rrs[d->numhttps_rrs];
    609     h->val = Curl_memdup(&doh[index], len);
    610     if(!h->val)
    611       return DOH_OUT_OF_MEM;
    612     h->len = len;
    613     d->numhttps_rrs++;
    614   }
    615   return DOH_OK;
    616 }
    617 #endif
    618 
    619 static DOHcode doh_store_cname(const unsigned char *doh, size_t dohlen,
    620                                unsigned int index, struct dohentry *d)
    621 {
    622   struct dynbuf *c;
    623   unsigned int loop = 128; /* a valid DNS name can never loop this much */
    624   unsigned char length;
    625 
    626   if(d->numcname == DOH_MAX_CNAME)
    627     return DOH_OK; /* skip! */
    628 
    629   c = &d->cname[d->numcname++];
    630   do {
    631     if(index >= dohlen)
    632       return DOH_DNS_OUT_OF_RANGE;
    633     length = doh[index];
    634     if((length & 0xc0) == 0xc0) {
    635       int newpos;
    636       /* name pointer, get the new offset (14 bits) */
    637       if((index + 1) >= dohlen)
    638         return DOH_DNS_OUT_OF_RANGE;
    639 
    640       /* move to the new index */
    641       newpos = (length & 0x3f) << 8 | doh[index + 1];
    642       index = (unsigned int)newpos;
    643       continue;
    644     }
    645     else if(length & 0xc0)
    646       return DOH_DNS_BAD_LABEL; /* bad input */
    647     else
    648       index++;
    649 
    650     if(length) {
    651       if(curlx_dyn_len(c)) {
    652         if(curlx_dyn_addn(c, STRCONST(".")))
    653           return DOH_OUT_OF_MEM;
    654       }
    655       if((index + length) > dohlen)
    656         return DOH_DNS_BAD_LABEL;
    657 
    658       if(curlx_dyn_addn(c, &doh[index], length))
    659         return DOH_OUT_OF_MEM;
    660       index += length;
    661     }
    662   } while(length && --loop);
    663 
    664   if(!loop)
    665     return DOH_DNS_LABEL_LOOP;
    666   return DOH_OK;
    667 }
    668 
    669 static DOHcode doh_rdata(const unsigned char *doh,
    670                          size_t dohlen,
    671                          unsigned short rdlength,
    672                          unsigned short type,
    673                          int index,
    674                          struct dohentry *d)
    675 {
    676   /* RDATA
    677      - A (TYPE 1):  4 bytes
    678      - AAAA (TYPE 28): 16 bytes
    679      - NS (TYPE 2): N bytes
    680      - HTTPS (TYPE 65): N bytes */
    681   DOHcode rc;
    682 
    683   switch(type) {
    684   case DNS_TYPE_A:
    685     if(rdlength != 4)
    686       return DOH_DNS_RDATA_LEN;
    687     doh_store_a(doh, index, d);
    688     break;
    689   case DNS_TYPE_AAAA:
    690     if(rdlength != 16)
    691       return DOH_DNS_RDATA_LEN;
    692     doh_store_aaaa(doh, index, d);
    693     break;
    694 #ifdef USE_HTTPSRR
    695   case DNS_TYPE_HTTPS:
    696     rc = doh_store_https(doh, index, d, rdlength);
    697     if(rc)
    698       return rc;
    699     break;
    700 #endif
    701   case DNS_TYPE_CNAME:
    702     rc = doh_store_cname(doh, dohlen, (unsigned int)index, d);
    703     if(rc)
    704       return rc;
    705     break;
    706   case DNS_TYPE_DNAME:
    707     /* explicit for clarity; just skip; rely on synthesized CNAME  */
    708     break;
    709   default:
    710     /* unsupported type, just skip it */
    711     break;
    712   }
    713   return DOH_OK;
    714 }
    715 
    716 UNITTEST void de_init(struct dohentry *de)
    717 {
    718   int i;
    719   memset(de, 0, sizeof(*de));
    720   de->ttl = INT_MAX;
    721   for(i = 0; i < DOH_MAX_CNAME; i++)
    722     curlx_dyn_init(&de->cname[i], DYN_DOH_CNAME);
    723 }
    724 
    725 
    726 UNITTEST DOHcode doh_resp_decode(const unsigned char *doh,
    727                                  size_t dohlen,
    728                                  DNStype dnstype,
    729                                  struct dohentry *d)
    730 {
    731   unsigned char rcode;
    732   unsigned short qdcount;
    733   unsigned short ancount;
    734   unsigned short type = 0;
    735   unsigned short rdlength;
    736   unsigned short nscount;
    737   unsigned short arcount;
    738   unsigned int index = 12;
    739   DOHcode rc;
    740 
    741   if(dohlen < 12)
    742     return DOH_TOO_SMALL_BUFFER; /* too small */
    743   if(!doh || doh[0] || doh[1])
    744     return DOH_DNS_BAD_ID; /* bad ID */
    745   rcode = doh[3] & 0x0f;
    746   if(rcode)
    747     return DOH_DNS_BAD_RCODE; /* bad rcode */
    748 
    749   qdcount = doh_get16bit(doh, 4);
    750   while(qdcount) {
    751     rc = doh_skipqname(doh, dohlen, &index);
    752     if(rc)
    753       return rc; /* bad qname */
    754     if(dohlen < (index + 4))
    755       return DOH_DNS_OUT_OF_RANGE;
    756     index += 4; /* skip question's type and class */
    757     qdcount--;
    758   }
    759 
    760   ancount = doh_get16bit(doh, 6);
    761   while(ancount) {
    762     unsigned short class;
    763     unsigned int ttl;
    764 
    765     rc = doh_skipqname(doh, dohlen, &index);
    766     if(rc)
    767       return rc; /* bad qname */
    768 
    769     if(dohlen < (index + 2))
    770       return DOH_DNS_OUT_OF_RANGE;
    771 
    772     type = doh_get16bit(doh, index);
    773     if((type != DNS_TYPE_CNAME)    /* may be synthesized from DNAME */
    774        && (type != DNS_TYPE_DNAME) /* if present, accept and ignore */
    775        && (type != dnstype))
    776       /* Not the same type as was asked for nor CNAME nor DNAME */
    777       return DOH_DNS_UNEXPECTED_TYPE;
    778     index += 2;
    779 
    780     if(dohlen < (index + 2))
    781       return DOH_DNS_OUT_OF_RANGE;
    782     class = doh_get16bit(doh, index);
    783     if(DNS_CLASS_IN != class)
    784       return DOH_DNS_UNEXPECTED_CLASS; /* unsupported */
    785     index += 2;
    786 
    787     if(dohlen < (index + 4))
    788       return DOH_DNS_OUT_OF_RANGE;
    789 
    790     ttl = doh_get32bit(doh, index);
    791     if(ttl < d->ttl)
    792       d->ttl = ttl;
    793     index += 4;
    794 
    795     if(dohlen < (index + 2))
    796       return DOH_DNS_OUT_OF_RANGE;
    797 
    798     rdlength = doh_get16bit(doh, index);
    799     index += 2;
    800     if(dohlen < (index + rdlength))
    801       return DOH_DNS_OUT_OF_RANGE;
    802 
    803     rc = doh_rdata(doh, dohlen, rdlength, type, (int)index, d);
    804     if(rc)
    805       return rc; /* bad doh_rdata */
    806     index += rdlength;
    807     ancount--;
    808   }
    809 
    810   nscount = doh_get16bit(doh, 8);
    811   while(nscount) {
    812     rc = doh_skipqname(doh, dohlen, &index);
    813     if(rc)
    814       return rc; /* bad qname */
    815 
    816     if(dohlen < (index + 8))
    817       return DOH_DNS_OUT_OF_RANGE;
    818 
    819     index += 2 + 2 + 4; /* type, class and ttl */
    820 
    821     if(dohlen < (index + 2))
    822       return DOH_DNS_OUT_OF_RANGE;
    823 
    824     rdlength = doh_get16bit(doh, index);
    825     index += 2;
    826     if(dohlen < (index + rdlength))
    827       return DOH_DNS_OUT_OF_RANGE;
    828     index += rdlength;
    829     nscount--;
    830   }
    831 
    832   arcount = doh_get16bit(doh, 10);
    833   while(arcount) {
    834     rc = doh_skipqname(doh, dohlen, &index);
    835     if(rc)
    836       return rc; /* bad qname */
    837 
    838     if(dohlen < (index + 8))
    839       return DOH_DNS_OUT_OF_RANGE;
    840 
    841     index += 2 + 2 + 4; /* type, class and ttl */
    842 
    843     if(dohlen < (index + 2))
    844       return DOH_DNS_OUT_OF_RANGE;
    845 
    846     rdlength = doh_get16bit(doh, index);
    847     index += 2;
    848     if(dohlen < (index + rdlength))
    849       return DOH_DNS_OUT_OF_RANGE;
    850     index += rdlength;
    851     arcount--;
    852   }
    853 
    854   if(index != dohlen)
    855     return DOH_DNS_MALFORMAT; /* something is wrong */
    856 
    857 #ifdef USE_HTTTPS
    858   if((type != DNS_TYPE_NS) && !d->numcname && !d->numaddr && !d->numhttps_rrs)
    859 #else
    860   if((type != DNS_TYPE_NS) && !d->numcname && !d->numaddr)
    861 #endif
    862     /* nothing stored! */
    863     return DOH_NO_CONTENT;
    864 
    865   return DOH_OK; /* ok */
    866 }
    867 
    868 #ifndef CURL_DISABLE_VERBOSE_STRINGS
    869 static void doh_show(struct Curl_easy *data,
    870                      const struct dohentry *d)
    871 {
    872   int i;
    873   infof(data, "[DoH] TTL: %u seconds", d->ttl);
    874   for(i = 0; i < d->numaddr; i++) {
    875     const struct dohaddr *a = &d->addr[i];
    876     if(a->type == DNS_TYPE_A) {
    877       infof(data, "[DoH] A: %u.%u.%u.%u",
    878             a->ip.v4[0], a->ip.v4[1],
    879             a->ip.v4[2], a->ip.v4[3]);
    880     }
    881     else if(a->type == DNS_TYPE_AAAA) {
    882       int j;
    883       char buffer[128] = "[DoH] AAAA: ";
    884       size_t len = strlen(buffer);
    885       char *ptr = &buffer[len];
    886       len = sizeof(buffer) - len;
    887       for(j = 0; j < 16; j += 2) {
    888         size_t l;
    889         msnprintf(ptr, len, "%s%02x%02x", j ? ":" : "", d->addr[i].ip.v6[j],
    890                   d->addr[i].ip.v6[j + 1]);
    891         l = strlen(ptr);
    892         len -= l;
    893         ptr += l;
    894       }
    895       infof(data, "%s", buffer);
    896     }
    897   }
    898 #ifdef USE_HTTPSRR
    899   for(i = 0; i < d->numhttps_rrs; i++) {
    900 # ifdef DEBUGBUILD
    901     doh_print_buf(data, "DoH HTTPS",
    902                   d->https_rrs[i].val, d->https_rrs[i].len);
    903 # else
    904     infof(data, "DoH HTTPS RR: length %d", d->https_rrs[i].len);
    905 # endif
    906   }
    907 #endif
    908   for(i = 0; i < d->numcname; i++) {
    909     infof(data, "CNAME: %s", curlx_dyn_ptr(&d->cname[i]));
    910   }
    911 }
    912 #else
    913 #define doh_show(x,y)
    914 #endif
    915 
    916 /*
    917  * doh2ai()
    918  *
    919  * This function returns a pointer to the first element of a newly allocated
    920  * Curl_addrinfo struct linked list filled with the data from a set of DoH
    921  * lookups. Curl_addrinfo is meant to work like the addrinfo struct does for
    922  * an IPv6 stack, but usable also for IPv4, all hosts and environments.
    923  *
    924  * The memory allocated by this function *MUST* be free'd later on calling
    925  * Curl_freeaddrinfo(). For each successful call to this function there
    926  * must be an associated call later to Curl_freeaddrinfo().
    927  */
    928 
    929 static CURLcode doh2ai(const struct dohentry *de, const char *hostname,
    930                        int port, struct Curl_addrinfo **aip)
    931 {
    932   struct Curl_addrinfo *ai;
    933   struct Curl_addrinfo *prevai = NULL;
    934   struct Curl_addrinfo *firstai = NULL;
    935   struct sockaddr_in *addr;
    936 #ifdef USE_IPV6
    937   struct sockaddr_in6 *addr6;
    938 #endif
    939   CURLcode result = CURLE_OK;
    940   int i;
    941   size_t hostlen = strlen(hostname) + 1; /* include null-terminator */
    942 
    943   DEBUGASSERT(de);
    944 
    945   if(!de->numaddr)
    946     return CURLE_COULDNT_RESOLVE_HOST;
    947 
    948   for(i = 0; i < de->numaddr; i++) {
    949     size_t ss_size;
    950     CURL_SA_FAMILY_T addrtype;
    951     if(de->addr[i].type == DNS_TYPE_AAAA) {
    952 #ifndef USE_IPV6
    953       /* we cannot handle IPv6 addresses */
    954       continue;
    955 #else
    956       ss_size = sizeof(struct sockaddr_in6);
    957       addrtype = AF_INET6;
    958 #endif
    959     }
    960     else {
    961       ss_size = sizeof(struct sockaddr_in);
    962       addrtype = AF_INET;
    963     }
    964 
    965     ai = calloc(1, sizeof(struct Curl_addrinfo) + ss_size + hostlen);
    966     if(!ai) {
    967       result = CURLE_OUT_OF_MEMORY;
    968       break;
    969     }
    970     ai->ai_addr = (void *)((char *)ai + sizeof(struct Curl_addrinfo));
    971     ai->ai_canonname = (void *)((char *)ai->ai_addr + ss_size);
    972     memcpy(ai->ai_canonname, hostname, hostlen);
    973 
    974     if(!firstai)
    975       /* store the pointer we want to return from this function */
    976       firstai = ai;
    977 
    978     if(prevai)
    979       /* make the previous entry point to this */
    980       prevai->ai_next = ai;
    981 
    982     ai->ai_family = addrtype;
    983 
    984     /* we return all names as STREAM, so when using this address for TFTP
    985        the type must be ignored and conn->socktype be used instead! */
    986     ai->ai_socktype = SOCK_STREAM;
    987 
    988     ai->ai_addrlen = (curl_socklen_t)ss_size;
    989 
    990     /* leave the rest of the struct filled with zero */
    991 
    992     switch(ai->ai_family) {
    993     case AF_INET:
    994       addr = (void *)ai->ai_addr; /* storage area for this info */
    995       DEBUGASSERT(sizeof(struct in_addr) == sizeof(de->addr[i].ip.v4));
    996       memcpy(&addr->sin_addr, &de->addr[i].ip.v4, sizeof(struct in_addr));
    997       addr->sin_family = (CURL_SA_FAMILY_T)addrtype;
    998       addr->sin_port = htons((unsigned short)port);
    999       break;
   1000 
   1001 #ifdef USE_IPV6
   1002     case AF_INET6:
   1003       addr6 = (void *)ai->ai_addr; /* storage area for this info */
   1004       DEBUGASSERT(sizeof(struct in6_addr) == sizeof(de->addr[i].ip.v6));
   1005       memcpy(&addr6->sin6_addr, &de->addr[i].ip.v6, sizeof(struct in6_addr));
   1006       addr6->sin6_family = (CURL_SA_FAMILY_T)addrtype;
   1007       addr6->sin6_port = htons((unsigned short)port);
   1008       break;
   1009 #endif
   1010     }
   1011 
   1012     prevai = ai;
   1013   }
   1014 
   1015   if(result) {
   1016     Curl_freeaddrinfo(firstai);
   1017     firstai = NULL;
   1018   }
   1019   *aip = firstai;
   1020 
   1021   return result;
   1022 }
   1023 
   1024 #ifndef CURL_DISABLE_VERBOSE_STRINGS
   1025 static const char *doh_type2name(DNStype dnstype)
   1026 {
   1027   switch(dnstype) {
   1028     case DNS_TYPE_A:
   1029       return "A";
   1030     case DNS_TYPE_AAAA:
   1031       return "AAAA";
   1032 #ifdef USE_HTTPSRR
   1033     case DNS_TYPE_HTTPS:
   1034       return "HTTPS";
   1035 #endif
   1036     default:
   1037        return "unknown";
   1038   }
   1039 }
   1040 #endif
   1041 
   1042 UNITTEST void de_cleanup(struct dohentry *d)
   1043 {
   1044   int i = 0;
   1045   for(i = 0; i < d->numcname; i++) {
   1046     curlx_dyn_free(&d->cname[i]);
   1047   }
   1048 #ifdef USE_HTTPSRR
   1049   for(i = 0; i < d->numhttps_rrs; i++)
   1050     Curl_safefree(d->https_rrs[i].val);
   1051 #endif
   1052 }
   1053 
   1054 #ifdef USE_HTTPSRR
   1055 
   1056 /*
   1057  * @brief decode the DNS name in a binary RRData
   1058  * @param buf points to the buffer (in/out)
   1059  * @param remaining points to the remaining buffer length (in/out)
   1060  * @param dnsname returns the string form name on success
   1061  * @return is 1 for success, error otherwise
   1062  *
   1063  * The encoding here is defined in
   1064  * https://tools.ietf.org/html/rfc1035#section-3.1
   1065  *
   1066  * The input buffer pointer will be modified so it points to
   1067  * just after the end of the DNS name encoding on output. (And
   1068  * that is why it is an "unsigned char **" :-)
   1069  */
   1070 static CURLcode doh_decode_rdata_name(const unsigned char **buf,
   1071                                       size_t *remaining, char **dnsname)
   1072 {
   1073   const unsigned char *cp = NULL;
   1074   size_t rem = 0;
   1075   unsigned char clen = 0; /* chunk len */
   1076   struct dynbuf thename;
   1077 
   1078   DEBUGASSERT(buf && remaining && dnsname);
   1079   if(!buf || !remaining || !dnsname || !*remaining)
   1080     return CURLE_OUT_OF_MEMORY;
   1081   curlx_dyn_init(&thename, CURL_MAXLEN_host_name);
   1082   rem = *remaining;
   1083   cp = *buf;
   1084   clen = *cp++;
   1085   if(clen == 0) {
   1086     /* special case - return "." as name */
   1087     if(curlx_dyn_addn(&thename, ".", 1))
   1088       return CURLE_OUT_OF_MEMORY;
   1089   }
   1090   while(clen) {
   1091     if(clen >= rem) {
   1092       curlx_dyn_free(&thename);
   1093       return CURLE_OUT_OF_MEMORY;
   1094     }
   1095     if(curlx_dyn_addn(&thename, cp, clen) ||
   1096        curlx_dyn_addn(&thename, ".", 1))
   1097       return CURLE_TOO_LARGE;
   1098 
   1099     cp += clen;
   1100     rem -= (clen + 1);
   1101     if(rem <= 0) {
   1102       curlx_dyn_free(&thename);
   1103       return CURLE_OUT_OF_MEMORY;
   1104     }
   1105     clen = *cp++;
   1106   }
   1107   *buf = cp;
   1108   *remaining = rem - 1;
   1109   *dnsname = curlx_dyn_ptr(&thename);
   1110   return CURLE_OK;
   1111 }
   1112 
   1113 UNITTEST CURLcode doh_resp_decode_httpsrr(struct Curl_easy *data,
   1114                                           const unsigned char *cp, size_t len,
   1115                                           struct Curl_https_rrinfo **hrr);
   1116 
   1117 /* @unittest 1658 */
   1118 UNITTEST CURLcode doh_resp_decode_httpsrr(struct Curl_easy *data,
   1119                                           const unsigned char *cp, size_t len,
   1120                                           struct Curl_https_rrinfo **hrr)
   1121 {
   1122   uint16_t pcode = 0, plen = 0;
   1123   uint32_t expected_min_pcode = 0;
   1124   struct Curl_https_rrinfo *lhrr = NULL;
   1125   char *dnsname = NULL;
   1126   CURLcode result = CURLE_OUT_OF_MEMORY;
   1127   size_t olen;
   1128 
   1129   *hrr = NULL;
   1130   if(len <= 2)
   1131     return CURLE_BAD_FUNCTION_ARGUMENT;
   1132   lhrr = calloc(1, sizeof(struct Curl_https_rrinfo));
   1133   if(!lhrr)
   1134     return CURLE_OUT_OF_MEMORY;
   1135   lhrr->priority = doh_get16bit(cp, 0);
   1136   cp += 2;
   1137   len -= 2;
   1138   if(doh_decode_rdata_name(&cp, &len, &dnsname) != CURLE_OK)
   1139     goto err;
   1140   lhrr->target = dnsname;
   1141   if(Curl_junkscan(dnsname, &olen, FALSE)) {
   1142     /* unacceptable hostname content */
   1143     result = CURLE_WEIRD_SERVER_REPLY;
   1144     goto err;
   1145   }
   1146   lhrr->port = -1; /* until set */
   1147   while(len >= 4) {
   1148     pcode = doh_get16bit(cp, 0);
   1149     plen = doh_get16bit(cp, 2);
   1150     cp += 4;
   1151     len -= 4;
   1152     if(pcode < expected_min_pcode || plen > len) {
   1153       result = CURLE_WEIRD_SERVER_REPLY;
   1154       goto err;
   1155     }
   1156     result = Curl_httpsrr_set(data, lhrr, pcode, cp, plen);
   1157     if(result)
   1158       goto err;
   1159     cp += plen;
   1160     len -= plen;
   1161     expected_min_pcode = pcode + 1;
   1162   }
   1163   DEBUGASSERT(!len);
   1164   *hrr = lhrr;
   1165   return CURLE_OK;
   1166 err:
   1167   Curl_httpsrr_cleanup(lhrr);
   1168   Curl_safefree(lhrr);
   1169   return result;
   1170 }
   1171 
   1172 #ifdef DEBUGBUILD
   1173 UNITTEST void doh_print_httpsrr(struct Curl_easy *data,
   1174                                 struct Curl_https_rrinfo *hrr);
   1175 
   1176 UNITTEST void doh_print_httpsrr(struct Curl_easy *data,
   1177                                 struct Curl_https_rrinfo *hrr)
   1178 {
   1179   DEBUGASSERT(hrr);
   1180   infof(data, "HTTPS RR: priority %d, target: %s",
   1181         hrr->priority, hrr->target);
   1182   if(hrr->alpns[0] != ALPN_none)
   1183     infof(data, "HTTPS RR: alpns %u %u %u %u",
   1184           hrr->alpns[0], hrr->alpns[1], hrr->alpns[2], hrr->alpns[3]);
   1185   else
   1186     infof(data, "HTTPS RR: no alpns");
   1187   if(hrr->no_def_alpn)
   1188     infof(data, "HTTPS RR: no_def_alpn set");
   1189   else
   1190     infof(data, "HTTPS RR: no_def_alpn not set");
   1191   if(hrr->ipv4hints) {
   1192     doh_print_buf(data, "HTTPS RR: ipv4hints",
   1193                   hrr->ipv4hints, hrr->ipv4hints_len);
   1194   }
   1195   else
   1196     infof(data, "HTTPS RR: no ipv4hints");
   1197   if(hrr->echconfiglist) {
   1198     doh_print_buf(data, "HTTPS RR: ECHConfigList",
   1199                   hrr->echconfiglist, hrr->echconfiglist_len);
   1200   }
   1201   else
   1202     infof(data, "HTTPS RR: no ECHConfigList");
   1203   if(hrr->ipv6hints) {
   1204     doh_print_buf(data, "HTTPS RR: ipv6hint",
   1205                   hrr->ipv6hints, hrr->ipv6hints_len);
   1206   }
   1207   else
   1208     infof(data, "HTTPS RR: no ipv6hints");
   1209   return;
   1210 }
   1211 # endif
   1212 #endif
   1213 
   1214 CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
   1215                               struct Curl_dns_entry **dnsp)
   1216 {
   1217   CURLcode result;
   1218   struct doh_probes *dohp = data->state.async.doh;
   1219   *dnsp = NULL; /* defaults to no response */
   1220   if(!dohp)
   1221     return CURLE_OUT_OF_MEMORY;
   1222 
   1223   if(dohp->probe_resp[DOH_SLOT_IPV4].probe_mid == UINT_MAX &&
   1224      dohp->probe_resp[DOH_SLOT_IPV6].probe_mid == UINT_MAX) {
   1225     failf(data, "Could not DoH-resolve: %s", dohp->host);
   1226     return CONN_IS_PROXIED(data->conn) ? CURLE_COULDNT_RESOLVE_PROXY :
   1227       CURLE_COULDNT_RESOLVE_HOST;
   1228   }
   1229   else if(!dohp->pending) {
   1230     DOHcode rc[DOH_SLOT_COUNT];
   1231     struct dohentry de;
   1232     int slot;
   1233 
   1234     /* Clear any result the might still be there */
   1235     Curl_resolv_unlink(data, &data->state.async.dns);
   1236 
   1237     memset(rc, 0, sizeof(rc));
   1238     /* remove DoH handles from multi handle and close them */
   1239     Curl_doh_close(data);
   1240     /* parse the responses, create the struct and return it! */
   1241     de_init(&de);
   1242     for(slot = 0; slot < DOH_SLOT_COUNT; slot++) {
   1243       struct doh_response *p = &dohp->probe_resp[slot];
   1244       if(!p->dnstype)
   1245         continue;
   1246       rc[slot] = doh_resp_decode(curlx_dyn_uptr(&p->body),
   1247                                  curlx_dyn_len(&p->body),
   1248                                  p->dnstype, &de);
   1249 #ifndef CURL_DISABLE_VERBOSE_STRINGS
   1250       if(rc[slot]) {
   1251         infof(data, "DoH: %s type %s for %s", doh_strerror(rc[slot]),
   1252               doh_type2name(p->dnstype), dohp->host);
   1253       }
   1254 #endif
   1255     } /* next slot */
   1256 
   1257     result = CURLE_COULDNT_RESOLVE_HOST; /* until we know better */
   1258     if(!rc[DOH_SLOT_IPV4] || !rc[DOH_SLOT_IPV6]) {
   1259       /* we have an address, of one kind or other */
   1260       struct Curl_dns_entry *dns;
   1261       struct Curl_addrinfo *ai;
   1262 
   1263 
   1264       if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_dns)) {
   1265         CURL_TRC_DNS(data, "hostname: %s", dohp->host);
   1266         doh_show(data, &de);
   1267       }
   1268 
   1269       result = doh2ai(&de, dohp->host, dohp->port, &ai);
   1270       if(result) {
   1271         de_cleanup(&de);
   1272         return result;
   1273       }
   1274 
   1275       /* we got a response, create a dns entry. */
   1276       dns = Curl_dnscache_mk_entry(data, ai, dohp->host, 0, dohp->port, FALSE);
   1277       if(dns) {
   1278         /* Now add and HTTPSRR information if we have */
   1279 #ifdef USE_HTTPSRR
   1280         if(de.numhttps_rrs > 0 && result == CURLE_OK) {
   1281           struct Curl_https_rrinfo *hrr = NULL;
   1282           result = doh_resp_decode_httpsrr(data, de.https_rrs->val,
   1283                                            de.https_rrs->len, &hrr);
   1284           if(result) {
   1285             infof(data, "Failed to decode HTTPS RR");
   1286             return result;
   1287           }
   1288           infof(data, "Some HTTPS RR to process");
   1289 # ifdef DEBUGBUILD
   1290           doh_print_httpsrr(data, hrr);
   1291 # endif
   1292           dns->hinfo = hrr;
   1293        }
   1294 #endif
   1295         /* and add the entry to the cache */
   1296         data->state.async.dns = dns;
   1297         result = Curl_dnscache_add(data, dns);
   1298         *dnsp = data->state.async.dns;
   1299       }
   1300     } /* address processing done */
   1301 
   1302     /* All done */
   1303     data->state.async.done = TRUE;
   1304     de_cleanup(&de);
   1305     Curl_doh_cleanup(data);
   1306     return result;
   1307 
   1308   } /* !dohp->pending */
   1309 
   1310   /* else wait for pending DoH transactions to complete */
   1311   return CURLE_OK;
   1312 }
   1313 
   1314 void Curl_doh_close(struct Curl_easy *data)
   1315 {
   1316   struct doh_probes *doh = data->state.async.doh;
   1317   if(doh && data->multi) {
   1318     struct Curl_easy *probe_data;
   1319     unsigned int mid;
   1320     size_t slot;
   1321     for(slot = 0; slot < DOH_SLOT_COUNT; slot++) {
   1322       mid = doh->probe_resp[slot].probe_mid;
   1323       if(mid == UINT_MAX)
   1324         continue;
   1325       doh->probe_resp[slot].probe_mid = UINT_MAX;
   1326       /* should have been called before data is removed from multi handle */
   1327       DEBUGASSERT(data->multi);
   1328       probe_data = data->multi ? Curl_multi_get_easy(data->multi, mid) :
   1329         NULL;
   1330       if(!probe_data) {
   1331         DEBUGF(infof(data, "Curl_doh_close: xfer for mid=%u not found!",
   1332                      doh->probe_resp[slot].probe_mid));
   1333         continue;
   1334       }
   1335       /* data->multi might already be reset at this time */
   1336       curl_multi_remove_handle(data->multi, probe_data);
   1337       Curl_close(&probe_data);
   1338     }
   1339     data->sub_xfer_done = NULL;
   1340   }
   1341 }
   1342 
   1343 void Curl_doh_cleanup(struct Curl_easy *data)
   1344 {
   1345   struct doh_probes *dohp = data->state.async.doh;
   1346   if(dohp) {
   1347     int i;
   1348     Curl_doh_close(data);
   1349     for(i = 0; i < DOH_SLOT_COUNT; ++i) {
   1350       curlx_dyn_free(&dohp->probe_resp[i].body);
   1351     }
   1352     Curl_safefree(data->state.async.doh);
   1353   }
   1354 }
   1355 
   1356 #endif /* CURL_DISABLE_DOH */