• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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   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_NGHTTP2
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 
Curl_doh(struct Curl_easy * data,const char * hostname,int port,int * waitp)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 
skipqname(const unsigned char * doh,size_t dohlen,unsigned int * indexp)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 
get16bit(const unsigned char * doh,int index)455 static unsigned short get16bit(const unsigned char *doh, int index)
456 {
457   return (unsigned short)((doh[index] << 8) | doh[index + 1]);
458 }
459 
get32bit(const unsigned char * doh,int index)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 
store_a(const unsigned char * doh,int index,struct dohentry * d)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 
store_aaaa(const unsigned char * doh,int index,struct dohentry * d)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 
store_cname(const unsigned char * doh,size_t dohlen,unsigned int index,struct dohentry * d)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_add(c, "."))
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 
rdata(const unsigned char * doh,size_t dohlen,unsigned short rdlength,unsigned short type,int index,struct dohentry * d)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 
de_init(struct dohentry * de)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 
doh_decode(const unsigned char * doh,size_t dohlen,DNStype dnstype,struct dohentry * d)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
showdoh(struct Curl_easy * data,const struct dohentry * d)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 *
doh2ai(const struct dohentry * de,const char * hostname,int port)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
type2name(DNStype dnstype)888 static const char *type2name(DNStype dnstype)
889 {
890   return (dnstype == DNS_TYPE_A)?"A":"AAAA";
891 }
892 #endif
893 
de_cleanup(struct dohentry * d)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 
Curl_doh_is_resolved(struct Curl_easy * data,struct Curl_dns_entry ** dnsp)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 data->conn->bits.proxy?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 */
994