• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 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 
doh_strerror(DOHcode code)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  */
doh_encode(const char * host,DNStype dnstype,unsigned char * dnsp,size_t len,size_t * olen)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
doh_write_cb(const void * contents,size_t size,size_t nmemb,void * userp)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 */
doh_done(struct Curl_easy * doh,CURLcode result)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 
dohprobe(struct Curl_easy * data,struct dnsprobe * p,DNStype dnstype,const char * host,const char * url,CURLM * multi,struct curl_slist * headers)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 
Curl_doh(struct Curl_easy * data,const char * hostname,int port,int * waitp)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 #ifdef ENABLE_IPV6
400   if((conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
401     /* create IPv6 DoH request */
402     result = dohprobe(data, &dohp->probe[DOH_PROBE_SLOT_IPADDR_V6],
403                       DNS_TYPE_AAAA, hostname, data->set.str[STRING_DOH],
404                       data->multi, dohp->headers);
405     if(result)
406       goto error;
407     dohp->pending++;
408   }
409 #endif
410   return NULL;
411 
412   error:
413   curl_slist_free_all(dohp->headers);
414   data->req.doh->headers = NULL;
415   for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) {
416     Curl_close(&dohp->probe[slot].easy);
417   }
418   Curl_safefree(data->req.doh);
419   return NULL;
420 }
421 
skipqname(const unsigned char * doh,size_t dohlen,unsigned int * indexp)422 static DOHcode skipqname(const unsigned char *doh, size_t dohlen,
423                          unsigned int *indexp)
424 {
425   unsigned char length;
426   do {
427     if(dohlen < (*indexp + 1))
428       return DOH_DNS_OUT_OF_RANGE;
429     length = doh[*indexp];
430     if((length & 0xc0) == 0xc0) {
431       /* name pointer, advance over it and be done */
432       if(dohlen < (*indexp + 2))
433         return DOH_DNS_OUT_OF_RANGE;
434       *indexp += 2;
435       break;
436     }
437     if(length & 0xc0)
438       return DOH_DNS_BAD_LABEL;
439     if(dohlen < (*indexp + 1 + length))
440       return DOH_DNS_OUT_OF_RANGE;
441     *indexp += 1 + length;
442   } while(length);
443   return DOH_OK;
444 }
445 
get16bit(const unsigned char * doh,int index)446 static unsigned short get16bit(const unsigned char *doh, int index)
447 {
448   return (unsigned short)((doh[index] << 8) | doh[index + 1]);
449 }
450 
get32bit(const unsigned char * doh,int index)451 static unsigned int get32bit(const unsigned char *doh, int index)
452 {
453    /* make clang and gcc optimize this to bswap by incrementing
454       the pointer first. */
455    doh += index;
456 
457    /* avoid undefined behavior by casting to unsigned before shifting
458       24 bits, possibly into the sign bit. codegen is same, but
459       ub sanitizer won't be upset */
460   return ( (unsigned)doh[0] << 24) | (doh[1] << 16) |(doh[2] << 8) | doh[3];
461 }
462 
store_a(const unsigned char * doh,int index,struct dohentry * d)463 static DOHcode store_a(const unsigned char *doh, int index, struct dohentry *d)
464 {
465   /* silently ignore addresses over the limit */
466   if(d->numaddr < DOH_MAX_ADDR) {
467     struct dohaddr *a = &d->addr[d->numaddr];
468     a->type = DNS_TYPE_A;
469     memcpy(&a->ip.v4, &doh[index], 4);
470     d->numaddr++;
471   }
472   return DOH_OK;
473 }
474 
store_aaaa(const unsigned char * doh,int index,struct dohentry * d)475 static DOHcode store_aaaa(const unsigned char *doh,
476                           int index,
477                           struct dohentry *d)
478 {
479   /* silently ignore addresses over the limit */
480   if(d->numaddr < DOH_MAX_ADDR) {
481     struct dohaddr *a = &d->addr[d->numaddr];
482     a->type = DNS_TYPE_AAAA;
483     memcpy(&a->ip.v6, &doh[index], 16);
484     d->numaddr++;
485   }
486   return DOH_OK;
487 }
488 
store_cname(const unsigned char * doh,size_t dohlen,unsigned int index,struct dohentry * d)489 static DOHcode store_cname(const unsigned char *doh,
490                            size_t dohlen,
491                            unsigned int index,
492                            struct dohentry *d)
493 {
494   struct dynbuf *c;
495   unsigned int loop = 128; /* a valid DNS name can never loop this much */
496   unsigned char length;
497 
498   if(d->numcname == DOH_MAX_CNAME)
499     return DOH_OK; /* skip! */
500 
501   c = &d->cname[d->numcname++];
502   do {
503     if(index >= dohlen)
504       return DOH_DNS_OUT_OF_RANGE;
505     length = doh[index];
506     if((length & 0xc0) == 0xc0) {
507       int newpos;
508       /* name pointer, get the new offset (14 bits) */
509       if((index + 1) >= dohlen)
510         return DOH_DNS_OUT_OF_RANGE;
511 
512       /* move to the new index */
513       newpos = (length & 0x3f) << 8 | doh[index + 1];
514       index = newpos;
515       continue;
516     }
517     else if(length & 0xc0)
518       return DOH_DNS_BAD_LABEL; /* bad input */
519     else
520       index++;
521 
522     if(length) {
523       if(Curl_dyn_len(c)) {
524         if(Curl_dyn_addn(c, STRCONST(".")))
525           return DOH_OUT_OF_MEM;
526       }
527       if((index + length) > dohlen)
528         return DOH_DNS_BAD_LABEL;
529 
530       if(Curl_dyn_addn(c, &doh[index], length))
531         return DOH_OUT_OF_MEM;
532       index += length;
533     }
534   } while(length && --loop);
535 
536   if(!loop)
537     return DOH_DNS_LABEL_LOOP;
538   return DOH_OK;
539 }
540 
rdata(const unsigned char * doh,size_t dohlen,unsigned short rdlength,unsigned short type,int index,struct dohentry * d)541 static DOHcode rdata(const unsigned char *doh,
542                      size_t dohlen,
543                      unsigned short rdlength,
544                      unsigned short type,
545                      int index,
546                      struct dohentry *d)
547 {
548   /* RDATA
549      - A (TYPE 1):  4 bytes
550      - AAAA (TYPE 28): 16 bytes
551      - NS (TYPE 2): N bytes */
552   DOHcode rc;
553 
554   switch(type) {
555   case DNS_TYPE_A:
556     if(rdlength != 4)
557       return DOH_DNS_RDATA_LEN;
558     rc = store_a(doh, index, d);
559     if(rc)
560       return rc;
561     break;
562   case DNS_TYPE_AAAA:
563     if(rdlength != 16)
564       return DOH_DNS_RDATA_LEN;
565     rc = store_aaaa(doh, index, d);
566     if(rc)
567       return rc;
568     break;
569   case DNS_TYPE_CNAME:
570     rc = store_cname(doh, dohlen, index, d);
571     if(rc)
572       return rc;
573     break;
574   case DNS_TYPE_DNAME:
575     /* explicit for clarity; just skip; rely on synthesized CNAME  */
576     break;
577   default:
578     /* unsupported type, just skip it */
579     break;
580   }
581   return DOH_OK;
582 }
583 
de_init(struct dohentry * de)584 UNITTEST void de_init(struct dohentry *de)
585 {
586   int i;
587   memset(de, 0, sizeof(*de));
588   de->ttl = INT_MAX;
589   for(i = 0; i < DOH_MAX_CNAME; i++)
590     Curl_dyn_init(&de->cname[i], DYN_DOH_CNAME);
591 }
592 
593 
doh_decode(const unsigned char * doh,size_t dohlen,DNStype dnstype,struct dohentry * d)594 UNITTEST DOHcode doh_decode(const unsigned char *doh,
595                             size_t dohlen,
596                             DNStype dnstype,
597                             struct dohentry *d)
598 {
599   unsigned char rcode;
600   unsigned short qdcount;
601   unsigned short ancount;
602   unsigned short type = 0;
603   unsigned short rdlength;
604   unsigned short nscount;
605   unsigned short arcount;
606   unsigned int index = 12;
607   DOHcode rc;
608 
609   if(dohlen < 12)
610     return DOH_TOO_SMALL_BUFFER; /* too small */
611   if(!doh || doh[0] || doh[1])
612     return DOH_DNS_BAD_ID; /* bad ID */
613   rcode = doh[3] & 0x0f;
614   if(rcode)
615     return DOH_DNS_BAD_RCODE; /* bad rcode */
616 
617   qdcount = get16bit(doh, 4);
618   while(qdcount) {
619     rc = skipqname(doh, dohlen, &index);
620     if(rc)
621       return rc; /* bad qname */
622     if(dohlen < (index + 4))
623       return DOH_DNS_OUT_OF_RANGE;
624     index += 4; /* skip question's type and class */
625     qdcount--;
626   }
627 
628   ancount = get16bit(doh, 6);
629   while(ancount) {
630     unsigned short class;
631     unsigned int ttl;
632 
633     rc = skipqname(doh, dohlen, &index);
634     if(rc)
635       return rc; /* bad qname */
636 
637     if(dohlen < (index + 2))
638       return DOH_DNS_OUT_OF_RANGE;
639 
640     type = get16bit(doh, index);
641     if((type != DNS_TYPE_CNAME)    /* may be synthesized from DNAME */
642        && (type != DNS_TYPE_DNAME) /* if present, accept and ignore */
643        && (type != dnstype))
644       /* Not the same type as was asked for nor CNAME nor DNAME */
645       return DOH_DNS_UNEXPECTED_TYPE;
646     index += 2;
647 
648     if(dohlen < (index + 2))
649       return DOH_DNS_OUT_OF_RANGE;
650     class = get16bit(doh, index);
651     if(DNS_CLASS_IN != class)
652       return DOH_DNS_UNEXPECTED_CLASS; /* unsupported */
653     index += 2;
654 
655     if(dohlen < (index + 4))
656       return DOH_DNS_OUT_OF_RANGE;
657 
658     ttl = get32bit(doh, index);
659     if(ttl < d->ttl)
660       d->ttl = ttl;
661     index += 4;
662 
663     if(dohlen < (index + 2))
664       return DOH_DNS_OUT_OF_RANGE;
665 
666     rdlength = get16bit(doh, index);
667     index += 2;
668     if(dohlen < (index + rdlength))
669       return DOH_DNS_OUT_OF_RANGE;
670 
671     rc = rdata(doh, dohlen, rdlength, type, index, d);
672     if(rc)
673       return rc; /* bad rdata */
674     index += rdlength;
675     ancount--;
676   }
677 
678   nscount = get16bit(doh, 8);
679   while(nscount) {
680     rc = skipqname(doh, dohlen, &index);
681     if(rc)
682       return rc; /* bad qname */
683 
684     if(dohlen < (index + 8))
685       return DOH_DNS_OUT_OF_RANGE;
686 
687     index += 2 + 2 + 4; /* type, class and ttl */
688 
689     if(dohlen < (index + 2))
690       return DOH_DNS_OUT_OF_RANGE;
691 
692     rdlength = get16bit(doh, index);
693     index += 2;
694     if(dohlen < (index + rdlength))
695       return DOH_DNS_OUT_OF_RANGE;
696     index += rdlength;
697     nscount--;
698   }
699 
700   arcount = get16bit(doh, 10);
701   while(arcount) {
702     rc = skipqname(doh, dohlen, &index);
703     if(rc)
704       return rc; /* bad qname */
705 
706     if(dohlen < (index + 8))
707       return DOH_DNS_OUT_OF_RANGE;
708 
709     index += 2 + 2 + 4; /* type, class and ttl */
710 
711     if(dohlen < (index + 2))
712       return DOH_DNS_OUT_OF_RANGE;
713 
714     rdlength = get16bit(doh, index);
715     index += 2;
716     if(dohlen < (index + rdlength))
717       return DOH_DNS_OUT_OF_RANGE;
718     index += rdlength;
719     arcount--;
720   }
721 
722   if(index != dohlen)
723     return DOH_DNS_MALFORMAT; /* something is wrong */
724 
725   if((type != DNS_TYPE_NS) && !d->numcname && !d->numaddr)
726     /* nothing stored! */
727     return DOH_NO_CONTENT;
728 
729   return DOH_OK; /* ok */
730 }
731 
732 #ifndef CURL_DISABLE_VERBOSE_STRINGS
showdoh(struct Curl_easy * data,const struct dohentry * d)733 static void showdoh(struct Curl_easy *data,
734                     const struct dohentry *d)
735 {
736   int i;
737   infof(data, "TTL: %u seconds", d->ttl);
738   for(i = 0; i < d->numaddr; i++) {
739     const struct dohaddr *a = &d->addr[i];
740     if(a->type == DNS_TYPE_A) {
741       infof(data, "DoH A: %u.%u.%u.%u",
742             a->ip.v4[0], a->ip.v4[1],
743             a->ip.v4[2], a->ip.v4[3]);
744     }
745     else if(a->type == DNS_TYPE_AAAA) {
746       int j;
747       char buffer[128];
748       char *ptr;
749       size_t len;
750       msnprintf(buffer, 128, "DoH AAAA: ");
751       ptr = &buffer[10];
752       len = 118;
753       for(j = 0; j < 16; j += 2) {
754         size_t l;
755         msnprintf(ptr, len, "%s%02x%02x", j?":":"", d->addr[i].ip.v6[j],
756                   d->addr[i].ip.v6[j + 1]);
757         l = strlen(ptr);
758         len -= l;
759         ptr += l;
760       }
761       infof(data, "%s", buffer);
762     }
763   }
764   for(i = 0; i < d->numcname; i++) {
765     infof(data, "CNAME: %s", Curl_dyn_ptr(&d->cname[i]));
766   }
767 }
768 #else
769 #define showdoh(x,y)
770 #endif
771 
772 /*
773  * doh2ai()
774  *
775  * This function returns a pointer to the first element of a newly allocated
776  * Curl_addrinfo struct linked list filled with the data from a set of DoH
777  * lookups.  Curl_addrinfo is meant to work like the addrinfo struct does for
778  * a IPv6 stack, but usable also for IPv4, all hosts and environments.
779  *
780  * The memory allocated by this function *MUST* be free'd later on calling
781  * Curl_freeaddrinfo().  For each successful call to this function there
782  * must be an associated call later to Curl_freeaddrinfo().
783  */
784 
785 static struct Curl_addrinfo *
doh2ai(const struct dohentry * de,const char * hostname,int port)786 doh2ai(const struct dohentry *de, const char *hostname, int port)
787 {
788   struct Curl_addrinfo *ai;
789   struct Curl_addrinfo *prevai = NULL;
790   struct Curl_addrinfo *firstai = NULL;
791   struct sockaddr_in *addr;
792 #ifdef ENABLE_IPV6
793   struct sockaddr_in6 *addr6;
794 #endif
795   CURLcode result = CURLE_OK;
796   int i;
797   size_t hostlen = strlen(hostname) + 1; /* include null-terminator */
798 
799   if(!de)
800     /* no input == no output! */
801     return NULL;
802 
803   for(i = 0; i < de->numaddr; i++) {
804     size_t ss_size;
805     CURL_SA_FAMILY_T addrtype;
806     if(de->addr[i].type == DNS_TYPE_AAAA) {
807 #ifndef ENABLE_IPV6
808       /* we can't handle IPv6 addresses */
809       continue;
810 #else
811       ss_size = sizeof(struct sockaddr_in6);
812       addrtype = AF_INET6;
813 #endif
814     }
815     else {
816       ss_size = sizeof(struct sockaddr_in);
817       addrtype = AF_INET;
818     }
819 
820     ai = calloc(1, sizeof(struct Curl_addrinfo) + ss_size + hostlen);
821     if(!ai) {
822       result = CURLE_OUT_OF_MEMORY;
823       break;
824     }
825     ai->ai_addr = (void *)((char *)ai + sizeof(struct Curl_addrinfo));
826     ai->ai_canonname = (void *)((char *)ai->ai_addr + ss_size);
827     memcpy(ai->ai_canonname, hostname, hostlen);
828 
829     if(!firstai)
830       /* store the pointer we want to return from this function */
831       firstai = ai;
832 
833     if(prevai)
834       /* make the previous entry point to this */
835       prevai->ai_next = ai;
836 
837     ai->ai_family = addrtype;
838 
839     /* we return all names as STREAM, so when using this address for TFTP
840        the type must be ignored and conn->socktype be used instead! */
841     ai->ai_socktype = SOCK_STREAM;
842 
843     ai->ai_addrlen = (curl_socklen_t)ss_size;
844 
845     /* leave the rest of the struct filled with zero */
846 
847     switch(ai->ai_family) {
848     case AF_INET:
849       addr = (void *)ai->ai_addr; /* storage area for this info */
850       DEBUGASSERT(sizeof(struct in_addr) == sizeof(de->addr[i].ip.v4));
851       memcpy(&addr->sin_addr, &de->addr[i].ip.v4, sizeof(struct in_addr));
852       addr->sin_family = addrtype;
853       addr->sin_port = htons((unsigned short)port);
854       break;
855 
856 #ifdef ENABLE_IPV6
857     case AF_INET6:
858       addr6 = (void *)ai->ai_addr; /* storage area for this info */
859       DEBUGASSERT(sizeof(struct in6_addr) == sizeof(de->addr[i].ip.v6));
860       memcpy(&addr6->sin6_addr, &de->addr[i].ip.v6, sizeof(struct in6_addr));
861       addr6->sin6_family = addrtype;
862       addr6->sin6_port = htons((unsigned short)port);
863       break;
864 #endif
865     }
866 
867     prevai = ai;
868   }
869 
870   if(result) {
871     Curl_freeaddrinfo(firstai);
872     firstai = NULL;
873   }
874 
875   return firstai;
876 }
877 
878 #ifndef CURL_DISABLE_VERBOSE_STRINGS
type2name(DNStype dnstype)879 static const char *type2name(DNStype dnstype)
880 {
881   return (dnstype == DNS_TYPE_A)?"A":"AAAA";
882 }
883 #endif
884 
de_cleanup(struct dohentry * d)885 UNITTEST void de_cleanup(struct dohentry *d)
886 {
887   int i = 0;
888   for(i = 0; i < d->numcname; i++) {
889     Curl_dyn_free(&d->cname[i]);
890   }
891 }
892 
Curl_doh_is_resolved(struct Curl_easy * data,struct Curl_dns_entry ** dnsp)893 CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
894                               struct Curl_dns_entry **dnsp)
895 {
896   CURLcode result;
897   struct dohdata *dohp = data->req.doh;
898   *dnsp = NULL; /* defaults to no response */
899   if(!dohp)
900     return CURLE_OUT_OF_MEMORY;
901 
902   if(!dohp->probe[DOH_PROBE_SLOT_IPADDR_V4].easy &&
903      !dohp->probe[DOH_PROBE_SLOT_IPADDR_V6].easy) {
904     failf(data, "Could not DoH-resolve: %s", data->state.async.hostname);
905     return CONN_IS_PROXIED(data->conn)?CURLE_COULDNT_RESOLVE_PROXY:
906       CURLE_COULDNT_RESOLVE_HOST;
907   }
908   else if(!dohp->pending) {
909     DOHcode rc[DOH_PROBE_SLOTS] = {
910       DOH_OK, DOH_OK
911     };
912     struct dohentry de;
913     int slot;
914     /* remove DoH handles from multi handle and close them */
915     for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) {
916       curl_multi_remove_handle(data->multi, dohp->probe[slot].easy);
917       Curl_close(&dohp->probe[slot].easy);
918     }
919     /* parse the responses, create the struct and return it! */
920     de_init(&de);
921     for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) {
922       struct dnsprobe *p = &dohp->probe[slot];
923       if(!p->dnstype)
924         continue;
925       rc[slot] = doh_decode(Curl_dyn_uptr(&p->serverdoh),
926                             Curl_dyn_len(&p->serverdoh),
927                             p->dnstype,
928                             &de);
929       Curl_dyn_free(&p->serverdoh);
930       if(rc[slot]) {
931         infof(data, "DoH: %s type %s for %s", doh_strerror(rc[slot]),
932               type2name(p->dnstype), dohp->host);
933       }
934     } /* next slot */
935 
936     result = CURLE_COULDNT_RESOLVE_HOST; /* until we know better */
937     if(!rc[DOH_PROBE_SLOT_IPADDR_V4] || !rc[DOH_PROBE_SLOT_IPADDR_V6]) {
938       /* we have an address, of one kind or other */
939       struct Curl_dns_entry *dns;
940       struct Curl_addrinfo *ai;
941 
942       infof(data, "DoH Host name: %s", dohp->host);
943       showdoh(data, &de);
944 
945       ai = doh2ai(&de, dohp->host, dohp->port);
946       if(!ai) {
947         de_cleanup(&de);
948         return CURLE_OUT_OF_MEMORY;
949       }
950 
951       if(data->share)
952         Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
953 
954       /* we got a response, store it in the cache */
955       dns = Curl_cache_addr(data, ai, dohp->host, 0, dohp->port);
956 
957       if(data->share)
958         Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
959 
960       if(!dns) {
961         /* returned failure, bail out nicely */
962         Curl_freeaddrinfo(ai);
963       }
964       else {
965         data->state.async.dns = dns;
966         *dnsp = dns;
967         result = CURLE_OK;      /* address resolution OK */
968       }
969     } /* address processing done */
970 
971     /* Now process any build-specific attributes retrieved from DNS */
972 
973     /* All done */
974     de_cleanup(&de);
975     Curl_safefree(data->req.doh);
976     return result;
977 
978   } /* !dohp->pending */
979 
980   /* else wait for pending DoH transactions to complete */
981   return CURLE_OK;
982 }
983 
984 #endif /* CURL_DISABLE_DOH */
985