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