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