• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 2018 - 2019, 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 #include "urldata.h"
26 #include "curl_addrinfo.h"
27 #include "doh.h"
28 
29 #include "sendf.h"
30 #include "multiif.h"
31 #include "url.h"
32 #include "share.h"
33 #include "curl_base64.h"
34 #include "connect.h"
35 #include "strdup.h"
36 /* The last 3 #include files should be in this order */
37 #include "curl_printf.h"
38 #include "curl_memory.h"
39 #include "memdebug.h"
40 
41 #define DNS_CLASS_IN 0x01
42 #define DOH_MAX_RESPONSE_SIZE 3000 /* bytes */
43 
44 #ifndef CURL_DISABLE_VERBOSE_STRINGS
45 static const char * const errors[]={
46   "",
47   "Bad label",
48   "Out of range",
49   "Label loop",
50   "Too small",
51   "Out of memory",
52   "RDATA length",
53   "Malformat",
54   "Bad RCODE",
55   "Unexpected TYPE",
56   "Unexpected CLASS",
57   "No content",
58   "Bad ID"
59 };
60 
doh_strerror(DOHcode code)61 static const char *doh_strerror(DOHcode code)
62 {
63   if((code >= DOH_OK) && (code <= DOH_DNS_BAD_ID))
64     return errors[code];
65   return "bad error code";
66 }
67 #endif
68 
69 #ifdef DEBUGBUILD
70 #define UNITTEST
71 #else
72 #define UNITTEST static
73 #endif
74 
doh_encode(const char * host,DNStype dnstype,unsigned char * dnsp,size_t len,size_t * olen)75 UNITTEST DOHcode doh_encode(const char *host,
76                             DNStype dnstype,
77                             unsigned char *dnsp, /* buffer */
78                             size_t len,  /* buffer size */
79                             size_t *olen) /* output length */
80 {
81   size_t hostlen = strlen(host);
82   unsigned char *orig = dnsp;
83   const char *hostp = host;
84 
85   if(len < (12 + hostlen + 4))
86     return DOH_TOO_SMALL_BUFFER;
87 
88   *dnsp++ = 0; /* 16 bit id */
89   *dnsp++ = 0;
90   *dnsp++ = 0x01; /* |QR|   Opcode  |AA|TC|RD| Set the RD bit */
91   *dnsp++ = '\0'; /* |RA|   Z    |   RCODE   |                */
92   *dnsp++ = '\0';
93   *dnsp++ = 1;    /* QDCOUNT (number of entries in the question section) */
94   *dnsp++ = '\0';
95   *dnsp++ = '\0'; /* ANCOUNT */
96   *dnsp++ = '\0';
97   *dnsp++ = '\0'; /* NSCOUNT */
98   *dnsp++ = '\0';
99   *dnsp++ = '\0'; /* ARCOUNT */
100 
101   /* store a QNAME */
102   do {
103     char *dot = strchr(hostp, '.');
104     size_t labellen;
105     bool found = false;
106     if(dot) {
107       found = true;
108       labellen = dot - hostp;
109     }
110     else
111       labellen = strlen(hostp);
112     if(labellen > 63) {
113       /* too long label, error out */
114       *olen = 0;
115       return DOH_DNS_BAD_LABEL;
116     }
117     *dnsp++ = (unsigned char)labellen;
118     memcpy(dnsp, hostp, labellen);
119     dnsp += labellen;
120     hostp += labellen + 1;
121     if(!found) {
122       *dnsp++ = 0; /* terminating zero */
123       break;
124     }
125   } while(1);
126 
127   *dnsp++ = '\0'; /* upper 8 bit TYPE */
128   *dnsp++ = (unsigned char)dnstype;
129   *dnsp++ = '\0'; /* upper 8 bit CLASS */
130   *dnsp++ = DNS_CLASS_IN; /* IN - "the Internet" */
131 
132   *olen = dnsp - orig;
133   return DOH_OK;
134 }
135 
136 static size_t
doh_write_cb(void * contents,size_t size,size_t nmemb,void * userp)137 doh_write_cb(void *contents, size_t size, size_t nmemb, void *userp)
138 {
139   size_t realsize = size * nmemb;
140   struct dohresponse *mem = (struct dohresponse *)userp;
141 
142   if((mem->size + realsize) > DOH_MAX_RESPONSE_SIZE)
143     /* suspiciously much for us */
144     return 0;
145 
146   mem->memory = Curl_saferealloc(mem->memory, mem->size + realsize);
147   if(!mem->memory)
148     /* out of memory! */
149     return 0;
150 
151   memcpy(&(mem->memory[mem->size]), contents, realsize);
152   mem->size += realsize;
153 
154   return realsize;
155 }
156 
157 /* called from multi.c when this DOH transfer is complete */
Curl_doh_done(struct Curl_easy * doh,CURLcode result)158 static int Curl_doh_done(struct Curl_easy *doh, CURLcode result)
159 {
160   struct Curl_easy *data = doh->set.dohfor;
161   /* so one of the DOH request done for the 'data' transfer is now complete! */
162   data->req.doh.pending--;
163   infof(data, "a DOH request is completed, %u to go\n", data->req.doh.pending);
164   if(result)
165     infof(data, "DOH request %s\n", curl_easy_strerror(result));
166 
167   if(!data->req.doh.pending) {
168     /* DOH completed */
169     curl_slist_free_all(data->req.doh.headers);
170     data->req.doh.headers = NULL;
171     Curl_expire(data, 0, EXPIRE_RUN_NOW);
172   }
173   return 0;
174 }
175 
176 #define ERROR_CHECK_SETOPT(x,y) \
177 do {                                      \
178   result = curl_easy_setopt(doh, x, y);   \
179   if(result)                              \
180     goto error;                           \
181 } WHILE_FALSE
182 
dohprobe(struct Curl_easy * data,struct dnsprobe * p,DNStype dnstype,const char * host,const char * url,CURLM * multi,struct curl_slist * headers)183 static CURLcode dohprobe(struct Curl_easy *data,
184                          struct dnsprobe *p, DNStype dnstype,
185                          const char *host,
186                          const char *url, CURLM *multi,
187                          struct curl_slist *headers)
188 {
189   struct Curl_easy *doh = NULL;
190   char *nurl = NULL;
191   CURLcode result = CURLE_OK;
192   timediff_t timeout_ms;
193   DOHcode d = doh_encode(host, dnstype, p->dohbuffer, sizeof(p->dohbuffer),
194                          &p->dohlen);
195   if(d) {
196     failf(data, "Failed to encode DOH packet [%d]\n", d);
197     return CURLE_OUT_OF_MEMORY;
198   }
199 
200   p->dnstype = dnstype;
201   p->serverdoh.memory = NULL;
202   /* the memory will be grown as needed by realloc in the doh_write_cb
203      function */
204   p->serverdoh.size = 0;
205 
206   /* Note: this is code for sending the DoH request with GET but there's still
207      no logic that actually enables this. We should either add that ability or
208      yank out the GET code. Discuss! */
209   if(data->set.doh_get) {
210     char *b64;
211     size_t b64len;
212     result = Curl_base64url_encode(data, (char *)p->dohbuffer, p->dohlen,
213                                    &b64, &b64len);
214     if(result)
215       goto error;
216     nurl = aprintf("%s?dns=%s", url, b64);
217     free(b64);
218     if(!nurl) {
219       result = CURLE_OUT_OF_MEMORY;
220       goto error;
221     }
222     url = nurl;
223   }
224 
225   timeout_ms = Curl_timeleft(data, NULL, TRUE);
226 
227   /* Curl_open() is the internal version of curl_easy_init() */
228   result = Curl_open(&doh);
229   if(!result) {
230     /* pass in the struct pointer via a local variable to please coverity and
231        the gcc typecheck helpers */
232     struct dohresponse *resp = &p->serverdoh;
233     ERROR_CHECK_SETOPT(CURLOPT_URL, url);
234     ERROR_CHECK_SETOPT(CURLOPT_WRITEFUNCTION, doh_write_cb);
235     ERROR_CHECK_SETOPT(CURLOPT_WRITEDATA, resp);
236     if(!data->set.doh_get) {
237       ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDS, p->dohbuffer);
238       ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDSIZE, (long)p->dohlen);
239     }
240     ERROR_CHECK_SETOPT(CURLOPT_HTTPHEADER, headers);
241 #ifdef USE_NGHTTP2
242     ERROR_CHECK_SETOPT(CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS);
243 #endif
244 #ifndef CURLDEBUG
245     /* enforce HTTPS if not debug */
246     ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTPS);
247 #endif
248     ERROR_CHECK_SETOPT(CURLOPT_TIMEOUT_MS, (long)timeout_ms);
249     if(data->set.verbose)
250       ERROR_CHECK_SETOPT(CURLOPT_VERBOSE, 1L);
251     if(data->set.no_signal)
252       ERROR_CHECK_SETOPT(CURLOPT_NOSIGNAL, 1L);
253 
254     /* Inherit *some* SSL options from the user's transfer. This is a
255        best-guess as to which options are needed for compatibility. #3661 */
256     if(data->set.ssl.falsestart)
257       ERROR_CHECK_SETOPT(CURLOPT_SSL_FALSESTART, 1L);
258     if(data->set.ssl.primary.verifyhost)
259       ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYHOST, 2L);
260     if(data->set.proxy_ssl.primary.verifyhost)
261       ERROR_CHECK_SETOPT(CURLOPT_PROXY_SSL_VERIFYHOST, 2L);
262     if(data->set.ssl.primary.verifypeer)
263       ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYPEER, 1L);
264     if(data->set.proxy_ssl.primary.verifypeer)
265       ERROR_CHECK_SETOPT(CURLOPT_PROXY_SSL_VERIFYPEER, 1L);
266     if(data->set.ssl.primary.verifystatus)
267       ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYSTATUS, 1L);
268     if(data->set.str[STRING_SSL_CAFILE_ORIG]) {
269       ERROR_CHECK_SETOPT(CURLOPT_CAINFO,
270         data->set.str[STRING_SSL_CAFILE_ORIG]);
271     }
272     if(data->set.str[STRING_SSL_CAFILE_PROXY]) {
273       ERROR_CHECK_SETOPT(CURLOPT_PROXY_CAINFO,
274         data->set.str[STRING_SSL_CAFILE_PROXY]);
275     }
276     if(data->set.str[STRING_SSL_CAPATH_ORIG]) {
277       ERROR_CHECK_SETOPT(CURLOPT_CAPATH,
278         data->set.str[STRING_SSL_CAPATH_ORIG]);
279     }
280     if(data->set.str[STRING_SSL_CAPATH_PROXY]) {
281       ERROR_CHECK_SETOPT(CURLOPT_PROXY_CAPATH,
282         data->set.str[STRING_SSL_CAPATH_PROXY]);
283     }
284     if(data->set.str[STRING_SSL_CRLFILE_ORIG]) {
285       ERROR_CHECK_SETOPT(CURLOPT_CRLFILE,
286         data->set.str[STRING_SSL_CRLFILE_ORIG]);
287     }
288     if(data->set.str[STRING_SSL_CRLFILE_PROXY]) {
289       ERROR_CHECK_SETOPT(CURLOPT_PROXY_CRLFILE,
290         data->set.str[STRING_SSL_CRLFILE_PROXY]);
291     }
292     if(data->set.ssl.certinfo)
293       ERROR_CHECK_SETOPT(CURLOPT_CERTINFO, 1L);
294     if(data->set.str[STRING_SSL_RANDOM_FILE]) {
295       ERROR_CHECK_SETOPT(CURLOPT_RANDOM_FILE,
296         data->set.str[STRING_SSL_RANDOM_FILE]);
297     }
298     if(data->set.str[STRING_SSL_EGDSOCKET]) {
299       ERROR_CHECK_SETOPT(CURLOPT_EGDSOCKET,
300         data->set.str[STRING_SSL_EGDSOCKET]);
301     }
302     if(data->set.ssl.no_revoke)
303       ERROR_CHECK_SETOPT(CURLOPT_SSL_OPTIONS, CURLSSLOPT_NO_REVOKE);
304     if(data->set.proxy_ssl.no_revoke)
305       ERROR_CHECK_SETOPT(CURLOPT_PROXY_SSL_OPTIONS, CURLSSLOPT_NO_REVOKE);
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 
311     doh->set.fmultidone = Curl_doh_done;
312     doh->set.dohfor = data; /* identify for which transfer this is done */
313     p->easy = doh;
314 
315     /* add this transfer to the multi handle */
316     if(curl_multi_add_handle(multi, doh))
317       goto error;
318   }
319   else
320     goto error;
321   free(nurl);
322   return CURLE_OK;
323 
324   error:
325   free(nurl);
326   Curl_close(doh);
327   return result;
328 }
329 
330 /*
331  * Curl_doh() resolves a name using DOH. It resolves a name and returns a
332  * 'Curl_addrinfo *' with the address information.
333  */
334 
Curl_doh(struct connectdata * conn,const char * hostname,int port,int * waitp)335 Curl_addrinfo *Curl_doh(struct connectdata *conn,
336                         const char *hostname,
337                         int port,
338                         int *waitp)
339 {
340   struct Curl_easy *data = conn->data;
341   CURLcode result = CURLE_OK;
342   *waitp = TRUE; /* this never returns synchronously */
343   (void)conn;
344   (void)hostname;
345   (void)port;
346 
347   /* start clean, consider allocating this struct on demand */
348   memset(&data->req.doh, 0, sizeof(struct dohdata));
349 
350   data->req.doh.host = hostname;
351   data->req.doh.port = port;
352   data->req.doh.headers =
353     curl_slist_append(NULL,
354                       "Content-Type: application/dns-message");
355   if(!data->req.doh.headers)
356     goto error;
357 
358   if(conn->ip_version != CURL_IPRESOLVE_V6) {
359     /* create IPv4 DOH request */
360     result = dohprobe(data, &data->req.doh.probe[0], DNS_TYPE_A,
361                       hostname, data->set.str[STRING_DOH],
362                       data->multi, data->req.doh.headers);
363     if(result)
364       goto error;
365     data->req.doh.pending++;
366   }
367 
368   if(conn->ip_version != CURL_IPRESOLVE_V4) {
369     /* create IPv6 DOH request */
370     result = dohprobe(data, &data->req.doh.probe[1], DNS_TYPE_AAAA,
371                       hostname, data->set.str[STRING_DOH],
372                       data->multi, data->req.doh.headers);
373     if(result)
374       goto error;
375     data->req.doh.pending++;
376   }
377   return NULL;
378 
379   error:
380   curl_slist_free_all(data->req.doh.headers);
381   data->req.doh.headers = NULL;
382   curl_easy_cleanup(data->req.doh.probe[0].easy);
383   data->req.doh.probe[0].easy = NULL;
384   curl_easy_cleanup(data->req.doh.probe[1].easy);
385   data->req.doh.probe[1].easy = NULL;
386   return NULL;
387 }
388 
skipqname(unsigned char * doh,size_t dohlen,unsigned int * indexp)389 static DOHcode skipqname(unsigned char *doh, size_t dohlen,
390                          unsigned int *indexp)
391 {
392   unsigned char length;
393   do {
394     if(dohlen < (*indexp + 1))
395       return DOH_DNS_OUT_OF_RANGE;
396     length = doh[*indexp];
397     if((length & 0xc0) == 0xc0) {
398       /* name pointer, advance over it and be done */
399       if(dohlen < (*indexp + 2))
400         return DOH_DNS_OUT_OF_RANGE;
401       *indexp += 2;
402       break;
403     }
404     if(length & 0xc0)
405       return DOH_DNS_BAD_LABEL;
406     if(dohlen < (*indexp + 1 + length))
407       return DOH_DNS_OUT_OF_RANGE;
408     *indexp += 1 + length;
409   } while(length);
410   return DOH_OK;
411 }
412 
get16bit(unsigned char * doh,int index)413 static unsigned short get16bit(unsigned char *doh, int index)
414 {
415   return (unsigned short)((doh[index] << 8) | doh[index + 1]);
416 }
417 
get32bit(unsigned char * doh,int index)418 static unsigned int get32bit(unsigned char *doh, int index)
419 {
420   return (doh[index] << 24) | (doh[index + 1] << 16) |
421     (doh[index + 2] << 8) | doh[index + 3];
422 }
423 
store_a(unsigned char * doh,int index,struct dohentry * d)424 static DOHcode store_a(unsigned char *doh, int index, struct dohentry *d)
425 {
426   /* silently ignore addresses over the limit */
427   if(d->numaddr < DOH_MAX_ADDR) {
428     struct dohaddr *a = &d->addr[d->numaddr];
429     a->type = DNS_TYPE_A;
430     memcpy(&a->ip.v4, &doh[index], 4);
431     d->numaddr++;
432   }
433   return DOH_OK;
434 }
435 
store_aaaa(unsigned char * doh,int index,struct dohentry * d)436 static DOHcode store_aaaa(unsigned char *doh, int index, struct dohentry *d)
437 {
438   /* silently ignore addresses over the limit */
439   if(d->numaddr < DOH_MAX_ADDR) {
440     struct dohaddr *a = &d->addr[d->numaddr];
441     a->type = DNS_TYPE_AAAA;
442     memcpy(&a->ip.v6, &doh[index], 16);
443     d->numaddr++;
444   }
445   return DOH_OK;
446 }
447 
cnameappend(struct cnamestore * c,unsigned char * src,size_t len)448 static DOHcode cnameappend(struct cnamestore *c,
449                            unsigned char *src,
450                            size_t len)
451 {
452   if(!c->alloc) {
453     c->allocsize = len + 1;
454     c->alloc = malloc(c->allocsize);
455     if(!c->alloc)
456       return DOH_OUT_OF_MEM;
457   }
458   else if(c->allocsize < (c->allocsize + len + 1)) {
459     char *ptr;
460     c->allocsize += len + 1;
461     ptr = realloc(c->alloc, c->allocsize);
462     if(!ptr) {
463       free(c->alloc);
464       return DOH_OUT_OF_MEM;
465     }
466     c->alloc = ptr;
467   }
468   memcpy(&c->alloc[c->len], src, len);
469   c->len += len;
470   c->alloc[c->len] = 0; /* keep it zero terminated */
471   return DOH_OK;
472 }
473 
store_cname(unsigned char * doh,size_t dohlen,unsigned int index,struct dohentry * d)474 static DOHcode store_cname(unsigned char *doh,
475                            size_t dohlen,
476                            unsigned int index,
477                            struct dohentry *d)
478 {
479   struct cnamestore *c;
480   unsigned int loop = 128; /* a valid DNS name can never loop this much */
481   unsigned char length;
482 
483   if(d->numcname == DOH_MAX_CNAME)
484     return DOH_OK; /* skip! */
485 
486   c = &d->cname[d->numcname++];
487   do {
488     if(index >= dohlen)
489       return DOH_DNS_OUT_OF_RANGE;
490     length = doh[index];
491     if((length & 0xc0) == 0xc0) {
492       int newpos;
493       /* name pointer, get the new offset (14 bits) */
494       if((index + 1) >= dohlen)
495         return DOH_DNS_OUT_OF_RANGE;
496 
497       /* move to the the new index */
498       newpos = (length & 0x3f) << 8 | doh[index + 1];
499       index = newpos;
500       continue;
501     }
502     else if(length & 0xc0)
503       return DOH_DNS_BAD_LABEL; /* bad input */
504     else
505       index++;
506 
507     if(length) {
508       DOHcode rc;
509       if(c->len) {
510         rc = cnameappend(c, (unsigned char *)".", 1);
511         if(rc)
512           return rc;
513       }
514       if((index + length) > dohlen)
515         return DOH_DNS_BAD_LABEL;
516 
517       rc = cnameappend(c, &doh[index], length);
518       if(rc)
519         return rc;
520       index += length;
521     }
522   } while(length && --loop);
523 
524   if(!loop)
525     return DOH_DNS_LABEL_LOOP;
526   return DOH_OK;
527 }
528 
rdata(unsigned char * doh,size_t dohlen,unsigned short rdlength,unsigned short type,int index,struct dohentry * d)529 static DOHcode rdata(unsigned char *doh,
530                      size_t dohlen,
531                      unsigned short rdlength,
532                      unsigned short type,
533                      int index,
534                      struct dohentry *d)
535 {
536   /* RDATA
537      - A (TYPE 1):  4 bytes
538      - AAAA (TYPE 28): 16 bytes
539      - NS (TYPE 2): N bytes */
540   DOHcode rc;
541 
542   switch(type) {
543   case DNS_TYPE_A:
544     if(rdlength != 4)
545       return DOH_DNS_RDATA_LEN;
546     rc = store_a(doh, index, d);
547     if(rc)
548       return rc;
549     break;
550   case DNS_TYPE_AAAA:
551     if(rdlength != 16)
552       return DOH_DNS_RDATA_LEN;
553     rc = store_aaaa(doh, index, d);
554     if(rc)
555       return rc;
556     break;
557   case DNS_TYPE_CNAME:
558     rc = store_cname(doh, dohlen, index, d);
559     if(rc)
560       return rc;
561     break;
562   default:
563     /* unsupported type, just skip it */
564     break;
565   }
566   return DOH_OK;
567 }
568 
init_dohentry(struct dohentry * de)569 static void init_dohentry(struct dohentry *de)
570 {
571   memset(de, 0, sizeof(*de));
572   de->ttl = INT_MAX;
573 }
574 
575 
doh_decode(unsigned char * doh,size_t dohlen,DNStype dnstype,struct dohentry * d)576 UNITTEST DOHcode doh_decode(unsigned char *doh,
577                             size_t dohlen,
578                             DNStype dnstype,
579                             struct dohentry *d)
580 {
581   unsigned char rcode;
582   unsigned short qdcount;
583   unsigned short ancount;
584   unsigned short type = 0;
585   unsigned short class;
586   unsigned short rdlength;
587   unsigned short nscount;
588   unsigned short arcount;
589   unsigned int index = 12;
590   DOHcode rc;
591 
592   if(dohlen < 12)
593     return DOH_TOO_SMALL_BUFFER; /* too small */
594   if(!doh || doh[0] || doh[1])
595     return DOH_DNS_BAD_ID; /* bad ID */
596   rcode = doh[3] & 0x0f;
597   if(rcode)
598     return DOH_DNS_BAD_RCODE; /* bad rcode */
599 
600   qdcount = get16bit(doh, 4);
601   while(qdcount) {
602     rc = skipqname(doh, dohlen, &index);
603     if(rc)
604       return rc; /* bad qname */
605     if(dohlen < (index + 4))
606       return DOH_DNS_OUT_OF_RANGE;
607     index += 4; /* skip question's type and class */
608     qdcount--;
609   }
610 
611   ancount = get16bit(doh, 6);
612   while(ancount) {
613     unsigned int ttl;
614 
615     rc = skipqname(doh, dohlen, &index);
616     if(rc)
617       return rc; /* bad qname */
618 
619     if(dohlen < (index + 2))
620       return DOH_DNS_OUT_OF_RANGE;
621 
622     type = get16bit(doh, index);
623     if((type != DNS_TYPE_CNAME) && (type != dnstype))
624       /* Not the same type as was asked for nor CNAME */
625       return DOH_DNS_UNEXPECTED_TYPE;
626     index += 2;
627 
628     if(dohlen < (index + 2))
629       return DOH_DNS_OUT_OF_RANGE;
630     class = get16bit(doh, index);
631     if(DNS_CLASS_IN != class)
632       return DOH_DNS_UNEXPECTED_CLASS; /* unsupported */
633     index += 2;
634 
635     if(dohlen < (index + 4))
636       return DOH_DNS_OUT_OF_RANGE;
637 
638     ttl = get32bit(doh, index);
639     if(ttl < d->ttl)
640       d->ttl = ttl;
641     index += 4;
642 
643     if(dohlen < (index + 2))
644       return DOH_DNS_OUT_OF_RANGE;
645 
646     rdlength = get16bit(doh, index);
647     index += 2;
648     if(dohlen < (index + rdlength))
649       return DOH_DNS_OUT_OF_RANGE;
650 
651     rc = rdata(doh, dohlen, rdlength, type, index, d);
652     if(rc)
653       return rc; /* bad rdata */
654     index += rdlength;
655     ancount--;
656   }
657 
658   nscount = get16bit(doh, 8);
659   while(nscount) {
660     rc = skipqname(doh, dohlen, &index);
661     if(rc)
662       return rc; /* bad qname */
663 
664     if(dohlen < (index + 8))
665       return DOH_DNS_OUT_OF_RANGE;
666 
667     index += 2 + 2 + 4; /* type, class and ttl */
668 
669     if(dohlen < (index + 2))
670       return DOH_DNS_OUT_OF_RANGE;
671 
672     rdlength = get16bit(doh, index);
673     index += 2;
674     if(dohlen < (index + rdlength))
675       return DOH_DNS_OUT_OF_RANGE;
676     index += rdlength;
677     nscount--;
678   }
679 
680   arcount = get16bit(doh, 10);
681   while(arcount) {
682     rc = skipqname(doh, dohlen, &index);
683     if(rc)
684       return rc; /* bad qname */
685 
686     if(dohlen < (index + 8))
687       return DOH_DNS_OUT_OF_RANGE;
688 
689     index += 2 + 2 + 4; /* type, class and ttl */
690 
691     if(dohlen < (index + 2))
692       return DOH_DNS_OUT_OF_RANGE;
693 
694     rdlength = get16bit(doh, index);
695     index += 2;
696     if(dohlen < (index + rdlength))
697       return DOH_DNS_OUT_OF_RANGE;
698     index += rdlength;
699     arcount--;
700   }
701 
702   if(index != dohlen)
703     return DOH_DNS_MALFORMAT; /* something is wrong */
704 
705   if((type != DNS_TYPE_NS) && !d->numcname && !d->numaddr)
706     /* nothing stored! */
707     return DOH_NO_CONTENT;
708 
709   return DOH_OK; /* ok */
710 }
711 
712 #ifndef CURL_DISABLE_VERBOSE_STRINGS
showdoh(struct Curl_easy * data,struct dohentry * d)713 static void showdoh(struct Curl_easy *data,
714                     struct dohentry *d)
715 {
716   int i;
717   infof(data, "TTL: %u seconds\n", d->ttl);
718   for(i = 0; i < d->numaddr; i++) {
719     struct dohaddr *a = &d->addr[i];
720     if(a->type == DNS_TYPE_A) {
721       infof(data, "DOH A: %u.%u.%u.%u\n",
722             a->ip.v4[0], a->ip.v4[1],
723             a->ip.v4[2], a->ip.v4[3]);
724     }
725     else if(a->type == DNS_TYPE_AAAA) {
726       int j;
727       char buffer[128];
728       char *ptr;
729       size_t len;
730       msnprintf(buffer, 128, "DOH AAAA: ");
731       ptr = &buffer[10];
732       len = 118;
733       for(j = 0; j < 16; j += 2) {
734         size_t l;
735         msnprintf(ptr, len, "%s%02x%02x", j?":":"", d->addr[i].ip.v6[j],
736                   d->addr[i].ip.v6[j + 1]);
737         l = strlen(ptr);
738         len -= l;
739         ptr += l;
740       }
741       infof(data, "%s\n", buffer);
742     }
743   }
744   for(i = 0; i < d->numcname; i++) {
745     infof(data, "CNAME: %s\n", d->cname[i].alloc);
746   }
747 }
748 #else
749 #define showdoh(x,y)
750 #endif
751 
752 /*
753  * doh2ai()
754  *
755  * This function returns a pointer to the first element of a newly allocated
756  * Curl_addrinfo struct linked list filled with the data from a set of DOH
757  * lookups.  Curl_addrinfo is meant to work like the addrinfo struct does for
758  * a IPv6 stack, but usable also for IPv4, all hosts and environments.
759  *
760  * The memory allocated by this function *MUST* be free'd later on calling
761  * Curl_freeaddrinfo().  For each successful call to this function there
762  * must be an associated call later to Curl_freeaddrinfo().
763  */
764 
765 static Curl_addrinfo *
doh2ai(const struct dohentry * de,const char * hostname,int port)766 doh2ai(const struct dohentry *de, const char *hostname, int port)
767 {
768   Curl_addrinfo *ai;
769   Curl_addrinfo *prevai = NULL;
770   Curl_addrinfo *firstai = NULL;
771   struct sockaddr_in *addr;
772 #ifdef ENABLE_IPV6
773   struct sockaddr_in6 *addr6;
774 #endif
775   CURLcode result = CURLE_OK;
776   int i;
777 
778   if(!de)
779     /* no input == no output! */
780     return NULL;
781 
782   for(i = 0; i < de->numaddr; i++) {
783     size_t ss_size;
784     CURL_SA_FAMILY_T addrtype;
785     if(de->addr[i].type == DNS_TYPE_AAAA) {
786 #ifndef ENABLE_IPV6
787       /* we can't handle IPv6 addresses */
788       continue;
789 #else
790       ss_size = sizeof(struct sockaddr_in6);
791       addrtype = AF_INET6;
792 #endif
793     }
794     else {
795       ss_size = sizeof(struct sockaddr_in);
796       addrtype = AF_INET;
797     }
798 
799     ai = calloc(1, sizeof(Curl_addrinfo));
800     if(!ai) {
801       result = CURLE_OUT_OF_MEMORY;
802       break;
803     }
804     ai->ai_canonname = strdup(hostname);
805     if(!ai->ai_canonname) {
806       result = CURLE_OUT_OF_MEMORY;
807       free(ai);
808       break;
809     }
810     ai->ai_addr = calloc(1, ss_size);
811     if(!ai->ai_addr) {
812       result = CURLE_OUT_OF_MEMORY;
813       free(ai->ai_canonname);
814       free(ai);
815       break;
816     }
817 
818     if(!firstai)
819       /* store the pointer we want to return from this function */
820       firstai = ai;
821 
822     if(prevai)
823       /* make the previous entry point to this */
824       prevai->ai_next = ai;
825 
826     ai->ai_family = addrtype;
827 
828     /* we return all names as STREAM, so when using this address for TFTP
829        the type must be ignored and conn->socktype be used instead! */
830     ai->ai_socktype = SOCK_STREAM;
831 
832     ai->ai_addrlen = (curl_socklen_t)ss_size;
833 
834     /* leave the rest of the struct filled with zero */
835 
836     switch(ai->ai_family) {
837     case AF_INET:
838       addr = (void *)ai->ai_addr; /* storage area for this info */
839       DEBUGASSERT(sizeof(struct in_addr) == sizeof(de->addr[i].ip.v4));
840       memcpy(&addr->sin_addr, &de->addr[i].ip.v4, sizeof(struct in_addr));
841       addr->sin_family = (CURL_SA_FAMILY_T)addrtype;
842       addr->sin_port = htons((unsigned short)port);
843       break;
844 
845 #ifdef ENABLE_IPV6
846     case AF_INET6:
847       addr6 = (void *)ai->ai_addr; /* storage area for this info */
848       DEBUGASSERT(sizeof(struct in6_addr) == sizeof(de->addr[i].ip.v6));
849       memcpy(&addr6->sin6_addr, &de->addr[i].ip.v6, sizeof(struct in6_addr));
850       addr6->sin6_family = (CURL_SA_FAMILY_T)addrtype;
851       addr6->sin6_port = htons((unsigned short)port);
852       break;
853 #endif
854     }
855 
856     prevai = ai;
857   }
858 
859   if(result) {
860     Curl_freeaddrinfo(firstai);
861     firstai = NULL;
862   }
863 
864   return firstai;
865 }
866 
867 #ifndef CURL_DISABLE_VERBOSE_STRINGS
type2name(DNStype dnstype)868 static const char *type2name(DNStype dnstype)
869 {
870   return (dnstype == DNS_TYPE_A)?"A":"AAAA";
871 }
872 #endif
873 
de_cleanup(struct dohentry * d)874 UNITTEST void de_cleanup(struct dohentry *d)
875 {
876   int i = 0;
877   for(i = 0; i < d->numcname; i++) {
878     free(d->cname[i].alloc);
879   }
880 }
881 
Curl_doh_is_resolved(struct connectdata * conn,struct Curl_dns_entry ** dnsp)882 CURLcode Curl_doh_is_resolved(struct connectdata *conn,
883                               struct Curl_dns_entry **dnsp)
884 {
885   struct Curl_easy *data = conn->data;
886   *dnsp = NULL; /* defaults to no response */
887 
888   if(!data->req.doh.probe[0].easy && !data->req.doh.probe[1].easy) {
889     failf(data, "Could not DOH-resolve: %s", conn->async.hostname);
890     return conn->bits.proxy?CURLE_COULDNT_RESOLVE_PROXY:
891       CURLE_COULDNT_RESOLVE_HOST;
892   }
893   else if(!data->req.doh.pending) {
894     DOHcode rc;
895     DOHcode rc2;
896     struct dohentry de;
897     struct Curl_dns_entry *dns;
898     struct Curl_addrinfo *ai;
899     /* remove DOH handles from multi handle and close them */
900     curl_multi_remove_handle(data->multi, data->req.doh.probe[0].easy);
901     Curl_close(data->req.doh.probe[0].easy);
902     curl_multi_remove_handle(data->multi, data->req.doh.probe[1].easy);
903     Curl_close(data->req.doh.probe[1].easy);
904 
905     /* parse the responses, create the struct and return it! */
906     init_dohentry(&de);
907     rc = doh_decode(data->req.doh.probe[0].serverdoh.memory,
908                     data->req.doh.probe[0].serverdoh.size,
909                     data->req.doh.probe[0].dnstype,
910                     &de);
911     free(data->req.doh.probe[0].serverdoh.memory);
912     if(rc) {
913       infof(data, "DOH: %s type %s for %s\n", doh_strerror(rc),
914             type2name(data->req.doh.probe[0].dnstype),
915             data->req.doh.host);
916     }
917     rc2 = doh_decode(data->req.doh.probe[1].serverdoh.memory,
918                      data->req.doh.probe[1].serverdoh.size,
919                      data->req.doh.probe[1].dnstype,
920                      &de);
921     free(data->req.doh.probe[1].serverdoh.memory);
922     if(rc2) {
923       infof(data, "DOH: %s type %s for %s\n", doh_strerror(rc2),
924             type2name(data->req.doh.probe[1].dnstype),
925             data->req.doh.host);
926     }
927     if(!rc || !rc2) {
928       infof(data, "DOH Host name: %s\n", data->req.doh.host);
929       showdoh(data, &de);
930 
931       ai = doh2ai(&de, data->req.doh.host, data->req.doh.port);
932       if(!ai) {
933         de_cleanup(&de);
934         return CURLE_OUT_OF_MEMORY;
935       }
936 
937       if(data->share)
938         Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
939 
940       /* we got a response, store it in the cache */
941       dns = Curl_cache_addr(data, ai, data->req.doh.host, data->req.doh.port);
942 
943       if(data->share)
944         Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
945 
946       de_cleanup(&de);
947       if(!dns)
948         /* returned failure, bail out nicely */
949         Curl_freeaddrinfo(ai);
950       else {
951         conn->async.dns = dns;
952         *dnsp = dns;
953         return CURLE_OK;
954       }
955     }
956     de_cleanup(&de);
957 
958     return CURLE_COULDNT_RESOLVE_HOST;
959   }
960 
961   return CURLE_OK;
962 }
963