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