• 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 #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 /* The last 3 #include files should be in this order */
39 #include "curl_printf.h"
40 #include "curl_memory.h"
41 #include "memdebug.h"
42 
43 #define DNS_CLASS_IN 0x01
44 #define DOH_MAX_RESPONSE_SIZE 3000 /* bytes */
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 does not depend on the number of dots within
90    * the host name. It will always be two more than the length of the host
91    * name, one for the size and one trailing null. In case there are dots,
92    * each dot adds one size but removes the need to store the dot, net zero.
93    */
94   const size_t expected_len = 12 + ( 1 + hostlen + 1) + 4;
95 
96   if(len < expected_len)
97     return DOH_TOO_SMALL_BUFFER;
98 
99   *dnsp++ = 0; /* 16 bit id */
100   *dnsp++ = 0;
101   *dnsp++ = 0x01; /* |QR|   Opcode  |AA|TC|RD| Set the RD bit */
102   *dnsp++ = '\0'; /* |RA|   Z    |   RCODE   |                */
103   *dnsp++ = '\0';
104   *dnsp++ = 1;    /* QDCOUNT (number of entries in the question section) */
105   *dnsp++ = '\0';
106   *dnsp++ = '\0'; /* ANCOUNT */
107   *dnsp++ = '\0';
108   *dnsp++ = '\0'; /* NSCOUNT */
109   *dnsp++ = '\0';
110   *dnsp++ = '\0'; /* ARCOUNT */
111 
112   /* store a QNAME */
113   do {
114     char *dot = strchr(hostp, '.');
115     size_t labellen;
116     bool found = false;
117     if(dot) {
118       found = true;
119       labellen = dot - hostp;
120     }
121     else
122       labellen = strlen(hostp);
123     if(labellen > 63) {
124       /* too long label, error out */
125       *olen = 0;
126       return DOH_DNS_BAD_LABEL;
127     }
128     *dnsp++ = (unsigned char)labellen;
129     memcpy(dnsp, hostp, labellen);
130     dnsp += labellen;
131     hostp += labellen + 1;
132     if(!found) {
133       *dnsp++ = 0; /* terminating zero */
134       break;
135     }
136   } while(1);
137 
138   /* There are assigned TYPE codes beyond 255: use range [1..65535]  */
139   *dnsp++ = (unsigned char)(255 & (dnstype>>8)); /* upper 8 bit TYPE */
140   *dnsp++ = (unsigned char)(255 & dnstype);      /* lower 8 bit TYPE */
141 
142   *dnsp++ = '\0'; /* upper 8 bit CLASS */
143   *dnsp++ = DNS_CLASS_IN; /* IN - "the Internet" */
144 
145   *olen = dnsp - orig;
146 
147   /* verify that our assumption of length is valid, since
148    * this has lead to buffer overflows in this function */
149   DEBUGASSERT(*olen == expected_len);
150   return DOH_OK;
151 }
152 
153 static size_t
doh_write_cb(void * contents,size_t size,size_t nmemb,void * userp)154 doh_write_cb(void *contents, size_t size, size_t nmemb, void *userp)
155 {
156   size_t realsize = size * nmemb;
157   struct dohresponse *mem = (struct dohresponse *)userp;
158 
159   if((mem->size + realsize) > DOH_MAX_RESPONSE_SIZE)
160     /* suspiciously much for us */
161     return 0;
162 
163   mem->memory = Curl_saferealloc(mem->memory, mem->size + realsize);
164   if(!mem->memory)
165     /* out of memory! */
166     return 0;
167 
168   memcpy(&(mem->memory[mem->size]), contents, realsize);
169   mem->size += realsize;
170 
171   return realsize;
172 }
173 
174 /* called from multi.c when this DOH transfer is complete */
Curl_doh_done(struct Curl_easy * doh,CURLcode result)175 static int Curl_doh_done(struct Curl_easy *doh, CURLcode result)
176 {
177   struct Curl_easy *data = doh->set.dohfor;
178   /* so one of the DOH request done for the 'data' transfer is now complete! */
179   data->req.doh.pending--;
180   infof(data, "a DOH request is completed, %u to go\n", data->req.doh.pending);
181   if(result)
182     infof(data, "DOH request %s\n", curl_easy_strerror(result));
183 
184   if(!data->req.doh.pending) {
185     /* DOH completed */
186     curl_slist_free_all(data->req.doh.headers);
187     data->req.doh.headers = NULL;
188     Curl_expire(data, 0, EXPIRE_RUN_NOW);
189   }
190   return 0;
191 }
192 
193 #define ERROR_CHECK_SETOPT(x,y) \
194 do {                                      \
195   result = curl_easy_setopt(doh, x, y);   \
196   if(result)                              \
197     goto error;                           \
198 } WHILE_FALSE
199 
dohprobe(struct Curl_easy * data,struct dnsprobe * p,DNStype dnstype,const char * host,const char * url,CURLM * multi,struct curl_slist * headers)200 static CURLcode dohprobe(struct Curl_easy *data,
201                          struct dnsprobe *p, DNStype dnstype,
202                          const char *host,
203                          const char *url, CURLM *multi,
204                          struct curl_slist *headers)
205 {
206   struct Curl_easy *doh = NULL;
207   char *nurl = NULL;
208   CURLcode result = CURLE_OK;
209   timediff_t timeout_ms;
210   DOHcode d = doh_encode(host, dnstype, p->dohbuffer, sizeof(p->dohbuffer),
211                          &p->dohlen);
212   if(d) {
213     failf(data, "Failed to encode DOH packet [%d]\n", d);
214     return CURLE_OUT_OF_MEMORY;
215   }
216 
217   p->dnstype = dnstype;
218   p->serverdoh.memory = NULL;
219   /* the memory will be grown as needed by realloc in the doh_write_cb
220      function */
221   p->serverdoh.size = 0;
222 
223   /* Note: this is code for sending the DoH request with GET but there's still
224      no logic that actually enables this. We should either add that ability or
225      yank out the GET code. Discuss! */
226   if(data->set.doh_get) {
227     char *b64;
228     size_t b64len;
229     result = Curl_base64url_encode(data, (char *)p->dohbuffer, p->dohlen,
230                                    &b64, &b64len);
231     if(result)
232       goto error;
233     nurl = aprintf("%s?dns=%s", url, b64);
234     free(b64);
235     if(!nurl) {
236       result = CURLE_OUT_OF_MEMORY;
237       goto error;
238     }
239     url = nurl;
240   }
241 
242   timeout_ms = Curl_timeleft(data, NULL, TRUE);
243   if(timeout_ms <= 0) {
244     result = CURLE_OPERATION_TIMEDOUT;
245     goto error;
246   }
247   /* Curl_open() is the internal version of curl_easy_init() */
248   result = Curl_open(&doh);
249   if(!result) {
250     /* pass in the struct pointer via a local variable to please coverity and
251        the gcc typecheck helpers */
252     struct dohresponse *resp = &p->serverdoh;
253     ERROR_CHECK_SETOPT(CURLOPT_URL, url);
254     ERROR_CHECK_SETOPT(CURLOPT_WRITEFUNCTION, doh_write_cb);
255     ERROR_CHECK_SETOPT(CURLOPT_WRITEDATA, resp);
256     if(!data->set.doh_get) {
257       ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDS, p->dohbuffer);
258       ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDSIZE, (long)p->dohlen);
259     }
260     ERROR_CHECK_SETOPT(CURLOPT_HTTPHEADER, headers);
261 #ifdef USE_NGHTTP2
262     ERROR_CHECK_SETOPT(CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS);
263 #endif
264 #ifndef CURLDEBUG
265     /* enforce HTTPS if not debug */
266     ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTPS);
267 #else
268     /* in debug mode, also allow http */
269     ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTP|CURLPROTO_HTTPS);
270 #endif
271     ERROR_CHECK_SETOPT(CURLOPT_TIMEOUT_MS, (long)timeout_ms);
272     if(data->set.verbose)
273       ERROR_CHECK_SETOPT(CURLOPT_VERBOSE, 1L);
274     if(data->set.no_signal)
275       ERROR_CHECK_SETOPT(CURLOPT_NOSIGNAL, 1L);
276 
277     /* Inherit *some* SSL options from the user's transfer. This is a
278        best-guess as to which options are needed for compatibility. #3661 */
279     if(data->set.ssl.falsestart)
280       ERROR_CHECK_SETOPT(CURLOPT_SSL_FALSESTART, 1L);
281     if(data->set.ssl.primary.verifyhost)
282       ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYHOST, 2L);
283     if(data->set.proxy_ssl.primary.verifyhost)
284       ERROR_CHECK_SETOPT(CURLOPT_PROXY_SSL_VERIFYHOST, 2L);
285     if(data->set.ssl.primary.verifypeer)
286       ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYPEER, 1L);
287     if(data->set.proxy_ssl.primary.verifypeer)
288       ERROR_CHECK_SETOPT(CURLOPT_PROXY_SSL_VERIFYPEER, 1L);
289     if(data->set.ssl.primary.verifystatus)
290       ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYSTATUS, 1L);
291     if(data->set.str[STRING_SSL_CAFILE_ORIG]) {
292       ERROR_CHECK_SETOPT(CURLOPT_CAINFO,
293         data->set.str[STRING_SSL_CAFILE_ORIG]);
294     }
295     if(data->set.str[STRING_SSL_CAFILE_PROXY]) {
296       ERROR_CHECK_SETOPT(CURLOPT_PROXY_CAINFO,
297         data->set.str[STRING_SSL_CAFILE_PROXY]);
298     }
299     if(data->set.str[STRING_SSL_CAPATH_ORIG]) {
300       ERROR_CHECK_SETOPT(CURLOPT_CAPATH,
301         data->set.str[STRING_SSL_CAPATH_ORIG]);
302     }
303     if(data->set.str[STRING_SSL_CAPATH_PROXY]) {
304       ERROR_CHECK_SETOPT(CURLOPT_PROXY_CAPATH,
305         data->set.str[STRING_SSL_CAPATH_PROXY]);
306     }
307     if(data->set.str[STRING_SSL_CRLFILE_ORIG]) {
308       ERROR_CHECK_SETOPT(CURLOPT_CRLFILE,
309         data->set.str[STRING_SSL_CRLFILE_ORIG]);
310     }
311     if(data->set.str[STRING_SSL_CRLFILE_PROXY]) {
312       ERROR_CHECK_SETOPT(CURLOPT_PROXY_CRLFILE,
313         data->set.str[STRING_SSL_CRLFILE_PROXY]);
314     }
315     if(data->set.ssl.certinfo)
316       ERROR_CHECK_SETOPT(CURLOPT_CERTINFO, 1L);
317     if(data->set.str[STRING_SSL_RANDOM_FILE]) {
318       ERROR_CHECK_SETOPT(CURLOPT_RANDOM_FILE,
319         data->set.str[STRING_SSL_RANDOM_FILE]);
320     }
321     if(data->set.str[STRING_SSL_EGDSOCKET]) {
322       ERROR_CHECK_SETOPT(CURLOPT_EGDSOCKET,
323         data->set.str[STRING_SSL_EGDSOCKET]);
324     }
325     if(data->set.ssl.no_revoke)
326       ERROR_CHECK_SETOPT(CURLOPT_SSL_OPTIONS, CURLSSLOPT_NO_REVOKE);
327     if(data->set.proxy_ssl.no_revoke)
328       ERROR_CHECK_SETOPT(CURLOPT_PROXY_SSL_OPTIONS, CURLSSLOPT_NO_REVOKE);
329     if(data->set.ssl.fsslctx)
330       ERROR_CHECK_SETOPT(CURLOPT_SSL_CTX_FUNCTION, data->set.ssl.fsslctx);
331     if(data->set.ssl.fsslctxp)
332       ERROR_CHECK_SETOPT(CURLOPT_SSL_CTX_DATA, data->set.ssl.fsslctxp);
333 
334     doh->set.fmultidone = Curl_doh_done;
335     doh->set.dohfor = data; /* identify for which transfer this is done */
336     p->easy = doh;
337 
338     /* add this transfer to the multi handle */
339     if(curl_multi_add_handle(multi, doh))
340       goto error;
341   }
342   else
343     goto error;
344   free(nurl);
345   return CURLE_OK;
346 
347   error:
348   free(nurl);
349   Curl_close(&doh);
350   return result;
351 }
352 
353 /*
354  * Curl_doh() resolves a name using DOH. It resolves a name and returns a
355  * 'Curl_addrinfo *' with the address information.
356  */
357 
Curl_doh(struct connectdata * conn,const char * hostname,int port,int * waitp)358 Curl_addrinfo *Curl_doh(struct connectdata *conn,
359                         const char *hostname,
360                         int port,
361                         int *waitp)
362 {
363   struct Curl_easy *data = conn->data;
364   CURLcode result = CURLE_OK;
365   *waitp = TRUE; /* this never returns synchronously */
366   (void)conn;
367   (void)hostname;
368   (void)port;
369 
370   /* start clean, consider allocating this struct on demand */
371   memset(&data->req.doh, 0, sizeof(struct dohdata));
372 
373   data->req.doh.host = hostname;
374   data->req.doh.port = port;
375   data->req.doh.headers =
376     curl_slist_append(NULL,
377                       "Content-Type: application/dns-message");
378   if(!data->req.doh.headers)
379     goto error;
380 
381   if(conn->ip_version != CURL_IPRESOLVE_V6) {
382     /* create IPv4 DOH request */
383     result = dohprobe(data, &data->req.doh.probe[0], DNS_TYPE_A,
384                       hostname, data->set.str[STRING_DOH],
385                       data->multi, data->req.doh.headers);
386     if(result)
387       goto error;
388     data->req.doh.pending++;
389   }
390 
391   if(conn->ip_version != CURL_IPRESOLVE_V4) {
392     /* create IPv6 DOH request */
393     result = dohprobe(data, &data->req.doh.probe[1], DNS_TYPE_AAAA,
394                       hostname, data->set.str[STRING_DOH],
395                       data->multi, data->req.doh.headers);
396     if(result)
397       goto error;
398     data->req.doh.pending++;
399   }
400   return NULL;
401 
402   error:
403   curl_slist_free_all(data->req.doh.headers);
404   data->req.doh.headers = NULL;
405   Curl_close(&data->req.doh.probe[0].easy);
406   Curl_close(&data->req.doh.probe[1].easy);
407   return NULL;
408 }
409 
skipqname(unsigned char * doh,size_t dohlen,unsigned int * indexp)410 static DOHcode skipqname(unsigned char *doh, size_t dohlen,
411                          unsigned int *indexp)
412 {
413   unsigned char length;
414   do {
415     if(dohlen < (*indexp + 1))
416       return DOH_DNS_OUT_OF_RANGE;
417     length = doh[*indexp];
418     if((length & 0xc0) == 0xc0) {
419       /* name pointer, advance over it and be done */
420       if(dohlen < (*indexp + 2))
421         return DOH_DNS_OUT_OF_RANGE;
422       *indexp += 2;
423       break;
424     }
425     if(length & 0xc0)
426       return DOH_DNS_BAD_LABEL;
427     if(dohlen < (*indexp + 1 + length))
428       return DOH_DNS_OUT_OF_RANGE;
429     *indexp += 1 + length;
430   } while(length);
431   return DOH_OK;
432 }
433 
get16bit(unsigned char * doh,int index)434 static unsigned short get16bit(unsigned char *doh, int index)
435 {
436   return (unsigned short)((doh[index] << 8) | doh[index + 1]);
437 }
438 
get32bit(unsigned char * doh,int index)439 static unsigned int get32bit(unsigned char *doh, int index)
440 {
441    /* make clang and gcc optimize this to bswap by incrementing
442       the pointer first. */
443    doh += index;
444 
445    /* avoid undefined behaviour by casting to unsigned before shifting
446       24 bits, possibly into the sign bit. codegen is same, but
447       ub sanitizer won't be upset */
448   return ( (unsigned)doh[0] << 24) | (doh[1] << 16) |(doh[2] << 8) | doh[3];
449 }
450 
store_a(unsigned char * doh,int index,struct dohentry * d)451 static DOHcode store_a(unsigned char *doh, int index, struct dohentry *d)
452 {
453   /* silently ignore addresses over the limit */
454   if(d->numaddr < DOH_MAX_ADDR) {
455     struct dohaddr *a = &d->addr[d->numaddr];
456     a->type = DNS_TYPE_A;
457     memcpy(&a->ip.v4, &doh[index], 4);
458     d->numaddr++;
459   }
460   return DOH_OK;
461 }
462 
store_aaaa(unsigned char * doh,int index,struct dohentry * d)463 static DOHcode store_aaaa(unsigned char *doh, int index, struct dohentry *d)
464 {
465   /* silently ignore addresses over the limit */
466   if(d->numaddr < DOH_MAX_ADDR) {
467     struct dohaddr *a = &d->addr[d->numaddr];
468     a->type = DNS_TYPE_AAAA;
469     memcpy(&a->ip.v6, &doh[index], 16);
470     d->numaddr++;
471   }
472   return DOH_OK;
473 }
474 
cnameappend(struct cnamestore * c,unsigned char * src,size_t len)475 static DOHcode cnameappend(struct cnamestore *c,
476                            unsigned char *src,
477                            size_t len)
478 {
479   if(!c->alloc) {
480     c->allocsize = len + 1;
481     c->alloc = malloc(c->allocsize);
482     if(!c->alloc)
483       return DOH_OUT_OF_MEM;
484   }
485   else if(c->allocsize < (c->allocsize + len + 1)) {
486     char *ptr;
487     c->allocsize += len + 1;
488     ptr = realloc(c->alloc, c->allocsize);
489     if(!ptr) {
490       free(c->alloc);
491       return DOH_OUT_OF_MEM;
492     }
493     c->alloc = ptr;
494   }
495   memcpy(&c->alloc[c->len], src, len);
496   c->len += len;
497   c->alloc[c->len] = 0; /* keep it zero terminated */
498   return DOH_OK;
499 }
500 
store_cname(unsigned char * doh,size_t dohlen,unsigned int index,struct dohentry * d)501 static DOHcode store_cname(unsigned char *doh,
502                            size_t dohlen,
503                            unsigned int index,
504                            struct dohentry *d)
505 {
506   struct cnamestore *c;
507   unsigned int loop = 128; /* a valid DNS name can never loop this much */
508   unsigned char length;
509 
510   if(d->numcname == DOH_MAX_CNAME)
511     return DOH_OK; /* skip! */
512 
513   c = &d->cname[d->numcname++];
514   do {
515     if(index >= dohlen)
516       return DOH_DNS_OUT_OF_RANGE;
517     length = doh[index];
518     if((length & 0xc0) == 0xc0) {
519       int newpos;
520       /* name pointer, get the new offset (14 bits) */
521       if((index + 1) >= dohlen)
522         return DOH_DNS_OUT_OF_RANGE;
523 
524       /* move to the the new index */
525       newpos = (length & 0x3f) << 8 | doh[index + 1];
526       index = newpos;
527       continue;
528     }
529     else if(length & 0xc0)
530       return DOH_DNS_BAD_LABEL; /* bad input */
531     else
532       index++;
533 
534     if(length) {
535       DOHcode rc;
536       if(c->len) {
537         rc = cnameappend(c, (unsigned char *)".", 1);
538         if(rc)
539           return rc;
540       }
541       if((index + length) > dohlen)
542         return DOH_DNS_BAD_LABEL;
543 
544       rc = cnameappend(c, &doh[index], length);
545       if(rc)
546         return rc;
547       index += length;
548     }
549   } while(length && --loop);
550 
551   if(!loop)
552     return DOH_DNS_LABEL_LOOP;
553   return DOH_OK;
554 }
555 
rdata(unsigned char * doh,size_t dohlen,unsigned short rdlength,unsigned short type,int index,struct dohentry * d)556 static DOHcode rdata(unsigned char *doh,
557                      size_t dohlen,
558                      unsigned short rdlength,
559                      unsigned short type,
560                      int index,
561                      struct dohentry *d)
562 {
563   /* RDATA
564      - A (TYPE 1):  4 bytes
565      - AAAA (TYPE 28): 16 bytes
566      - NS (TYPE 2): N bytes */
567   DOHcode rc;
568 
569   switch(type) {
570   case DNS_TYPE_A:
571     if(rdlength != 4)
572       return DOH_DNS_RDATA_LEN;
573     rc = store_a(doh, index, d);
574     if(rc)
575       return rc;
576     break;
577   case DNS_TYPE_AAAA:
578     if(rdlength != 16)
579       return DOH_DNS_RDATA_LEN;
580     rc = store_aaaa(doh, index, d);
581     if(rc)
582       return rc;
583     break;
584   case DNS_TYPE_CNAME:
585     rc = store_cname(doh, dohlen, index, d);
586     if(rc)
587       return rc;
588     break;
589   default:
590     /* unsupported type, just skip it */
591     break;
592   }
593   return DOH_OK;
594 }
595 
init_dohentry(struct dohentry * de)596 static void init_dohentry(struct dohentry *de)
597 {
598   memset(de, 0, sizeof(*de));
599   de->ttl = INT_MAX;
600 }
601 
602 
doh_decode(unsigned char * doh,size_t dohlen,DNStype dnstype,struct dohentry * d)603 UNITTEST DOHcode doh_decode(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) && (type != dnstype))
651       /* Not the same type as was asked for nor CNAME */
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,struct dohentry * d)740 static void showdoh(struct Curl_easy *data,
741                     struct dohentry *d)
742 {
743   int i;
744   infof(data, "TTL: %u seconds\n", d->ttl);
745   for(i = 0; i < d->numaddr; i++) {
746     struct dohaddr *a = &d->addr[i];
747     if(a->type == DNS_TYPE_A) {
748       infof(data, "DOH A: %u.%u.%u.%u\n",
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\n", buffer);
769     }
770   }
771   for(i = 0; i < d->numcname; i++) {
772     infof(data, "CNAME: %s\n", d->cname[i].alloc);
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 
792 static Curl_addrinfo *
doh2ai(const struct dohentry * de,const char * hostname,int port)793 doh2ai(const struct dohentry *de, const char *hostname, int port)
794 {
795   Curl_addrinfo *ai;
796   Curl_addrinfo *prevai = NULL;
797   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 
805   if(!de)
806     /* no input == no output! */
807     return NULL;
808 
809   for(i = 0; i < de->numaddr; i++) {
810     size_t ss_size;
811     CURL_SA_FAMILY_T addrtype;
812     if(de->addr[i].type == DNS_TYPE_AAAA) {
813 #ifndef ENABLE_IPV6
814       /* we can't handle IPv6 addresses */
815       continue;
816 #else
817       ss_size = sizeof(struct sockaddr_in6);
818       addrtype = AF_INET6;
819 #endif
820     }
821     else {
822       ss_size = sizeof(struct sockaddr_in);
823       addrtype = AF_INET;
824     }
825 
826     ai = calloc(1, sizeof(Curl_addrinfo));
827     if(!ai) {
828       result = CURLE_OUT_OF_MEMORY;
829       break;
830     }
831     ai->ai_canonname = strdup(hostname);
832     if(!ai->ai_canonname) {
833       result = CURLE_OUT_OF_MEMORY;
834       free(ai);
835       break;
836     }
837     ai->ai_addr = calloc(1, ss_size);
838     if(!ai->ai_addr) {
839       result = CURLE_OUT_OF_MEMORY;
840       free(ai->ai_canonname);
841       free(ai);
842       break;
843     }
844 
845     if(!firstai)
846       /* store the pointer we want to return from this function */
847       firstai = ai;
848 
849     if(prevai)
850       /* make the previous entry point to this */
851       prevai->ai_next = ai;
852 
853     ai->ai_family = addrtype;
854 
855     /* we return all names as STREAM, so when using this address for TFTP
856        the type must be ignored and conn->socktype be used instead! */
857     ai->ai_socktype = SOCK_STREAM;
858 
859     ai->ai_addrlen = (curl_socklen_t)ss_size;
860 
861     /* leave the rest of the struct filled with zero */
862 
863     switch(ai->ai_family) {
864     case AF_INET:
865       addr = (void *)ai->ai_addr; /* storage area for this info */
866       DEBUGASSERT(sizeof(struct in_addr) == sizeof(de->addr[i].ip.v4));
867       memcpy(&addr->sin_addr, &de->addr[i].ip.v4, sizeof(struct in_addr));
868       addr->sin_family = (CURL_SA_FAMILY_T)addrtype;
869       addr->sin_port = htons((unsigned short)port);
870       break;
871 
872 #ifdef ENABLE_IPV6
873     case AF_INET6:
874       addr6 = (void *)ai->ai_addr; /* storage area for this info */
875       DEBUGASSERT(sizeof(struct in6_addr) == sizeof(de->addr[i].ip.v6));
876       memcpy(&addr6->sin6_addr, &de->addr[i].ip.v6, sizeof(struct in6_addr));
877       addr6->sin6_family = (CURL_SA_FAMILY_T)addrtype;
878       addr6->sin6_port = htons((unsigned short)port);
879       break;
880 #endif
881     }
882 
883     prevai = ai;
884   }
885 
886   if(result) {
887     Curl_freeaddrinfo(firstai);
888     firstai = NULL;
889   }
890 
891   return firstai;
892 }
893 
894 #ifndef CURL_DISABLE_VERBOSE_STRINGS
type2name(DNStype dnstype)895 static const char *type2name(DNStype dnstype)
896 {
897   return (dnstype == DNS_TYPE_A)?"A":"AAAA";
898 }
899 #endif
900 
de_cleanup(struct dohentry * d)901 UNITTEST void de_cleanup(struct dohentry *d)
902 {
903   int i = 0;
904   for(i = 0; i < d->numcname; i++) {
905     free(d->cname[i].alloc);
906   }
907 }
908 
Curl_doh_is_resolved(struct connectdata * conn,struct Curl_dns_entry ** dnsp)909 CURLcode Curl_doh_is_resolved(struct connectdata *conn,
910                               struct Curl_dns_entry **dnsp)
911 {
912   struct Curl_easy *data = conn->data;
913   *dnsp = NULL; /* defaults to no response */
914 
915   if(!data->req.doh.probe[0].easy && !data->req.doh.probe[1].easy) {
916     failf(data, "Could not DOH-resolve: %s", conn->async.hostname);
917     return conn->bits.proxy?CURLE_COULDNT_RESOLVE_PROXY:
918       CURLE_COULDNT_RESOLVE_HOST;
919   }
920   else if(!data->req.doh.pending) {
921     DOHcode rc;
922     DOHcode rc2;
923     struct dohentry de;
924     /* remove DOH handles from multi handle and close them */
925     curl_multi_remove_handle(data->multi, data->req.doh.probe[0].easy);
926     Curl_close(&data->req.doh.probe[0].easy);
927     curl_multi_remove_handle(data->multi, data->req.doh.probe[1].easy);
928     Curl_close(&data->req.doh.probe[1].easy);
929     /* parse the responses, create the struct and return it! */
930     init_dohentry(&de);
931     rc = doh_decode(data->req.doh.probe[0].serverdoh.memory,
932                     data->req.doh.probe[0].serverdoh.size,
933                     data->req.doh.probe[0].dnstype,
934                     &de);
935     Curl_safefree(data->req.doh.probe[0].serverdoh.memory);
936     if(rc) {
937       infof(data, "DOH: %s type %s for %s\n", doh_strerror(rc),
938             type2name(data->req.doh.probe[0].dnstype),
939             data->req.doh.host);
940     }
941     rc2 = doh_decode(data->req.doh.probe[1].serverdoh.memory,
942                      data->req.doh.probe[1].serverdoh.size,
943                      data->req.doh.probe[1].dnstype,
944                      &de);
945     Curl_safefree(data->req.doh.probe[1].serverdoh.memory);
946     if(rc2) {
947       infof(data, "DOH: %s type %s for %s\n", doh_strerror(rc2),
948             type2name(data->req.doh.probe[1].dnstype),
949             data->req.doh.host);
950     }
951     if(!rc || !rc2) {
952       struct Curl_dns_entry *dns;
953       struct Curl_addrinfo *ai;
954 
955       infof(data, "DOH Host name: %s\n", data->req.doh.host);
956       showdoh(data, &de);
957 
958       ai = doh2ai(&de, data->req.doh.host, data->req.doh.port);
959       if(!ai) {
960         de_cleanup(&de);
961         return CURLE_OUT_OF_MEMORY;
962       }
963 
964       if(data->share)
965         Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
966 
967       /* we got a response, store it in the cache */
968       dns = Curl_cache_addr(data, ai, data->req.doh.host, data->req.doh.port);
969 
970       if(data->share)
971         Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
972 
973       de_cleanup(&de);
974       if(!dns)
975         /* returned failure, bail out nicely */
976         Curl_freeaddrinfo(ai);
977       else {
978         conn->async.dns = dns;
979         *dnsp = dns;
980         return CURLE_OK;
981       }
982     }
983     de_cleanup(&de);
984 
985     return CURLE_COULDNT_RESOLVE_HOST;
986   }
987 
988   return CURLE_OK;
989 }
990 
991 #endif /* CURL_DISABLE_DOH */
992