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