1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 2018 - 2020, 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.haxx.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 };
62 
doh_strerror(DOHcode code)63 static const char *doh_strerror(DOHcode code)
64 {
65   if((code >= DOH_OK) && (code <= DOH_DNS_BAD_ID))
66     return errors[code];
67   return "bad error code";
68 }
69 #endif
70 
71 #ifdef DEBUGBUILD
72 #define UNITTEST
73 #else
74 #define UNITTEST static
75 #endif
76 
77 /* @unittest 1655
78  */
doh_encode(const char * host,DNStype dnstype,unsigned char * dnsp,size_t len,size_t * olen)79 UNITTEST DOHcode doh_encode(const char *host,
80                             DNStype dnstype,
81                             unsigned char *dnsp, /* buffer */
82                             size_t len,  /* buffer size */
83                             size_t *olen) /* output length */
84 {
85   const size_t hostlen = strlen(host);
86   unsigned char *orig = dnsp;
87   const char *hostp = host;
88 
89   /* The expected output length is 16 bytes more than the length of
90    * the QNAME-encoding of the host name.
91    *
92    * A valid DNS name may not contain a zero-length label, except at
93    * the end.  For this reason, a name beginning with a dot, or
94    * containing a sequence of two or more consecutive dots, is invalid
95    * and cannot be encoded as a QNAME.
96    *
97    * If the host name ends with a trailing dot, the corresponding
98    * QNAME-encoding is one byte longer than the host name. If (as is
99    * also valid) the hostname is shortened by the omission of the
100    * trailing dot, then its QNAME-encoding will be two bytes longer
101    * than the host name.
102    *
103    * Each [ label, dot ] pair is encoded as [ length, label ],
104    * preserving overall length.  A final [ label ] without a dot is
105    * also encoded as [ length, label ], increasing overall length
106    * by one. The encoding is completed by appending a zero byte,
107    * representing the zero-length root label, again increasing
108    * the overall length by one.
109    */
110 
111   size_t expected_len;
112   DEBUGASSERT(hostlen);
113   expected_len = 12 + 1 + hostlen + 4;
114   if(host[hostlen-1]!='.')
115     expected_len++;
116 
117   if(expected_len > (256 + 16)) /* RFCs 1034, 1035 */
118     return DOH_DNS_NAME_TOO_LONG;
119 
120   if(len < expected_len)
121     return DOH_TOO_SMALL_BUFFER;
122 
123   *dnsp++ = 0; /* 16 bit id */
124   *dnsp++ = 0;
125   *dnsp++ = 0x01; /* |QR|   Opcode  |AA|TC|RD| Set the RD bit */
126   *dnsp++ = '\0'; /* |RA|   Z    |   RCODE   |                */
127   *dnsp++ = '\0';
128   *dnsp++ = 1;    /* QDCOUNT (number of entries in the question section) */
129   *dnsp++ = '\0';
130   *dnsp++ = '\0'; /* ANCOUNT */
131   *dnsp++ = '\0';
132   *dnsp++ = '\0'; /* NSCOUNT */
133   *dnsp++ = '\0';
134   *dnsp++ = '\0'; /* ARCOUNT */
135 
136   /* encode each label and store it in the QNAME */
137   while(*hostp) {
138     size_t labellen;
139     char *dot = strchr(hostp, '.');
140     if(dot)
141       labellen = dot - hostp;
142     else
143       labellen = strlen(hostp);
144     if((labellen > 63) || (!labellen)) {
145       /* label is too long or too short, error out */
146       *olen = 0;
147       return DOH_DNS_BAD_LABEL;
148     }
149     /* label is non-empty, process it */
150     *dnsp++ = (unsigned char)labellen;
151     memcpy(dnsp, hostp, labellen);
152     dnsp += labellen;
153     hostp += labellen;
154     /* advance past dot, but only if there is one */
155     if(dot)
156       hostp++;
157   } /* next label */
158 
159   *dnsp++ = 0; /* append zero-length label for root */
160 
161   /* There are assigned TYPE codes beyond 255: use range [1..65535]  */
162   *dnsp++ = (unsigned char)(255 & (dnstype>>8)); /* upper 8 bit TYPE */
163   *dnsp++ = (unsigned char)(255 & dnstype);      /* lower 8 bit TYPE */
164 
165   *dnsp++ = '\0'; /* upper 8 bit CLASS */
166   *dnsp++ = DNS_CLASS_IN; /* IN - "the Internet" */
167 
168   *olen = dnsp - orig;
169 
170   /* verify that our estimation of length is valid, since
171    * this has led to buffer overflows in this function */
172   DEBUGASSERT(*olen == expected_len);
173   return DOH_OK;
174 }
175 
176 static size_t
doh_write_cb(const void * contents,size_t size,size_t nmemb,void * userp)177 doh_write_cb(const void *contents, size_t size, size_t nmemb, void *userp)
178 {
179   size_t realsize = size * nmemb;
180   struct dynbuf *mem = (struct dynbuf *)userp;
181 
182   if(Curl_dyn_addn(mem, contents, realsize))
183     return 0;
184 
185   return realsize;
186 }
187 
188 /* called from multi.c when this DOH transfer is complete */
Curl_doh_done(struct Curl_easy * doh,CURLcode result)189 static int Curl_doh_done(struct Curl_easy *doh, CURLcode result)
190 {
191   struct Curl_easy *data = doh->set.dohfor;
192   /* so one of the DOH request done for the 'data' transfer is now complete! */
193   data->req.doh.pending--;
194   infof(data, "a DOH request is completed, %u to go\n", data->req.doh.pending);
195   if(result)
196     infof(data, "DOH request %s\n", curl_easy_strerror(result));
197 
198   if(!data->req.doh.pending) {
199     /* DOH completed */
200     curl_slist_free_all(data->req.doh.headers);
201     data->req.doh.headers = NULL;
202     Curl_expire(data, 0, EXPIRE_RUN_NOW);
203   }
204   return 0;
205 }
206 
207 #define ERROR_CHECK_SETOPT(x,y) \
208 do {                                      \
209   result = curl_easy_setopt(doh, x, y);   \
210   if(result)                              \
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]\n", d);
228     return CURLE_OUT_OF_MEMORY;
229   }
230 
231   p->dnstype = dnstype;
232   Curl_dyn_init(&p->serverdoh, DYN_DOH_RESPONSE);
233 
234   /* Note: this is code for sending the DoH request with GET but there's still
235      no logic that actually enables this. We should either add that ability or
236      yank out the GET code. Discuss! */
237   if(data->set.doh_get) {
238     char *b64;
239     size_t b64len;
240     result = Curl_base64url_encode(data, (char *)p->dohbuffer, p->dohlen,
241                                    &b64, &b64len);
242     if(result)
243       goto error;
244     nurl = aprintf("%s?dns=%s", url, b64);
245     free(b64);
246     if(!nurl) {
247       result = CURLE_OUT_OF_MEMORY;
248       goto error;
249     }
250     url = nurl;
251   }
252 
253   timeout_ms = Curl_timeleft(data, NULL, TRUE);
254   if(timeout_ms <= 0) {
255     result = CURLE_OPERATION_TIMEDOUT;
256     goto error;
257   }
258   /* Curl_open() is the internal version of curl_easy_init() */
259   result = Curl_open(&doh);
260   if(!result) {
261     /* pass in the struct pointer via a local variable to please coverity and
262        the gcc typecheck helpers */
263     struct dynbuf *resp = &p->serverdoh;
264     ERROR_CHECK_SETOPT(CURLOPT_URL, url);
265     ERROR_CHECK_SETOPT(CURLOPT_WRITEFUNCTION, doh_write_cb);
266     ERROR_CHECK_SETOPT(CURLOPT_WRITEDATA, resp);
267     if(!data->set.doh_get) {
268       ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDS, p->dohbuffer);
269       ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDSIZE, (long)p->dohlen);
270     }
271     ERROR_CHECK_SETOPT(CURLOPT_HTTPHEADER, headers);
272 #ifdef USE_NGHTTP2
273     ERROR_CHECK_SETOPT(CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS);
274 #endif
275 #ifndef CURLDEBUG
276     /* enforce HTTPS if not debug */
277     ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTPS);
278 #else
279     /* in debug mode, also allow http */
280     ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTP|CURLPROTO_HTTPS);
281 #endif
282     ERROR_CHECK_SETOPT(CURLOPT_TIMEOUT_MS, (long)timeout_ms);
283     if(data->set.verbose)
284       ERROR_CHECK_SETOPT(CURLOPT_VERBOSE, 1L);
285     if(data->set.no_signal)
286       ERROR_CHECK_SETOPT(CURLOPT_NOSIGNAL, 1L);
287 
288     /* Inherit *some* SSL options from the user's transfer. This is a
289        best-guess as to which options are needed for compatibility. #3661 */
290     if(data->set.ssl.falsestart)
291       ERROR_CHECK_SETOPT(CURLOPT_SSL_FALSESTART, 1L);
292     if(data->set.ssl.primary.verifyhost)
293       ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYHOST, 2L);
294 #ifndef CURL_DISABLE_PROXY
295     if(data->set.proxy_ssl.primary.verifyhost)
296       ERROR_CHECK_SETOPT(CURLOPT_PROXY_SSL_VERIFYHOST, 2L);
297     if(data->set.proxy_ssl.primary.verifypeer)
298       ERROR_CHECK_SETOPT(CURLOPT_PROXY_SSL_VERIFYPEER, 1L);
299     if(data->set.str[STRING_SSL_CAFILE_PROXY]) {
300       ERROR_CHECK_SETOPT(CURLOPT_PROXY_CAINFO,
301         data->set.str[STRING_SSL_CAFILE_PROXY]);
302     }
303     if(data->set.str[STRING_SSL_CRLFILE_PROXY]) {
304       ERROR_CHECK_SETOPT(CURLOPT_PROXY_CRLFILE,
305         data->set.str[STRING_SSL_CRLFILE_PROXY]);
306     }
307     if(data->set.proxy_ssl.no_revoke)
308       ERROR_CHECK_SETOPT(CURLOPT_PROXY_SSL_OPTIONS, CURLSSLOPT_NO_REVOKE);
309     else if(data->set.proxy_ssl.revoke_best_effort)
310       ERROR_CHECK_SETOPT(CURLOPT_PROXY_SSL_OPTIONS,
311                          CURLSSLOPT_REVOKE_BEST_EFFORT);
312     if(data->set.str[STRING_SSL_CAPATH_PROXY]) {
313       ERROR_CHECK_SETOPT(CURLOPT_PROXY_CAPATH,
314         data->set.str[STRING_SSL_CAPATH_PROXY]);
315     }
316 #endif
317     if(data->set.ssl.primary.verifypeer)
318       ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYPEER, 1L);
319     if(data->set.ssl.primary.verifystatus)
320       ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYSTATUS, 1L);
321     if(data->set.str[STRING_SSL_CAFILE_ORIG]) {
322       ERROR_CHECK_SETOPT(CURLOPT_CAINFO,
323         data->set.str[STRING_SSL_CAFILE_ORIG]);
324     }
325     if(data->set.str[STRING_SSL_CAPATH_ORIG]) {
326       ERROR_CHECK_SETOPT(CURLOPT_CAPATH,
327         data->set.str[STRING_SSL_CAPATH_ORIG]);
328     }
329     if(data->set.str[STRING_SSL_CRLFILE_ORIG]) {
330       ERROR_CHECK_SETOPT(CURLOPT_CRLFILE,
331         data->set.str[STRING_SSL_CRLFILE_ORIG]);
332     }
333     if(data->set.ssl.certinfo)
334       ERROR_CHECK_SETOPT(CURLOPT_CERTINFO, 1L);
335     if(data->set.str[STRING_SSL_RANDOM_FILE]) {
336       ERROR_CHECK_SETOPT(CURLOPT_RANDOM_FILE,
337         data->set.str[STRING_SSL_RANDOM_FILE]);
338     }
339     if(data->set.str[STRING_SSL_EGDSOCKET]) {
340       ERROR_CHECK_SETOPT(CURLOPT_EGDSOCKET,
341         data->set.str[STRING_SSL_EGDSOCKET]);
342     }
343     if(data->set.ssl.no_revoke)
344       ERROR_CHECK_SETOPT(CURLOPT_SSL_OPTIONS, CURLSSLOPT_NO_REVOKE);
345     else if(data->set.ssl.revoke_best_effort)
346       ERROR_CHECK_SETOPT(CURLOPT_SSL_OPTIONS, CURLSSLOPT_REVOKE_BEST_EFFORT);
347     if(data->set.ssl.fsslctx)
348       ERROR_CHECK_SETOPT(CURLOPT_SSL_CTX_FUNCTION, data->set.ssl.fsslctx);
349     if(data->set.ssl.fsslctxp)
350       ERROR_CHECK_SETOPT(CURLOPT_SSL_CTX_DATA, data->set.ssl.fsslctxp);
351 
352     doh->set.fmultidone = Curl_doh_done;
353     doh->set.dohfor = data; /* identify for which transfer this is done */
354     p->easy = doh;
355 
356     /* add this transfer to the multi handle */
357     if(curl_multi_add_handle(multi, doh))
358       goto error;
359   }
360   else
361     goto error;
362   free(nurl);
363   return CURLE_OK;
364 
365   error:
366   free(nurl);
367   Curl_close(&doh);
368   return result;
369 }
370 
371 /*
372  * Curl_doh() resolves a name using DOH. It resolves a name and returns a
373  * 'Curl_addrinfo *' with the address information.
374  */
375 
Curl_doh(struct connectdata * conn,const char * hostname,int port,int * waitp)376 struct Curl_addrinfo *Curl_doh(struct connectdata *conn,
377                                const char *hostname,
378                                int port,
379                                int *waitp)
380 {
381   struct Curl_easy *data = conn->data;
382   CURLcode result = CURLE_OK;
383   int slot;
384   *waitp = TRUE; /* this never returns synchronously */
385   (void)conn;
386   (void)hostname;
387   (void)port;
388 
389   /* start clean, consider allocating this struct on demand */
390   memset(&data->req.doh, 0, sizeof(struct dohdata));
391 
392   conn->bits.doh = TRUE;
393   data->req.doh.host = hostname;
394   data->req.doh.port = port;
395   data->req.doh.headers =
396     curl_slist_append(NULL,
397                       "Content-Type: application/dns-message");
398   if(!data->req.doh.headers)
399     goto error;
400 
401   if(conn->ip_version != CURL_IPRESOLVE_V6) {
402     /* create IPv4 DOH request */
403     result = dohprobe(data, &data->req.doh.probe[DOH_PROBE_SLOT_IPADDR_V4],
404                       DNS_TYPE_A, hostname, data->set.str[STRING_DOH],
405                       data->multi, data->req.doh.headers);
406     if(result)
407       goto error;
408     data->req.doh.pending++;
409   }
410 
411   if(conn->ip_version != CURL_IPRESOLVE_V4) {
412     /* create IPv6 DOH request */
413     result = dohprobe(data, &data->req.doh.probe[DOH_PROBE_SLOT_IPADDR_V6],
414                       DNS_TYPE_AAAA, hostname, data->set.str[STRING_DOH],
415                       data->multi, data->req.doh.headers);
416     if(result)
417       goto error;
418     data->req.doh.pending++;
419   }
420   return NULL;
421 
422   error:
423   curl_slist_free_all(data->req.doh.headers);
424   data->req.doh.headers = NULL;
425   for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) {
426     Curl_close(&data->req.doh.probe[slot].easy);
427   }
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 behaviour 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\n", 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\n",
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\n", buffer);
771     }
772   }
773   for(i = 0; i < d->numcname; i++) {
774     infof(data, "CNAME: %s\n", 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 = (CURL_SA_FAMILY_T)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 = (CURL_SA_FAMILY_T)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 connectdata * conn,struct Curl_dns_entry ** dnsp)902 CURLcode Curl_doh_is_resolved(struct connectdata *conn,
903                               struct Curl_dns_entry **dnsp)
904 {
905   CURLcode result;
906   struct Curl_easy *data = conn->data;
907   *dnsp = NULL; /* defaults to no response */
908 
909   if(!data->req.doh.probe[DOH_PROBE_SLOT_IPADDR_V4].easy &&
910      !data->req.doh.probe[DOH_PROBE_SLOT_IPADDR_V6].easy) {
911     failf(data, "Could not DOH-resolve: %s", conn->async.hostname);
912     return conn->bits.proxy?CURLE_COULDNT_RESOLVE_PROXY:
913       CURLE_COULDNT_RESOLVE_HOST;
914   }
915   else if(!data->req.doh.pending) {
916     DOHcode rc[DOH_PROBE_SLOTS] = {
917       DOH_OK, DOH_OK
918     };
919     struct dohentry de;
920     int slot;
921     /* remove DOH handles from multi handle and close them */
922     for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) {
923       curl_multi_remove_handle(data->multi, data->req.doh.probe[slot].easy);
924       Curl_close(&data->req.doh.probe[slot].easy);
925     }
926     /* parse the responses, create the struct and return it! */
927     de_init(&de);
928     for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) {
929       struct dnsprobe *p = &data->req.doh.probe[slot];
930       if(!p->dnstype)
931         continue;
932       rc[slot] = doh_decode(Curl_dyn_uptr(&p->serverdoh),
933                             Curl_dyn_len(&p->serverdoh),
934                             p->dnstype,
935                             &de);
936       Curl_dyn_free(&p->serverdoh);
937       if(rc[slot]) {
938         infof(data, "DOH: %s type %s for %s\n", doh_strerror(rc[slot]),
939               type2name(p->dnstype), data->req.doh.host);
940       }
941     } /* next slot */
942 
943     result = CURLE_COULDNT_RESOLVE_HOST; /* until we know better */
944     if(!rc[DOH_PROBE_SLOT_IPADDR_V4] || !rc[DOH_PROBE_SLOT_IPADDR_V6]) {
945       /* we have an address, of one kind or other */
946       struct Curl_dns_entry *dns;
947       struct Curl_addrinfo *ai;
948 
949       infof(data, "DOH Host name: %s\n", data->req.doh.host);
950       showdoh(data, &de);
951 
952       ai = doh2ai(&de, data->req.doh.host, data->req.doh.port);
953       if(!ai) {
954         de_cleanup(&de);
955         return CURLE_OUT_OF_MEMORY;
956       }
957 
958       if(data->share)
959         Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
960 
961       /* we got a response, store it in the cache */
962       dns = Curl_cache_addr(data, ai, data->req.doh.host, data->req.doh.port);
963 
964       if(data->share)
965         Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
966 
967       if(!dns) {
968         /* returned failure, bail out nicely */
969         Curl_freeaddrinfo(ai);
970       }
971       else {
972         conn->async.dns = dns;
973         *dnsp = dns;
974         result = CURLE_OK;      /* address resolution OK */
975       }
976     } /* address processing done */
977 
978     /* Now process any build-specific attributes retrieved from DNS */
979 
980     /* All done */
981     de_cleanup(&de);
982     return result;
983 
984   } /* !data->req.doh.pending */
985 
986   /* else wait for pending DOH transactions to complete */
987   return CURLE_OK;
988 }
989 
990 #endif /* CURL_DISABLE_DOH */
991