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