Home | History | Annotate | Download | only in lib
      1 /***************************************************************************
      2  *                                  _   _ ____  _
      3  *  Project                     ___| | | |  _ \| |
      4  *                             / __| | | | |_) | |
      5  *                            | (__| |_| |  _ <| |___
      6  *                             \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) 2018 - 2019, Daniel Stenberg, <daniel (at) 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.haxx.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  ***************************************************************************/
     22 
     23 #include "curl_setup.h"
     24 
     25 #include "urldata.h"
     26 #include "curl_addrinfo.h"
     27 #include "doh.h"
     28 
     29 #include "sendf.h"
     30 #include "multiif.h"
     31 #include "url.h"
     32 #include "share.h"
     33 #include "curl_base64.h"
     34 #include "connect.h"
     35 #include "strdup.h"
     36 /* The last 3 #include files should be in this order */
     37 #include "curl_printf.h"
     38 #include "curl_memory.h"
     39 #include "memdebug.h"
     40 
     41 #define DNS_CLASS_IN 0x01
     42 #define DOH_MAX_RESPONSE_SIZE 3000 /* bytes */
     43 
     44 #ifndef CURL_DISABLE_VERBOSE_STRINGS
     45 static const char * const errors[]={
     46   "",
     47   "Bad label",
     48   "Out of range",
     49   "Label loop",
     50   "Too small",
     51   "Out of memory",
     52   "RDATA length",
     53   "Malformat",
     54   "Bad RCODE",
     55   "Unexpected TYPE",
     56   "Unexpected CLASS",
     57   "No content",
     58   "Bad ID"
     59 };
     60 
     61 static const char *doh_strerror(DOHcode code)
     62 {
     63   if((code >= DOH_OK) && (code <= DOH_DNS_BAD_ID))
     64     return errors[code];
     65   return "bad error code";
     66 }
     67 #endif
     68 
     69 #ifdef DEBUGBUILD
     70 #define UNITTEST
     71 #else
     72 #define UNITTEST static
     73 #endif
     74 
     75 UNITTEST DOHcode doh_encode(const char *host,
     76                             DNStype dnstype,
     77                             unsigned char *dnsp, /* buffer */
     78                             size_t len,  /* buffer size */
     79                             size_t *olen) /* output length */
     80 {
     81   size_t hostlen = strlen(host);
     82   unsigned char *orig = dnsp;
     83   const char *hostp = host;
     84 
     85   if(len < (12 + hostlen + 4))
     86     return DOH_TOO_SMALL_BUFFER;
     87 
     88   *dnsp++ = 0; /* 16 bit id */
     89   *dnsp++ = 0;
     90   *dnsp++ = 0x01; /* |QR|   Opcode  |AA|TC|RD| Set the RD bit */
     91   *dnsp++ = '\0'; /* |RA|   Z    |   RCODE   |                */
     92   *dnsp++ = '\0';
     93   *dnsp++ = 1;    /* QDCOUNT (number of entries in the question section) */
     94   *dnsp++ = '\0';
     95   *dnsp++ = '\0'; /* ANCOUNT */
     96   *dnsp++ = '\0';
     97   *dnsp++ = '\0'; /* NSCOUNT */
     98   *dnsp++ = '\0';
     99   *dnsp++ = '\0'; /* ARCOUNT */
    100 
    101   /* store a QNAME */
    102   do {
    103     char *dot = strchr(hostp, '.');
    104     size_t labellen;
    105     bool found = false;
    106     if(dot) {
    107       found = true;
    108       labellen = dot - hostp;
    109     }
    110     else
    111       labellen = strlen(hostp);
    112     if(labellen > 63) {
    113       /* too long label, error out */
    114       *olen = 0;
    115       return DOH_DNS_BAD_LABEL;
    116     }
    117     *dnsp++ = (unsigned char)labellen;
    118     memcpy(dnsp, hostp, labellen);
    119     dnsp += labellen;
    120     hostp += labellen + 1;
    121     if(!found) {
    122       *dnsp++ = 0; /* terminating zero */
    123       break;
    124     }
    125   } while(1);
    126 
    127   *dnsp++ = '\0'; /* upper 8 bit TYPE */
    128   *dnsp++ = (unsigned char)dnstype;
    129   *dnsp++ = '\0'; /* upper 8 bit CLASS */
    130   *dnsp++ = DNS_CLASS_IN; /* IN - "the Internet" */
    131 
    132   *olen = dnsp - orig;
    133   return DOH_OK;
    134 }
    135 
    136 static size_t
    137 doh_write_cb(void *contents, size_t size, size_t nmemb, void *userp)
    138 {
    139   size_t realsize = size * nmemb;
    140   struct dohresponse *mem = (struct dohresponse *)userp;
    141 
    142   if((mem->size + realsize) > DOH_MAX_RESPONSE_SIZE)
    143     /* suspiciously much for us */
    144     return 0;
    145 
    146   mem->memory = Curl_saferealloc(mem->memory, mem->size + realsize);
    147   if(!mem->memory)
    148     /* out of memory! */
    149     return 0;
    150 
    151   memcpy(&(mem->memory[mem->size]), contents, realsize);
    152   mem->size += realsize;
    153 
    154   return realsize;
    155 }
    156 
    157 /* called from multi.c when this DOH transfer is complete */
    158 static int Curl_doh_done(struct Curl_easy *doh, CURLcode result)
    159 {
    160   struct Curl_easy *data = doh->set.dohfor;
    161   /* so one of the DOH request done for the 'data' transfer is now complete! */
    162   data->req.doh.pending--;
    163   infof(data, "a DOH request is completed, %u to go\n", data->req.doh.pending);
    164   if(result)
    165     infof(data, "DOH request %s\n", curl_easy_strerror(result));
    166 
    167   if(!data->req.doh.pending) {
    168     /* DOH completed */
    169     curl_slist_free_all(data->req.doh.headers);
    170     data->req.doh.headers = NULL;
    171     Curl_expire(data, 0, EXPIRE_RUN_NOW);
    172   }
    173   return 0;
    174 }
    175 
    176 #define ERROR_CHECK_SETOPT(x,y) \
    177 do {                                      \
    178   result = curl_easy_setopt(doh, x, y);   \
    179   if(result)                              \
    180     goto error;                           \
    181 } WHILE_FALSE
    182 
    183 static CURLcode dohprobe(struct Curl_easy *data,
    184                          struct dnsprobe *p, DNStype dnstype,
    185                          const char *host,
    186                          const char *url, CURLM *multi,
    187                          struct curl_slist *headers)
    188 {
    189   struct Curl_easy *doh = NULL;
    190   char *nurl = NULL;
    191   CURLcode result = CURLE_OK;
    192   timediff_t timeout_ms;
    193   DOHcode d = doh_encode(host, dnstype, p->dohbuffer, sizeof(p->dohbuffer),
    194                          &p->dohlen);
    195   if(d) {
    196     failf(data, "Failed to encode DOH packet [%d]\n", d);
    197     return CURLE_OUT_OF_MEMORY;
    198   }
    199 
    200   p->dnstype = dnstype;
    201   p->serverdoh.memory = NULL;
    202   /* the memory will be grown as needed by realloc in the doh_write_cb
    203      function */
    204   p->serverdoh.size = 0;
    205 
    206   /* Note: this is code for sending the DoH request with GET but there's still
    207      no logic that actually enables this. We should either add that ability or
    208      yank out the GET code. Discuss! */
    209   if(data->set.doh_get) {
    210     char *b64;
    211     size_t b64len;
    212     result = Curl_base64url_encode(data, (char *)p->dohbuffer, p->dohlen,
    213                                    &b64, &b64len);
    214     if(result)
    215       goto error;
    216     nurl = aprintf("%s?dns=%s", url, b64);
    217     free(b64);
    218     if(!nurl) {
    219       result = CURLE_OUT_OF_MEMORY;
    220       goto error;
    221     }
    222     url = nurl;
    223   }
    224 
    225   timeout_ms = Curl_timeleft(data, NULL, TRUE);
    226 
    227   /* Curl_open() is the internal version of curl_easy_init() */
    228   result = Curl_open(&doh);
    229   if(!result) {
    230     /* pass in the struct pointer via a local variable to please coverity and
    231        the gcc typecheck helpers */
    232     struct dohresponse *resp = &p->serverdoh;
    233     ERROR_CHECK_SETOPT(CURLOPT_URL, url);
    234     ERROR_CHECK_SETOPT(CURLOPT_WRITEFUNCTION, doh_write_cb);
    235     ERROR_CHECK_SETOPT(CURLOPT_WRITEDATA, resp);
    236     if(!data->set.doh_get) {
    237       ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDS, p->dohbuffer);
    238       ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDSIZE, (long)p->dohlen);
    239     }
    240     ERROR_CHECK_SETOPT(CURLOPT_HTTPHEADER, headers);
    241 #ifdef USE_NGHTTP2
    242     ERROR_CHECK_SETOPT(CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS);
    243 #endif
    244 #ifndef CURLDEBUG
    245     /* enforce HTTPS if not debug */
    246     ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTPS);
    247 #endif
    248     ERROR_CHECK_SETOPT(CURLOPT_TIMEOUT_MS, (long)timeout_ms);
    249     if(data->set.verbose)
    250       ERROR_CHECK_SETOPT(CURLOPT_VERBOSE, 1L);
    251     if(data->set.no_signal)
    252       ERROR_CHECK_SETOPT(CURLOPT_NOSIGNAL, 1L);
    253 
    254     /* Inherit *some* SSL options from the user's transfer. This is a
    255        best-guess as to which options are needed for compatibility. #3661 */
    256     if(data->set.ssl.falsestart)
    257       ERROR_CHECK_SETOPT(CURLOPT_SSL_FALSESTART, 1L);
    258     if(data->set.ssl.primary.verifyhost)
    259       ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYHOST, 2L);
    260     if(data->set.proxy_ssl.primary.verifyhost)
    261       ERROR_CHECK_SETOPT(CURLOPT_PROXY_SSL_VERIFYHOST, 2L);
    262     if(data->set.ssl.primary.verifypeer)
    263       ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYPEER, 1L);
    264     if(data->set.proxy_ssl.primary.verifypeer)
    265       ERROR_CHECK_SETOPT(CURLOPT_PROXY_SSL_VERIFYPEER, 1L);
    266     if(data->set.ssl.primary.verifystatus)
    267       ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYSTATUS, 1L);
    268     if(data->set.str[STRING_SSL_CAFILE_ORIG]) {
    269       ERROR_CHECK_SETOPT(CURLOPT_CAINFO,
    270         data->set.str[STRING_SSL_CAFILE_ORIG]);
    271     }
    272     if(data->set.str[STRING_SSL_CAFILE_PROXY]) {
    273       ERROR_CHECK_SETOPT(CURLOPT_PROXY_CAINFO,
    274         data->set.str[STRING_SSL_CAFILE_PROXY]);
    275     }
    276     if(data->set.str[STRING_SSL_CAPATH_ORIG]) {
    277       ERROR_CHECK_SETOPT(CURLOPT_CAPATH,
    278         data->set.str[STRING_SSL_CAPATH_ORIG]);
    279     }
    280     if(data->set.str[STRING_SSL_CAPATH_PROXY]) {
    281       ERROR_CHECK_SETOPT(CURLOPT_PROXY_CAPATH,
    282         data->set.str[STRING_SSL_CAPATH_PROXY]);
    283     }
    284     if(data->set.str[STRING_SSL_CRLFILE_ORIG]) {
    285       ERROR_CHECK_SETOPT(CURLOPT_CRLFILE,
    286         data->set.str[STRING_SSL_CRLFILE_ORIG]);
    287     }
    288     if(data->set.str[STRING_SSL_CRLFILE_PROXY]) {
    289       ERROR_CHECK_SETOPT(CURLOPT_PROXY_CRLFILE,
    290         data->set.str[STRING_SSL_CRLFILE_PROXY]);
    291     }
    292     if(data->set.ssl.certinfo)
    293       ERROR_CHECK_SETOPT(CURLOPT_CERTINFO, 1L);
    294     if(data->set.str[STRING_SSL_RANDOM_FILE]) {
    295       ERROR_CHECK_SETOPT(CURLOPT_RANDOM_FILE,
    296         data->set.str[STRING_SSL_RANDOM_FILE]);
    297     }
    298     if(data->set.str[STRING_SSL_EGDSOCKET]) {
    299       ERROR_CHECK_SETOPT(CURLOPT_EGDSOCKET,
    300         data->set.str[STRING_SSL_EGDSOCKET]);
    301     }
    302     if(data->set.ssl.no_revoke)
    303       ERROR_CHECK_SETOPT(CURLOPT_SSL_OPTIONS, CURLSSLOPT_NO_REVOKE);
    304     if(data->set.proxy_ssl.no_revoke)
    305       ERROR_CHECK_SETOPT(CURLOPT_PROXY_SSL_OPTIONS, CURLSSLOPT_NO_REVOKE);
    306     if(data->set.ssl.fsslctx)
    307       ERROR_CHECK_SETOPT(CURLOPT_SSL_CTX_FUNCTION, data->set.ssl.fsslctx);
    308     if(data->set.ssl.fsslctxp)
    309       ERROR_CHECK_SETOPT(CURLOPT_SSL_CTX_DATA, data->set.ssl.fsslctxp);
    310 
    311     doh->set.fmultidone = Curl_doh_done;
    312     doh->set.dohfor = data; /* identify for which transfer this is done */
    313     p->easy = doh;
    314 
    315     /* add this transfer to the multi handle */
    316     if(curl_multi_add_handle(multi, doh))
    317       goto error;
    318   }
    319   else
    320     goto error;
    321   free(nurl);
    322   return CURLE_OK;
    323 
    324   error:
    325   free(nurl);
    326   Curl_close(doh);
    327   return result;
    328 }
    329 
    330 /*
    331  * Curl_doh() resolves a name using DOH. It resolves a name and returns a
    332  * 'Curl_addrinfo *' with the address information.
    333  */
    334 
    335 Curl_addrinfo *Curl_doh(struct connectdata *conn,
    336                         const char *hostname,
    337                         int port,
    338                         int *waitp)
    339 {
    340   struct Curl_easy *data = conn->data;
    341   CURLcode result = CURLE_OK;
    342   *waitp = TRUE; /* this never returns synchronously */
    343   (void)conn;
    344   (void)hostname;
    345   (void)port;
    346 
    347   /* start clean, consider allocating this struct on demand */
    348   memset(&data->req.doh, 0, sizeof(struct dohdata));
    349 
    350   data->req.doh.host = hostname;
    351   data->req.doh.port = port;
    352   data->req.doh.headers =
    353     curl_slist_append(NULL,
    354                       "Content-Type: application/dns-message");
    355   if(!data->req.doh.headers)
    356     goto error;
    357 
    358   if(conn->ip_version != CURL_IPRESOLVE_V6) {
    359     /* create IPv4 DOH request */
    360     result = dohprobe(data, &data->req.doh.probe[0], DNS_TYPE_A,
    361                       hostname, data->set.str[STRING_DOH],
    362                       data->multi, data->req.doh.headers);
    363     if(result)
    364       goto error;
    365     data->req.doh.pending++;
    366   }
    367 
    368   if(conn->ip_version != CURL_IPRESOLVE_V4) {
    369     /* create IPv6 DOH request */
    370     result = dohprobe(data, &data->req.doh.probe[1], DNS_TYPE_AAAA,
    371                       hostname, data->set.str[STRING_DOH],
    372                       data->multi, data->req.doh.headers);
    373     if(result)
    374       goto error;
    375     data->req.doh.pending++;
    376   }
    377   return NULL;
    378 
    379   error:
    380   curl_slist_free_all(data->req.doh.headers);
    381   data->req.doh.headers = NULL;
    382   curl_easy_cleanup(data->req.doh.probe[0].easy);
    383   data->req.doh.probe[0].easy = NULL;
    384   curl_easy_cleanup(data->req.doh.probe[1].easy);
    385   data->req.doh.probe[1].easy = NULL;
    386   return NULL;
    387 }
    388 
    389 static DOHcode skipqname(unsigned char *doh, size_t dohlen,
    390                          unsigned int *indexp)
    391 {
    392   unsigned char length;
    393   do {
    394     if(dohlen < (*indexp + 1))
    395       return DOH_DNS_OUT_OF_RANGE;
    396     length = doh[*indexp];
    397     if((length & 0xc0) == 0xc0) {
    398       /* name pointer, advance over it and be done */
    399       if(dohlen < (*indexp + 2))
    400         return DOH_DNS_OUT_OF_RANGE;
    401       *indexp += 2;
    402       break;
    403     }
    404     if(length & 0xc0)
    405       return DOH_DNS_BAD_LABEL;
    406     if(dohlen < (*indexp + 1 + length))
    407       return DOH_DNS_OUT_OF_RANGE;
    408     *indexp += 1 + length;
    409   } while(length);
    410   return DOH_OK;
    411 }
    412 
    413 static unsigned short get16bit(unsigned char *doh, int index)
    414 {
    415   return (unsigned short)((doh[index] << 8) | doh[index + 1]);
    416 }
    417 
    418 static unsigned int get32bit(unsigned char *doh, int index)
    419 {
    420   return (doh[index] << 24) | (doh[index + 1] << 16) |
    421     (doh[index + 2] << 8) | doh[index + 3];
    422 }
    423 
    424 static DOHcode store_a(unsigned char *doh, int index, struct dohentry *d)
    425 {
    426   /* silently ignore addresses over the limit */
    427   if(d->numaddr < DOH_MAX_ADDR) {
    428     struct dohaddr *a = &d->addr[d->numaddr];
    429     a->type = DNS_TYPE_A;
    430     memcpy(&a->ip.v4, &doh[index], 4);
    431     d->numaddr++;
    432   }
    433   return DOH_OK;
    434 }
    435 
    436 static DOHcode store_aaaa(unsigned char *doh, int index, struct dohentry *d)
    437 {
    438   /* silently ignore addresses over the limit */
    439   if(d->numaddr < DOH_MAX_ADDR) {
    440     struct dohaddr *a = &d->addr[d->numaddr];
    441     a->type = DNS_TYPE_AAAA;
    442     memcpy(&a->ip.v6, &doh[index], 16);
    443     d->numaddr++;
    444   }
    445   return DOH_OK;
    446 }
    447 
    448 static DOHcode cnameappend(struct cnamestore *c,
    449                            unsigned char *src,
    450                            size_t len)
    451 {
    452   if(!c->alloc) {
    453     c->allocsize = len + 1;
    454     c->alloc = malloc(c->allocsize);
    455     if(!c->alloc)
    456       return DOH_OUT_OF_MEM;
    457   }
    458   else if(c->allocsize < (c->allocsize + len + 1)) {
    459     char *ptr;
    460     c->allocsize += len + 1;
    461     ptr = realloc(c->alloc, c->allocsize);
    462     if(!ptr) {
    463       free(c->alloc);
    464       return DOH_OUT_OF_MEM;
    465     }
    466     c->alloc = ptr;
    467   }
    468   memcpy(&c->alloc[c->len], src, len);
    469   c->len += len;
    470   c->alloc[c->len] = 0; /* keep it zero terminated */
    471   return DOH_OK;
    472 }
    473 
    474 static DOHcode store_cname(unsigned char *doh,
    475                            size_t dohlen,
    476                            unsigned int index,
    477                            struct dohentry *d)
    478 {
    479   struct cnamestore *c;
    480   unsigned int loop = 128; /* a valid DNS name can never loop this much */
    481   unsigned char length;
    482 
    483   if(d->numcname == DOH_MAX_CNAME)
    484     return DOH_OK; /* skip! */
    485 
    486   c = &d->cname[d->numcname++];
    487   do {
    488     if(index >= dohlen)
    489       return DOH_DNS_OUT_OF_RANGE;
    490     length = doh[index];
    491     if((length & 0xc0) == 0xc0) {
    492       int newpos;
    493       /* name pointer, get the new offset (14 bits) */
    494       if((index + 1) >= dohlen)
    495         return DOH_DNS_OUT_OF_RANGE;
    496 
    497       /* move to the the new index */
    498       newpos = (length & 0x3f) << 8 | doh[index + 1];
    499       index = newpos;
    500       continue;
    501     }
    502     else if(length & 0xc0)
    503       return DOH_DNS_BAD_LABEL; /* bad input */
    504     else
    505       index++;
    506 
    507     if(length) {
    508       DOHcode rc;
    509       if(c->len) {
    510         rc = cnameappend(c, (unsigned char *)".", 1);
    511         if(rc)
    512           return rc;
    513       }
    514       if((index + length) > dohlen)
    515         return DOH_DNS_BAD_LABEL;
    516 
    517       rc = cnameappend(c, &doh[index], length);
    518       if(rc)
    519         return rc;
    520       index += length;
    521     }
    522   } while(length && --loop);
    523 
    524   if(!loop)
    525     return DOH_DNS_LABEL_LOOP;
    526   return DOH_OK;
    527 }
    528 
    529 static DOHcode rdata(unsigned char *doh,
    530                      size_t dohlen,
    531                      unsigned short rdlength,
    532                      unsigned short type,
    533                      int index,
    534                      struct dohentry *d)
    535 {
    536   /* RDATA
    537      - A (TYPE 1):  4 bytes
    538      - AAAA (TYPE 28): 16 bytes
    539      - NS (TYPE 2): N bytes */
    540   DOHcode rc;
    541 
    542   switch(type) {
    543   case DNS_TYPE_A:
    544     if(rdlength != 4)
    545       return DOH_DNS_RDATA_LEN;
    546     rc = store_a(doh, index, d);
    547     if(rc)
    548       return rc;
    549     break;
    550   case DNS_TYPE_AAAA:
    551     if(rdlength != 16)
    552       return DOH_DNS_RDATA_LEN;
    553     rc = store_aaaa(doh, index, d);
    554     if(rc)
    555       return rc;
    556     break;
    557   case DNS_TYPE_CNAME:
    558     rc = store_cname(doh, dohlen, index, d);
    559     if(rc)
    560       return rc;
    561     break;
    562   default:
    563     /* unsupported type, just skip it */
    564     break;
    565   }
    566   return DOH_OK;
    567 }
    568 
    569 static void init_dohentry(struct dohentry *de)
    570 {
    571   memset(de, 0, sizeof(*de));
    572   de->ttl = INT_MAX;
    573 }
    574 
    575 
    576 UNITTEST DOHcode doh_decode(unsigned char *doh,
    577                             size_t dohlen,
    578                             DNStype dnstype,
    579                             struct dohentry *d)
    580 {
    581   unsigned char rcode;
    582   unsigned short qdcount;
    583   unsigned short ancount;
    584   unsigned short type = 0;
    585   unsigned short class;
    586   unsigned short rdlength;
    587   unsigned short nscount;
    588   unsigned short arcount;
    589   unsigned int index = 12;
    590   DOHcode rc;
    591 
    592   if(dohlen < 12)
    593     return DOH_TOO_SMALL_BUFFER; /* too small */
    594   if(!doh || doh[0] || doh[1])
    595     return DOH_DNS_BAD_ID; /* bad ID */
    596   rcode = doh[3] & 0x0f;
    597   if(rcode)
    598     return DOH_DNS_BAD_RCODE; /* bad rcode */
    599 
    600   qdcount = get16bit(doh, 4);
    601   while(qdcount) {
    602     rc = skipqname(doh, dohlen, &index);
    603     if(rc)
    604       return rc; /* bad qname */
    605     if(dohlen < (index + 4))
    606       return DOH_DNS_OUT_OF_RANGE;
    607     index += 4; /* skip question's type and class */
    608     qdcount--;
    609   }
    610 
    611   ancount = get16bit(doh, 6);
    612   while(ancount) {
    613     unsigned int ttl;
    614 
    615     rc = skipqname(doh, dohlen, &index);
    616     if(rc)
    617       return rc; /* bad qname */
    618 
    619     if(dohlen < (index + 2))
    620       return DOH_DNS_OUT_OF_RANGE;
    621 
    622     type = get16bit(doh, index);
    623     if((type != DNS_TYPE_CNAME) && (type != dnstype))
    624       /* Not the same type as was asked for nor CNAME */
    625       return DOH_DNS_UNEXPECTED_TYPE;
    626     index += 2;
    627 
    628     if(dohlen < (index + 2))
    629       return DOH_DNS_OUT_OF_RANGE;
    630     class = get16bit(doh, index);
    631     if(DNS_CLASS_IN != class)
    632       return DOH_DNS_UNEXPECTED_CLASS; /* unsupported */
    633     index += 2;
    634 
    635     if(dohlen < (index + 4))
    636       return DOH_DNS_OUT_OF_RANGE;
    637 
    638     ttl = get32bit(doh, index);
    639     if(ttl < d->ttl)
    640       d->ttl = ttl;
    641     index += 4;
    642 
    643     if(dohlen < (index + 2))
    644       return DOH_DNS_OUT_OF_RANGE;
    645 
    646     rdlength = get16bit(doh, index);
    647     index += 2;
    648     if(dohlen < (index + rdlength))
    649       return DOH_DNS_OUT_OF_RANGE;
    650 
    651     rc = rdata(doh, dohlen, rdlength, type, index, d);
    652     if(rc)
    653       return rc; /* bad rdata */
    654     index += rdlength;
    655     ancount--;
    656   }
    657 
    658   nscount = get16bit(doh, 8);
    659   while(nscount) {
    660     rc = skipqname(doh, dohlen, &index);
    661     if(rc)
    662       return rc; /* bad qname */
    663 
    664     if(dohlen < (index + 8))
    665       return DOH_DNS_OUT_OF_RANGE;
    666 
    667     index += 2 + 2 + 4; /* type, class and ttl */
    668 
    669     if(dohlen < (index + 2))
    670       return DOH_DNS_OUT_OF_RANGE;
    671 
    672     rdlength = get16bit(doh, index);
    673     index += 2;
    674     if(dohlen < (index + rdlength))
    675       return DOH_DNS_OUT_OF_RANGE;
    676     index += rdlength;
    677     nscount--;
    678   }
    679 
    680   arcount = get16bit(doh, 10);
    681   while(arcount) {
    682     rc = skipqname(doh, dohlen, &index);
    683     if(rc)
    684       return rc; /* bad qname */
    685 
    686     if(dohlen < (index + 8))
    687       return DOH_DNS_OUT_OF_RANGE;
    688 
    689     index += 2 + 2 + 4; /* type, class and ttl */
    690 
    691     if(dohlen < (index + 2))
    692       return DOH_DNS_OUT_OF_RANGE;
    693 
    694     rdlength = get16bit(doh, index);
    695     index += 2;
    696     if(dohlen < (index + rdlength))
    697       return DOH_DNS_OUT_OF_RANGE;
    698     index += rdlength;
    699     arcount--;
    700   }
    701 
    702   if(index != dohlen)
    703     return DOH_DNS_MALFORMAT; /* something is wrong */
    704 
    705   if((type != DNS_TYPE_NS) && !d->numcname && !d->numaddr)
    706     /* nothing stored! */
    707     return DOH_NO_CONTENT;
    708 
    709   return DOH_OK; /* ok */
    710 }
    711 
    712 #ifndef CURL_DISABLE_VERBOSE_STRINGS
    713 static void showdoh(struct Curl_easy *data,
    714                     struct dohentry *d)
    715 {
    716   int i;
    717   infof(data, "TTL: %u seconds\n", d->ttl);
    718   for(i = 0; i < d->numaddr; i++) {
    719     struct dohaddr *a = &d->addr[i];
    720     if(a->type == DNS_TYPE_A) {
    721       infof(data, "DOH A: %u.%u.%u.%u\n",
    722             a->ip.v4[0], a->ip.v4[1],
    723             a->ip.v4[2], a->ip.v4[3]);
    724     }
    725     else if(a->type == DNS_TYPE_AAAA) {
    726       int j;
    727       char buffer[128];
    728       char *ptr;
    729       size_t len;
    730       msnprintf(buffer, 128, "DOH AAAA: ");
    731       ptr = &buffer[10];
    732       len = 118;
    733       for(j = 0; j < 16; j += 2) {
    734         size_t l;
    735         msnprintf(ptr, len, "%s%02x%02x", j?":":"", d->addr[i].ip.v6[j],
    736                   d->addr[i].ip.v6[j + 1]);
    737         l = strlen(ptr);
    738         len -= l;
    739         ptr += l;
    740       }
    741       infof(data, "%s\n", buffer);
    742     }
    743   }
    744   for(i = 0; i < d->numcname; i++) {
    745     infof(data, "CNAME: %s\n", d->cname[i].alloc);
    746   }
    747 }
    748 #else
    749 #define showdoh(x,y)
    750 #endif
    751 
    752 /*
    753  * doh2ai()
    754  *
    755  * This function returns a pointer to the first element of a newly allocated
    756  * Curl_addrinfo struct linked list filled with the data from a set of DOH
    757  * lookups.  Curl_addrinfo is meant to work like the addrinfo struct does for
    758  * a IPv6 stack, but usable also for IPv4, all hosts and environments.
    759  *
    760  * The memory allocated by this function *MUST* be free'd later on calling
    761  * Curl_freeaddrinfo().  For each successful call to this function there
    762  * must be an associated call later to Curl_freeaddrinfo().
    763  */
    764 
    765 static Curl_addrinfo *
    766 doh2ai(const struct dohentry *de, const char *hostname, int port)
    767 {
    768   Curl_addrinfo *ai;
    769   Curl_addrinfo *prevai = NULL;
    770   Curl_addrinfo *firstai = NULL;
    771   struct sockaddr_in *addr;
    772 #ifdef ENABLE_IPV6
    773   struct sockaddr_in6 *addr6;
    774 #endif
    775   CURLcode result = CURLE_OK;
    776   int i;
    777 
    778   if(!de)
    779     /* no input == no output! */
    780     return NULL;
    781 
    782   for(i = 0; i < de->numaddr; i++) {
    783     size_t ss_size;
    784     CURL_SA_FAMILY_T addrtype;
    785     if(de->addr[i].type == DNS_TYPE_AAAA) {
    786 #ifndef ENABLE_IPV6
    787       /* we can't handle IPv6 addresses */
    788       continue;
    789 #else
    790       ss_size = sizeof(struct sockaddr_in6);
    791       addrtype = AF_INET6;
    792 #endif
    793     }
    794     else {
    795       ss_size = sizeof(struct sockaddr_in);
    796       addrtype = AF_INET;
    797     }
    798 
    799     ai = calloc(1, sizeof(Curl_addrinfo));
    800     if(!ai) {
    801       result = CURLE_OUT_OF_MEMORY;
    802       break;
    803     }
    804     ai->ai_canonname = strdup(hostname);
    805     if(!ai->ai_canonname) {
    806       result = CURLE_OUT_OF_MEMORY;
    807       free(ai);
    808       break;
    809     }
    810     ai->ai_addr = calloc(1, ss_size);
    811     if(!ai->ai_addr) {
    812       result = CURLE_OUT_OF_MEMORY;
    813       free(ai->ai_canonname);
    814       free(ai);
    815       break;
    816     }
    817 
    818     if(!firstai)
    819       /* store the pointer we want to return from this function */
    820       firstai = ai;
    821 
    822     if(prevai)
    823       /* make the previous entry point to this */
    824       prevai->ai_next = ai;
    825 
    826     ai->ai_family = addrtype;
    827 
    828     /* we return all names as STREAM, so when using this address for TFTP
    829        the type must be ignored and conn->socktype be used instead! */
    830     ai->ai_socktype = SOCK_STREAM;
    831 
    832     ai->ai_addrlen = (curl_socklen_t)ss_size;
    833 
    834     /* leave the rest of the struct filled with zero */
    835 
    836     switch(ai->ai_family) {
    837     case AF_INET:
    838       addr = (void *)ai->ai_addr; /* storage area for this info */
    839       DEBUGASSERT(sizeof(struct in_addr) == sizeof(de->addr[i].ip.v4));
    840       memcpy(&addr->sin_addr, &de->addr[i].ip.v4, sizeof(struct in_addr));
    841       addr->sin_family = (CURL_SA_FAMILY_T)addrtype;
    842       addr->sin_port = htons((unsigned short)port);
    843       break;
    844 
    845 #ifdef ENABLE_IPV6
    846     case AF_INET6:
    847       addr6 = (void *)ai->ai_addr; /* storage area for this info */
    848       DEBUGASSERT(sizeof(struct in6_addr) == sizeof(de->addr[i].ip.v6));
    849       memcpy(&addr6->sin6_addr, &de->addr[i].ip.v6, sizeof(struct in6_addr));
    850       addr6->sin6_family = (CURL_SA_FAMILY_T)addrtype;
    851       addr6->sin6_port = htons((unsigned short)port);
    852       break;
    853 #endif
    854     }
    855 
    856     prevai = ai;
    857   }
    858 
    859   if(result) {
    860     Curl_freeaddrinfo(firstai);
    861     firstai = NULL;
    862   }
    863 
    864   return firstai;
    865 }
    866 
    867 #ifndef CURL_DISABLE_VERBOSE_STRINGS
    868 static const char *type2name(DNStype dnstype)
    869 {
    870   return (dnstype == DNS_TYPE_A)?"A":"AAAA";
    871 }
    872 #endif
    873 
    874 UNITTEST void de_cleanup(struct dohentry *d)
    875 {
    876   int i = 0;
    877   for(i = 0; i < d->numcname; i++) {
    878     free(d->cname[i].alloc);
    879   }
    880 }
    881 
    882 CURLcode Curl_doh_is_resolved(struct connectdata *conn,
    883                               struct Curl_dns_entry **dnsp)
    884 {
    885   struct Curl_easy *data = conn->data;
    886   *dnsp = NULL; /* defaults to no response */
    887 
    888   if(!data->req.doh.probe[0].easy && !data->req.doh.probe[1].easy) {
    889     failf(data, "Could not DOH-resolve: %s", conn->async.hostname);
    890     return conn->bits.proxy?CURLE_COULDNT_RESOLVE_PROXY:
    891       CURLE_COULDNT_RESOLVE_HOST;
    892   }
    893   else if(!data->req.doh.pending) {
    894     DOHcode rc;
    895     DOHcode rc2;
    896     struct dohentry de;
    897     struct Curl_dns_entry *dns;
    898     struct Curl_addrinfo *ai;
    899     /* remove DOH handles from multi handle and close them */
    900     curl_multi_remove_handle(data->multi, data->req.doh.probe[0].easy);
    901     Curl_close(data->req.doh.probe[0].easy);
    902     curl_multi_remove_handle(data->multi, data->req.doh.probe[1].easy);
    903     Curl_close(data->req.doh.probe[1].easy);
    904 
    905     /* parse the responses, create the struct and return it! */
    906     init_dohentry(&de);
    907     rc = doh_decode(data->req.doh.probe[0].serverdoh.memory,
    908                     data->req.doh.probe[0].serverdoh.size,
    909                     data->req.doh.probe[0].dnstype,
    910                     &de);
    911     free(data->req.doh.probe[0].serverdoh.memory);
    912     if(rc) {
    913       infof(data, "DOH: %s type %s for %s\n", doh_strerror(rc),
    914             type2name(data->req.doh.probe[0].dnstype),
    915             data->req.doh.host);
    916     }
    917     rc2 = doh_decode(data->req.doh.probe[1].serverdoh.memory,
    918                      data->req.doh.probe[1].serverdoh.size,
    919                      data->req.doh.probe[1].dnstype,
    920                      &de);
    921     free(data->req.doh.probe[1].serverdoh.memory);
    922     if(rc2) {
    923       infof(data, "DOH: %s type %s for %s\n", doh_strerror(rc2),
    924             type2name(data->req.doh.probe[1].dnstype),
    925             data->req.doh.host);
    926     }
    927     if(!rc || !rc2) {
    928       infof(data, "DOH Host name: %s\n", data->req.doh.host);
    929       showdoh(data, &de);
    930 
    931       ai = doh2ai(&de, data->req.doh.host, data->req.doh.port);
    932       if(!ai) {
    933         de_cleanup(&de);
    934         return CURLE_OUT_OF_MEMORY;
    935       }
    936 
    937       if(data->share)
    938         Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
    939 
    940       /* we got a response, store it in the cache */
    941       dns = Curl_cache_addr(data, ai, data->req.doh.host, data->req.doh.port);
    942 
    943       if(data->share)
    944         Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
    945 
    946       de_cleanup(&de);
    947       if(!dns)
    948         /* returned failure, bail out nicely */
    949         Curl_freeaddrinfo(ai);
    950       else {
    951         conn->async.dns = dns;
    952         *dnsp = dns;
    953         return CURLE_OK;
    954       }
    955     }
    956     de_cleanup(&de);
    957 
    958     return CURLE_COULDNT_RESOLVE_HOST;
    959   }
    960 
    961   return CURLE_OK;
    962 }
    963