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