4aef8b266a3c054d09f2a7261f5ff69e8999eb49
[platform/upstream/cmake.git] / Utilities / cmcurl / lib / doh.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 2018 - 2022, 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  ***************************************************************************/
22
23 #include "curl_setup.h"
24
25 #ifndef CURL_DISABLE_DOH
26
27 #include "urldata.h"
28 #include "curl_addrinfo.h"
29 #include "doh.h"
30
31 #include "sendf.h"
32 #include "multiif.h"
33 #include "url.h"
34 #include "share.h"
35 #include "curl_base64.h"
36 #include "connect.h"
37 #include "strdup.h"
38 #include "dynbuf.h"
39 /* The last 3 #include files should be in this order */
40 #include "curl_printf.h"
41 #include "curl_memory.h"
42 #include "memdebug.h"
43
44 #define DNS_CLASS_IN 0x01
45
46 #ifndef CURL_DISABLE_VERBOSE_STRINGS
47 static const char * const errors[]={
48   "",
49   "Bad label",
50   "Out of range",
51   "Label loop",
52   "Too small",
53   "Out of memory",
54   "RDATA length",
55   "Malformat",
56   "Bad RCODE",
57   "Unexpected TYPE",
58   "Unexpected CLASS",
59   "No content",
60   "Bad ID",
61   "Name too long"
62 };
63
64 static const char *doh_strerror(DOHcode code)
65 {
66   if((code >= DOH_OK) && (code <= DOH_DNS_NAME_TOO_LONG))
67     return errors[code];
68   return "bad error code";
69 }
70 #endif
71
72 #ifdef DEBUGBUILD
73 #define UNITTEST
74 #else
75 #define UNITTEST static
76 #endif
77
78 /* @unittest 1655
79  */
80 UNITTEST DOHcode doh_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 host name.
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 host name ends with a trailing dot, the corresponding
99    * QNAME-encoding is one byte longer than the host name. 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 host name.
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 > (256 + 16)) /* RFCs 1034, 1035 */
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_write_cb(const void *contents, size_t size, size_t nmemb, void *userp)
179 {
180   size_t realsize = size * nmemb;
181   struct dynbuf *mem = (struct dynbuf *)userp;
182
183   if(Curl_dyn_addn(mem, contents, realsize))
184     return 0;
185
186   return realsize;
187 }
188
189 /* called from multi.c when this DoH transfer is complete */
190 static int doh_done(struct Curl_easy *doh, CURLcode result)
191 {
192   struct Curl_easy *data = doh->set.dohfor;
193   struct dohdata *dohp = data->req.doh;
194   /* so one of the DoH request done for the 'data' transfer is now complete! */
195   dohp->pending--;
196   infof(data, "a DoH request is completed, %u to go", dohp->pending);
197   if(result)
198     infof(data, "DoH request %s", curl_easy_strerror(result));
199
200   if(!dohp->pending) {
201     /* DoH completed */
202     curl_slist_free_all(dohp->headers);
203     dohp->headers = NULL;
204     Curl_expire(data, 0, EXPIRE_RUN_NOW);
205   }
206   return 0;
207 }
208
209 #define ERROR_CHECK_SETOPT(x,y) \
210 do {                                          \
211   result = curl_easy_setopt(doh, x, y);       \
212   if(result &&                                \
213      result != CURLE_NOT_BUILT_IN &&          \
214      result != CURLE_UNKNOWN_OPTION)          \
215     goto error;                               \
216 } while(0)
217
218 static CURLcode dohprobe(struct Curl_easy *data,
219                          struct dnsprobe *p, DNStype dnstype,
220                          const char *host,
221                          const char *url, CURLM *multi,
222                          struct curl_slist *headers)
223 {
224   struct Curl_easy *doh = NULL;
225   char *nurl = NULL;
226   CURLcode result = CURLE_OK;
227   timediff_t timeout_ms;
228   DOHcode d = doh_encode(host, dnstype, p->dohbuffer, sizeof(p->dohbuffer),
229                          &p->dohlen);
230   if(d) {
231     failf(data, "Failed to encode DoH packet [%d]", d);
232     return CURLE_OUT_OF_MEMORY;
233   }
234
235   p->dnstype = dnstype;
236   Curl_dyn_init(&p->serverdoh, DYN_DOH_RESPONSE);
237
238   timeout_ms = Curl_timeleft(data, NULL, TRUE);
239   if(timeout_ms <= 0) {
240     result = CURLE_OPERATION_TIMEDOUT;
241     goto error;
242   }
243   /* Curl_open() is the internal version of curl_easy_init() */
244   result = Curl_open(&doh);
245   if(!result) {
246     /* pass in the struct pointer via a local variable to please coverity and
247        the gcc typecheck helpers */
248     struct dynbuf *resp = &p->serverdoh;
249     ERROR_CHECK_SETOPT(CURLOPT_URL, url);
250     ERROR_CHECK_SETOPT(CURLOPT_WRITEFUNCTION, doh_write_cb);
251     ERROR_CHECK_SETOPT(CURLOPT_WRITEDATA, resp);
252     ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDS, p->dohbuffer);
253     ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDSIZE, (long)p->dohlen);
254     ERROR_CHECK_SETOPT(CURLOPT_HTTPHEADER, headers);
255 #ifdef USE_HTTP2
256     ERROR_CHECK_SETOPT(CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS);
257 #endif
258 #ifndef CURLDEBUG
259     /* enforce HTTPS if not debug */
260     ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTPS);
261 #else
262     /* in debug mode, also allow http */
263     ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTP|CURLPROTO_HTTPS);
264 #endif
265     ERROR_CHECK_SETOPT(CURLOPT_TIMEOUT_MS, (long)timeout_ms);
266     ERROR_CHECK_SETOPT(CURLOPT_SHARE, data->share);
267     if(data->set.err && data->set.err != stderr)
268       ERROR_CHECK_SETOPT(CURLOPT_STDERR, data->set.err);
269     if(data->set.verbose)
270       ERROR_CHECK_SETOPT(CURLOPT_VERBOSE, 1L);
271     if(data->set.no_signal)
272       ERROR_CHECK_SETOPT(CURLOPT_NOSIGNAL, 1L);
273
274     ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYHOST,
275       data->set.doh_verifyhost ? 2L : 0L);
276     ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYPEER,
277       data->set.doh_verifypeer ? 1L : 0L);
278     ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYSTATUS,
279       data->set.doh_verifystatus ? 1L : 0L);
280
281     /* Inherit *some* SSL options from the user's transfer. This is a
282        best-guess as to which options are needed for compatibility. #3661
283
284        Note DoH does not inherit the user's proxy server so proxy SSL settings
285        have no effect and are not inherited. If that changes then two new
286        options should be added to check doh proxy insecure separately,
287        CURLOPT_DOH_PROXY_SSL_VERIFYHOST and CURLOPT_DOH_PROXY_SSL_VERIFYPEER.
288        */
289     if(data->set.ssl.falsestart)
290       ERROR_CHECK_SETOPT(CURLOPT_SSL_FALSESTART, 1L);
291     if(data->set.str[STRING_SSL_CAFILE]) {
292       ERROR_CHECK_SETOPT(CURLOPT_CAINFO,
293                          data->set.str[STRING_SSL_CAFILE]);
294     }
295     if(data->set.blobs[BLOB_CAINFO]) {
296       ERROR_CHECK_SETOPT(CURLOPT_CAINFO_BLOB,
297                          data->set.blobs[BLOB_CAINFO]);
298     }
299     if(data->set.str[STRING_SSL_CAPATH]) {
300       ERROR_CHECK_SETOPT(CURLOPT_CAPATH,
301                          data->set.str[STRING_SSL_CAPATH]);
302     }
303     if(data->set.str[STRING_SSL_CRLFILE]) {
304       ERROR_CHECK_SETOPT(CURLOPT_CRLFILE,
305                          data->set.str[STRING_SSL_CRLFILE]);
306     }
307     if(data->set.ssl.certinfo)
308       ERROR_CHECK_SETOPT(CURLOPT_CERTINFO, 1L);
309     if(data->set.str[STRING_SSL_RANDOM_FILE]) {
310       ERROR_CHECK_SETOPT(CURLOPT_RANDOM_FILE,
311                          data->set.str[STRING_SSL_RANDOM_FILE]);
312     }
313     if(data->set.str[STRING_SSL_EGDSOCKET]) {
314       ERROR_CHECK_SETOPT(CURLOPT_EGDSOCKET,
315                          data->set.str[STRING_SSL_EGDSOCKET]);
316     }
317     if(data->set.ssl.fsslctx)
318       ERROR_CHECK_SETOPT(CURLOPT_SSL_CTX_FUNCTION, data->set.ssl.fsslctx);
319     if(data->set.ssl.fsslctxp)
320       ERROR_CHECK_SETOPT(CURLOPT_SSL_CTX_DATA, data->set.ssl.fsslctxp);
321     if(data->set.str[STRING_SSL_EC_CURVES]) {
322       ERROR_CHECK_SETOPT(CURLOPT_SSL_EC_CURVES,
323                          data->set.str[STRING_SSL_EC_CURVES]);
324     }
325
326     {
327       long mask =
328         (data->set.ssl.enable_beast ?
329          CURLSSLOPT_ALLOW_BEAST : 0) |
330         (data->set.ssl.no_revoke ?
331          CURLSSLOPT_NO_REVOKE : 0) |
332         (data->set.ssl.no_partialchain ?
333          CURLSSLOPT_NO_PARTIALCHAIN : 0) |
334         (data->set.ssl.revoke_best_effort ?
335          CURLSSLOPT_REVOKE_BEST_EFFORT : 0) |
336         (data->set.ssl.native_ca_store ?
337          CURLSSLOPT_NATIVE_CA : 0) |
338         (data->set.ssl.auto_client_cert ?
339          CURLSSLOPT_AUTO_CLIENT_CERT : 0);
340
341       (void)curl_easy_setopt(doh, CURLOPT_SSL_OPTIONS, mask);
342     }
343
344     doh->set.fmultidone = doh_done;
345     doh->set.dohfor = data; /* identify for which transfer this is done */
346     p->easy = doh;
347
348     /* DoH private_data must be null because the user must have a way to
349        distinguish their transfer's handle from DoH handles in user
350        callbacks (ie SSL CTX callback). */
351     DEBUGASSERT(!doh->set.private_data);
352
353     if(curl_multi_add_handle(multi, doh))
354       goto error;
355   }
356   else
357     goto error;
358   free(nurl);
359   return CURLE_OK;
360
361   error:
362   free(nurl);
363   Curl_close(&doh);
364   return result;
365 }
366
367 /*
368  * Curl_doh() resolves a name using DoH. It resolves a name and returns a
369  * 'Curl_addrinfo *' with the address information.
370  */
371
372 struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
373                                const char *hostname,
374                                int port,
375                                int *waitp)
376 {
377   CURLcode result = CURLE_OK;
378   int slot;
379   struct dohdata *dohp;
380   struct connectdata *conn = data->conn;
381   *waitp = TRUE; /* this never returns synchronously */
382   (void)hostname;
383   (void)port;
384
385   DEBUGASSERT(!data->req.doh);
386   DEBUGASSERT(conn);
387
388   /* start clean, consider allocating this struct on demand */
389   dohp = data->req.doh = calloc(sizeof(struct dohdata), 1);
390   if(!dohp)
391     return NULL;
392
393   conn->bits.doh = TRUE;
394   dohp->host = hostname;
395   dohp->port = port;
396   dohp->headers =
397     curl_slist_append(NULL,
398                       "Content-Type: application/dns-message");
399   if(!dohp->headers)
400     goto error;
401
402   /* create IPv4 DoH request */
403   result = dohprobe(data, &dohp->probe[DOH_PROBE_SLOT_IPADDR_V4],
404                     DNS_TYPE_A, hostname, data->set.str[STRING_DOH],
405                     data->multi, dohp->headers);
406   if(result)
407     goto error;
408   dohp->pending++;
409
410   if(Curl_ipv6works(data)) {
411     /* create IPv6 DoH request */
412     result = dohprobe(data, &dohp->probe[DOH_PROBE_SLOT_IPADDR_V6],
413                       DNS_TYPE_AAAA, hostname, data->set.str[STRING_DOH],
414                       data->multi, dohp->headers);
415     if(result)
416       goto error;
417     dohp->pending++;
418   }
419   return NULL;
420
421   error:
422   curl_slist_free_all(dohp->headers);
423   data->req.doh->headers = NULL;
424   for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) {
425     Curl_close(&dohp->probe[slot].easy);
426   }
427   Curl_safefree(data->req.doh);
428   return NULL;
429 }
430
431 static DOHcode skipqname(const unsigned char *doh, size_t dohlen,
432                          unsigned int *indexp)
433 {
434   unsigned char length;
435   do {
436     if(dohlen < (*indexp + 1))
437       return DOH_DNS_OUT_OF_RANGE;
438     length = doh[*indexp];
439     if((length & 0xc0) == 0xc0) {
440       /* name pointer, advance over it and be done */
441       if(dohlen < (*indexp + 2))
442         return DOH_DNS_OUT_OF_RANGE;
443       *indexp += 2;
444       break;
445     }
446     if(length & 0xc0)
447       return DOH_DNS_BAD_LABEL;
448     if(dohlen < (*indexp + 1 + length))
449       return DOH_DNS_OUT_OF_RANGE;
450     *indexp += 1 + length;
451   } while(length);
452   return DOH_OK;
453 }
454
455 static unsigned short get16bit(const unsigned char *doh, int index)
456 {
457   return (unsigned short)((doh[index] << 8) | doh[index + 1]);
458 }
459
460 static unsigned int get32bit(const unsigned char *doh, int index)
461 {
462    /* make clang and gcc optimize this to bswap by incrementing
463       the pointer first. */
464    doh += index;
465
466    /* avoid undefined behavior by casting to unsigned before shifting
467       24 bits, possibly into the sign bit. codegen is same, but
468       ub sanitizer won't be upset */
469   return ( (unsigned)doh[0] << 24) | (doh[1] << 16) |(doh[2] << 8) | doh[3];
470 }
471
472 static DOHcode store_a(const unsigned char *doh, int index, struct dohentry *d)
473 {
474   /* silently ignore addresses over the limit */
475   if(d->numaddr < DOH_MAX_ADDR) {
476     struct dohaddr *a = &d->addr[d->numaddr];
477     a->type = DNS_TYPE_A;
478     memcpy(&a->ip.v4, &doh[index], 4);
479     d->numaddr++;
480   }
481   return DOH_OK;
482 }
483
484 static DOHcode store_aaaa(const unsigned char *doh,
485                           int index,
486                           struct dohentry *d)
487 {
488   /* silently ignore addresses over the limit */
489   if(d->numaddr < DOH_MAX_ADDR) {
490     struct dohaddr *a = &d->addr[d->numaddr];
491     a->type = DNS_TYPE_AAAA;
492     memcpy(&a->ip.v6, &doh[index], 16);
493     d->numaddr++;
494   }
495   return DOH_OK;
496 }
497
498 static DOHcode store_cname(const unsigned char *doh,
499                            size_t dohlen,
500                            unsigned int index,
501                            struct dohentry *d)
502 {
503   struct dynbuf *c;
504   unsigned int loop = 128; /* a valid DNS name can never loop this much */
505   unsigned char length;
506
507   if(d->numcname == DOH_MAX_CNAME)
508     return DOH_OK; /* skip! */
509
510   c = &d->cname[d->numcname++];
511   do {
512     if(index >= dohlen)
513       return DOH_DNS_OUT_OF_RANGE;
514     length = doh[index];
515     if((length & 0xc0) == 0xc0) {
516       int newpos;
517       /* name pointer, get the new offset (14 bits) */
518       if((index + 1) >= dohlen)
519         return DOH_DNS_OUT_OF_RANGE;
520
521       /* move to the new index */
522       newpos = (length & 0x3f) << 8 | doh[index + 1];
523       index = newpos;
524       continue;
525     }
526     else if(length & 0xc0)
527       return DOH_DNS_BAD_LABEL; /* bad input */
528     else
529       index++;
530
531     if(length) {
532       if(Curl_dyn_len(c)) {
533         if(Curl_dyn_addn(c, STRCONST(".")))
534           return DOH_OUT_OF_MEM;
535       }
536       if((index + length) > dohlen)
537         return DOH_DNS_BAD_LABEL;
538
539       if(Curl_dyn_addn(c, &doh[index], length))
540         return DOH_OUT_OF_MEM;
541       index += length;
542     }
543   } while(length && --loop);
544
545   if(!loop)
546     return DOH_DNS_LABEL_LOOP;
547   return DOH_OK;
548 }
549
550 static DOHcode rdata(const unsigned char *doh,
551                      size_t dohlen,
552                      unsigned short rdlength,
553                      unsigned short type,
554                      int index,
555                      struct dohentry *d)
556 {
557   /* RDATA
558      - A (TYPE 1):  4 bytes
559      - AAAA (TYPE 28): 16 bytes
560      - NS (TYPE 2): N bytes */
561   DOHcode rc;
562
563   switch(type) {
564   case DNS_TYPE_A:
565     if(rdlength != 4)
566       return DOH_DNS_RDATA_LEN;
567     rc = store_a(doh, index, d);
568     if(rc)
569       return rc;
570     break;
571   case DNS_TYPE_AAAA:
572     if(rdlength != 16)
573       return DOH_DNS_RDATA_LEN;
574     rc = store_aaaa(doh, index, d);
575     if(rc)
576       return rc;
577     break;
578   case DNS_TYPE_CNAME:
579     rc = store_cname(doh, dohlen, index, d);
580     if(rc)
581       return rc;
582     break;
583   case DNS_TYPE_DNAME:
584     /* explicit for clarity; just skip; rely on synthesized CNAME  */
585     break;
586   default:
587     /* unsupported type, just skip it */
588     break;
589   }
590   return DOH_OK;
591 }
592
593 UNITTEST void de_init(struct dohentry *de)
594 {
595   int i;
596   memset(de, 0, sizeof(*de));
597   de->ttl = INT_MAX;
598   for(i = 0; i < DOH_MAX_CNAME; i++)
599     Curl_dyn_init(&de->cname[i], DYN_DOH_CNAME);
600 }
601
602
603 UNITTEST DOHcode doh_decode(const unsigned char *doh,
604                             size_t dohlen,
605                             DNStype dnstype,
606                             struct dohentry *d)
607 {
608   unsigned char rcode;
609   unsigned short qdcount;
610   unsigned short ancount;
611   unsigned short type = 0;
612   unsigned short rdlength;
613   unsigned short nscount;
614   unsigned short arcount;
615   unsigned int index = 12;
616   DOHcode rc;
617
618   if(dohlen < 12)
619     return DOH_TOO_SMALL_BUFFER; /* too small */
620   if(!doh || doh[0] || doh[1])
621     return DOH_DNS_BAD_ID; /* bad ID */
622   rcode = doh[3] & 0x0f;
623   if(rcode)
624     return DOH_DNS_BAD_RCODE; /* bad rcode */
625
626   qdcount = get16bit(doh, 4);
627   while(qdcount) {
628     rc = skipqname(doh, dohlen, &index);
629     if(rc)
630       return rc; /* bad qname */
631     if(dohlen < (index + 4))
632       return DOH_DNS_OUT_OF_RANGE;
633     index += 4; /* skip question's type and class */
634     qdcount--;
635   }
636
637   ancount = get16bit(doh, 6);
638   while(ancount) {
639     unsigned short class;
640     unsigned int ttl;
641
642     rc = skipqname(doh, dohlen, &index);
643     if(rc)
644       return rc; /* bad qname */
645
646     if(dohlen < (index + 2))
647       return DOH_DNS_OUT_OF_RANGE;
648
649     type = get16bit(doh, index);
650     if((type != DNS_TYPE_CNAME)    /* may be synthesized from DNAME */
651        && (type != DNS_TYPE_DNAME) /* if present, accept and ignore */
652        && (type != dnstype))
653       /* Not the same type as was asked for nor CNAME nor DNAME */
654       return DOH_DNS_UNEXPECTED_TYPE;
655     index += 2;
656
657     if(dohlen < (index + 2))
658       return DOH_DNS_OUT_OF_RANGE;
659     class = get16bit(doh, index);
660     if(DNS_CLASS_IN != class)
661       return DOH_DNS_UNEXPECTED_CLASS; /* unsupported */
662     index += 2;
663
664     if(dohlen < (index + 4))
665       return DOH_DNS_OUT_OF_RANGE;
666
667     ttl = get32bit(doh, index);
668     if(ttl < d->ttl)
669       d->ttl = ttl;
670     index += 4;
671
672     if(dohlen < (index + 2))
673       return DOH_DNS_OUT_OF_RANGE;
674
675     rdlength = get16bit(doh, index);
676     index += 2;
677     if(dohlen < (index + rdlength))
678       return DOH_DNS_OUT_OF_RANGE;
679
680     rc = rdata(doh, dohlen, rdlength, type, index, d);
681     if(rc)
682       return rc; /* bad rdata */
683     index += rdlength;
684     ancount--;
685   }
686
687   nscount = get16bit(doh, 8);
688   while(nscount) {
689     rc = skipqname(doh, dohlen, &index);
690     if(rc)
691       return rc; /* bad qname */
692
693     if(dohlen < (index + 8))
694       return DOH_DNS_OUT_OF_RANGE;
695
696     index += 2 + 2 + 4; /* type, class and ttl */
697
698     if(dohlen < (index + 2))
699       return DOH_DNS_OUT_OF_RANGE;
700
701     rdlength = get16bit(doh, index);
702     index += 2;
703     if(dohlen < (index + rdlength))
704       return DOH_DNS_OUT_OF_RANGE;
705     index += rdlength;
706     nscount--;
707   }
708
709   arcount = get16bit(doh, 10);
710   while(arcount) {
711     rc = skipqname(doh, dohlen, &index);
712     if(rc)
713       return rc; /* bad qname */
714
715     if(dohlen < (index + 8))
716       return DOH_DNS_OUT_OF_RANGE;
717
718     index += 2 + 2 + 4; /* type, class and ttl */
719
720     if(dohlen < (index + 2))
721       return DOH_DNS_OUT_OF_RANGE;
722
723     rdlength = get16bit(doh, index);
724     index += 2;
725     if(dohlen < (index + rdlength))
726       return DOH_DNS_OUT_OF_RANGE;
727     index += rdlength;
728     arcount--;
729   }
730
731   if(index != dohlen)
732     return DOH_DNS_MALFORMAT; /* something is wrong */
733
734   if((type != DNS_TYPE_NS) && !d->numcname && !d->numaddr)
735     /* nothing stored! */
736     return DOH_NO_CONTENT;
737
738   return DOH_OK; /* ok */
739 }
740
741 #ifndef CURL_DISABLE_VERBOSE_STRINGS
742 static void showdoh(struct Curl_easy *data,
743                     const struct dohentry *d)
744 {
745   int i;
746   infof(data, "TTL: %u seconds", d->ttl);
747   for(i = 0; i < d->numaddr; i++) {
748     const struct dohaddr *a = &d->addr[i];
749     if(a->type == DNS_TYPE_A) {
750       infof(data, "DoH A: %u.%u.%u.%u",
751             a->ip.v4[0], a->ip.v4[1],
752             a->ip.v4[2], a->ip.v4[3]);
753     }
754     else if(a->type == DNS_TYPE_AAAA) {
755       int j;
756       char buffer[128];
757       char *ptr;
758       size_t len;
759       msnprintf(buffer, 128, "DoH AAAA: ");
760       ptr = &buffer[10];
761       len = 118;
762       for(j = 0; j < 16; j += 2) {
763         size_t l;
764         msnprintf(ptr, len, "%s%02x%02x", j?":":"", d->addr[i].ip.v6[j],
765                   d->addr[i].ip.v6[j + 1]);
766         l = strlen(ptr);
767         len -= l;
768         ptr += l;
769       }
770       infof(data, "%s", buffer);
771     }
772   }
773   for(i = 0; i < d->numcname; i++) {
774     infof(data, "CNAME: %s", Curl_dyn_ptr(&d->cname[i]));
775   }
776 }
777 #else
778 #define showdoh(x,y)
779 #endif
780
781 /*
782  * doh2ai()
783  *
784  * This function returns a pointer to the first element of a newly allocated
785  * Curl_addrinfo struct linked list filled with the data from a set of DoH
786  * lookups.  Curl_addrinfo is meant to work like the addrinfo struct does for
787  * a IPv6 stack, but usable also for IPv4, all hosts and environments.
788  *
789  * The memory allocated by this function *MUST* be free'd later on calling
790  * Curl_freeaddrinfo().  For each successful call to this function there
791  * must be an associated call later to Curl_freeaddrinfo().
792  */
793
794 static struct Curl_addrinfo *
795 doh2ai(const struct dohentry *de, const char *hostname, int port)
796 {
797   struct Curl_addrinfo *ai;
798   struct Curl_addrinfo *prevai = NULL;
799   struct Curl_addrinfo *firstai = NULL;
800   struct sockaddr_in *addr;
801 #ifdef ENABLE_IPV6
802   struct sockaddr_in6 *addr6;
803 #endif
804   CURLcode result = CURLE_OK;
805   int i;
806   size_t hostlen = strlen(hostname) + 1; /* include zero terminator */
807
808   if(!de)
809     /* no input == no output! */
810     return NULL;
811
812   for(i = 0; i < de->numaddr; i++) {
813     size_t ss_size;
814     CURL_SA_FAMILY_T addrtype;
815     if(de->addr[i].type == DNS_TYPE_AAAA) {
816 #ifndef ENABLE_IPV6
817       /* we can't handle IPv6 addresses */
818       continue;
819 #else
820       ss_size = sizeof(struct sockaddr_in6);
821       addrtype = AF_INET6;
822 #endif
823     }
824     else {
825       ss_size = sizeof(struct sockaddr_in);
826       addrtype = AF_INET;
827     }
828
829     ai = calloc(1, sizeof(struct Curl_addrinfo) + ss_size + hostlen);
830     if(!ai) {
831       result = CURLE_OUT_OF_MEMORY;
832       break;
833     }
834     ai->ai_addr = (void *)((char *)ai + sizeof(struct Curl_addrinfo));
835     ai->ai_canonname = (void *)((char *)ai->ai_addr + ss_size);
836     memcpy(ai->ai_canonname, hostname, hostlen);
837
838     if(!firstai)
839       /* store the pointer we want to return from this function */
840       firstai = ai;
841
842     if(prevai)
843       /* make the previous entry point to this */
844       prevai->ai_next = ai;
845
846     ai->ai_family = addrtype;
847
848     /* we return all names as STREAM, so when using this address for TFTP
849        the type must be ignored and conn->socktype be used instead! */
850     ai->ai_socktype = SOCK_STREAM;
851
852     ai->ai_addrlen = (curl_socklen_t)ss_size;
853
854     /* leave the rest of the struct filled with zero */
855
856     switch(ai->ai_family) {
857     case AF_INET:
858       addr = (void *)ai->ai_addr; /* storage area for this info */
859       DEBUGASSERT(sizeof(struct in_addr) == sizeof(de->addr[i].ip.v4));
860       memcpy(&addr->sin_addr, &de->addr[i].ip.v4, sizeof(struct in_addr));
861       addr->sin_family = addrtype;
862       addr->sin_port = htons((unsigned short)port);
863       break;
864
865 #ifdef ENABLE_IPV6
866     case AF_INET6:
867       addr6 = (void *)ai->ai_addr; /* storage area for this info */
868       DEBUGASSERT(sizeof(struct in6_addr) == sizeof(de->addr[i].ip.v6));
869       memcpy(&addr6->sin6_addr, &de->addr[i].ip.v6, sizeof(struct in6_addr));
870       addr6->sin6_family = addrtype;
871       addr6->sin6_port = htons((unsigned short)port);
872       break;
873 #endif
874     }
875
876     prevai = ai;
877   }
878
879   if(result) {
880     Curl_freeaddrinfo(firstai);
881     firstai = NULL;
882   }
883
884   return firstai;
885 }
886
887 #ifndef CURL_DISABLE_VERBOSE_STRINGS
888 static const char *type2name(DNStype dnstype)
889 {
890   return (dnstype == DNS_TYPE_A)?"A":"AAAA";
891 }
892 #endif
893
894 UNITTEST void de_cleanup(struct dohentry *d)
895 {
896   int i = 0;
897   for(i = 0; i < d->numcname; i++) {
898     Curl_dyn_free(&d->cname[i]);
899   }
900 }
901
902 CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
903                               struct Curl_dns_entry **dnsp)
904 {
905   CURLcode result;
906   struct dohdata *dohp = data->req.doh;
907   *dnsp = NULL; /* defaults to no response */
908   if(!dohp)
909     return CURLE_OUT_OF_MEMORY;
910
911   if(!dohp->probe[DOH_PROBE_SLOT_IPADDR_V4].easy &&
912      !dohp->probe[DOH_PROBE_SLOT_IPADDR_V6].easy) {
913     failf(data, "Could not DoH-resolve: %s", data->state.async.hostname);
914     return CONN_IS_PROXIED(data->conn)?CURLE_COULDNT_RESOLVE_PROXY:
915       CURLE_COULDNT_RESOLVE_HOST;
916   }
917   else if(!dohp->pending) {
918     DOHcode rc[DOH_PROBE_SLOTS] = {
919       DOH_OK, DOH_OK
920     };
921     struct dohentry de;
922     int slot;
923     /* remove DoH handles from multi handle and close them */
924     for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) {
925       curl_multi_remove_handle(data->multi, dohp->probe[slot].easy);
926       Curl_close(&dohp->probe[slot].easy);
927     }
928     /* parse the responses, create the struct and return it! */
929     de_init(&de);
930     for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) {
931       struct dnsprobe *p = &dohp->probe[slot];
932       if(!p->dnstype)
933         continue;
934       rc[slot] = doh_decode(Curl_dyn_uptr(&p->serverdoh),
935                             Curl_dyn_len(&p->serverdoh),
936                             p->dnstype,
937                             &de);
938       Curl_dyn_free(&p->serverdoh);
939       if(rc[slot]) {
940         infof(data, "DoH: %s type %s for %s", doh_strerror(rc[slot]),
941               type2name(p->dnstype), dohp->host);
942       }
943     } /* next slot */
944
945     result = CURLE_COULDNT_RESOLVE_HOST; /* until we know better */
946     if(!rc[DOH_PROBE_SLOT_IPADDR_V4] || !rc[DOH_PROBE_SLOT_IPADDR_V6]) {
947       /* we have an address, of one kind or other */
948       struct Curl_dns_entry *dns;
949       struct Curl_addrinfo *ai;
950
951       infof(data, "DoH Host name: %s", dohp->host);
952       showdoh(data, &de);
953
954       ai = doh2ai(&de, dohp->host, dohp->port);
955       if(!ai) {
956         de_cleanup(&de);
957         return CURLE_OUT_OF_MEMORY;
958       }
959
960       if(data->share)
961         Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
962
963       /* we got a response, store it in the cache */
964       dns = Curl_cache_addr(data, ai, dohp->host, dohp->port);
965
966       if(data->share)
967         Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
968
969       if(!dns) {
970         /* returned failure, bail out nicely */
971         Curl_freeaddrinfo(ai);
972       }
973       else {
974         data->state.async.dns = dns;
975         *dnsp = dns;
976         result = CURLE_OK;      /* address resolution OK */
977       }
978     } /* address processing done */
979
980     /* Now process any build-specific attributes retrieved from DNS */
981
982     /* All done */
983     de_cleanup(&de);
984     Curl_safefree(data->req.doh);
985     return result;
986
987   } /* !dohp->pending */
988
989   /* else wait for pending DoH transactions to complete */
990   return CURLE_OK;
991 }
992
993 #endif /* CURL_DISABLE_DOH */