1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 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.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
49 static 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
doh_strerror(DOHcode code)66 static 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 */
doh_encode(const char * host,DNStype dnstype,unsigned char * dnsp,size_t len,size_t * olen)76 UNITTEST 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
173 static size_t
doh_write_cb(const void * contents,size_t size,size_t nmemb,void * userp)174 doh_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 */
doh_done(struct Curl_easy * doh,CURLcode result)186 static 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) \
206 do { \
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
dohprobe(struct Curl_easy * data,struct dnsprobe * p,DNStype dnstype,const char * host,const char * url,CURLM * multi,struct curl_slist * headers)214 static 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
Curl_doh(struct Curl_easy * data,const char * hostname,int port,int * waitp)361 struct 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 #ifdef ENABLE_IPV6
400 if((conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
401 /* create IPv6 DoH request */
402 result = dohprobe(data, &dohp->probe[DOH_PROBE_SLOT_IPADDR_V6],
403 DNS_TYPE_AAAA, hostname, data->set.str[STRING_DOH],
404 data->multi, dohp->headers);
405 if(result)
406 goto error;
407 dohp->pending++;
408 }
409 #endif
410 return NULL;
411
412 error:
413 curl_slist_free_all(dohp->headers);
414 data->req.doh->headers = NULL;
415 for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) {
416 Curl_close(&dohp->probe[slot].easy);
417 }
418 Curl_safefree(data->req.doh);
419 return NULL;
420 }
421
skipqname(const unsigned char * doh,size_t dohlen,unsigned int * indexp)422 static DOHcode skipqname(const unsigned char *doh, size_t dohlen,
423 unsigned int *indexp)
424 {
425 unsigned char length;
426 do {
427 if(dohlen < (*indexp + 1))
428 return DOH_DNS_OUT_OF_RANGE;
429 length = doh[*indexp];
430 if((length & 0xc0) == 0xc0) {
431 /* name pointer, advance over it and be done */
432 if(dohlen < (*indexp + 2))
433 return DOH_DNS_OUT_OF_RANGE;
434 *indexp += 2;
435 break;
436 }
437 if(length & 0xc0)
438 return DOH_DNS_BAD_LABEL;
439 if(dohlen < (*indexp + 1 + length))
440 return DOH_DNS_OUT_OF_RANGE;
441 *indexp += 1 + length;
442 } while(length);
443 return DOH_OK;
444 }
445
get16bit(const unsigned char * doh,int index)446 static unsigned short get16bit(const unsigned char *doh, int index)
447 {
448 return (unsigned short)((doh[index] << 8) | doh[index + 1]);
449 }
450
get32bit(const unsigned char * doh,int index)451 static unsigned int get32bit(const unsigned char *doh, int index)
452 {
453 /* make clang and gcc optimize this to bswap by incrementing
454 the pointer first. */
455 doh += index;
456
457 /* avoid undefined behavior by casting to unsigned before shifting
458 24 bits, possibly into the sign bit. codegen is same, but
459 ub sanitizer won't be upset */
460 return ( (unsigned)doh[0] << 24) | (doh[1] << 16) |(doh[2] << 8) | doh[3];
461 }
462
store_a(const unsigned char * doh,int index,struct dohentry * d)463 static DOHcode store_a(const 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_A;
469 memcpy(&a->ip.v4, &doh[index], 4);
470 d->numaddr++;
471 }
472 return DOH_OK;
473 }
474
store_aaaa(const unsigned char * doh,int index,struct dohentry * d)475 static DOHcode store_aaaa(const unsigned char *doh,
476 int index,
477 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_AAAA;
483 memcpy(&a->ip.v6, &doh[index], 16);
484 d->numaddr++;
485 }
486 return DOH_OK;
487 }
488
store_cname(const unsigned char * doh,size_t dohlen,unsigned int index,struct dohentry * d)489 static DOHcode store_cname(const unsigned char *doh,
490 size_t dohlen,
491 unsigned int index,
492 struct dohentry *d)
493 {
494 struct dynbuf *c;
495 unsigned int loop = 128; /* a valid DNS name can never loop this much */
496 unsigned char length;
497
498 if(d->numcname == DOH_MAX_CNAME)
499 return DOH_OK; /* skip! */
500
501 c = &d->cname[d->numcname++];
502 do {
503 if(index >= dohlen)
504 return DOH_DNS_OUT_OF_RANGE;
505 length = doh[index];
506 if((length & 0xc0) == 0xc0) {
507 int newpos;
508 /* name pointer, get the new offset (14 bits) */
509 if((index + 1) >= dohlen)
510 return DOH_DNS_OUT_OF_RANGE;
511
512 /* move to the new index */
513 newpos = (length & 0x3f) << 8 | doh[index + 1];
514 index = newpos;
515 continue;
516 }
517 else if(length & 0xc0)
518 return DOH_DNS_BAD_LABEL; /* bad input */
519 else
520 index++;
521
522 if(length) {
523 if(Curl_dyn_len(c)) {
524 if(Curl_dyn_addn(c, STRCONST(".")))
525 return DOH_OUT_OF_MEM;
526 }
527 if((index + length) > dohlen)
528 return DOH_DNS_BAD_LABEL;
529
530 if(Curl_dyn_addn(c, &doh[index], length))
531 return DOH_OUT_OF_MEM;
532 index += length;
533 }
534 } while(length && --loop);
535
536 if(!loop)
537 return DOH_DNS_LABEL_LOOP;
538 return DOH_OK;
539 }
540
rdata(const unsigned char * doh,size_t dohlen,unsigned short rdlength,unsigned short type,int index,struct dohentry * d)541 static DOHcode rdata(const unsigned char *doh,
542 size_t dohlen,
543 unsigned short rdlength,
544 unsigned short type,
545 int index,
546 struct dohentry *d)
547 {
548 /* RDATA
549 - A (TYPE 1): 4 bytes
550 - AAAA (TYPE 28): 16 bytes
551 - NS (TYPE 2): N bytes */
552 DOHcode rc;
553
554 switch(type) {
555 case DNS_TYPE_A:
556 if(rdlength != 4)
557 return DOH_DNS_RDATA_LEN;
558 rc = store_a(doh, index, d);
559 if(rc)
560 return rc;
561 break;
562 case DNS_TYPE_AAAA:
563 if(rdlength != 16)
564 return DOH_DNS_RDATA_LEN;
565 rc = store_aaaa(doh, index, d);
566 if(rc)
567 return rc;
568 break;
569 case DNS_TYPE_CNAME:
570 rc = store_cname(doh, dohlen, index, d);
571 if(rc)
572 return rc;
573 break;
574 case DNS_TYPE_DNAME:
575 /* explicit for clarity; just skip; rely on synthesized CNAME */
576 break;
577 default:
578 /* unsupported type, just skip it */
579 break;
580 }
581 return DOH_OK;
582 }
583
de_init(struct dohentry * de)584 UNITTEST void de_init(struct dohentry *de)
585 {
586 int i;
587 memset(de, 0, sizeof(*de));
588 de->ttl = INT_MAX;
589 for(i = 0; i < DOH_MAX_CNAME; i++)
590 Curl_dyn_init(&de->cname[i], DYN_DOH_CNAME);
591 }
592
593
doh_decode(const unsigned char * doh,size_t dohlen,DNStype dnstype,struct dohentry * d)594 UNITTEST DOHcode doh_decode(const unsigned char *doh,
595 size_t dohlen,
596 DNStype dnstype,
597 struct dohentry *d)
598 {
599 unsigned char rcode;
600 unsigned short qdcount;
601 unsigned short ancount;
602 unsigned short type = 0;
603 unsigned short rdlength;
604 unsigned short nscount;
605 unsigned short arcount;
606 unsigned int index = 12;
607 DOHcode rc;
608
609 if(dohlen < 12)
610 return DOH_TOO_SMALL_BUFFER; /* too small */
611 if(!doh || doh[0] || doh[1])
612 return DOH_DNS_BAD_ID; /* bad ID */
613 rcode = doh[3] & 0x0f;
614 if(rcode)
615 return DOH_DNS_BAD_RCODE; /* bad rcode */
616
617 qdcount = get16bit(doh, 4);
618 while(qdcount) {
619 rc = skipqname(doh, dohlen, &index);
620 if(rc)
621 return rc; /* bad qname */
622 if(dohlen < (index + 4))
623 return DOH_DNS_OUT_OF_RANGE;
624 index += 4; /* skip question's type and class */
625 qdcount--;
626 }
627
628 ancount = get16bit(doh, 6);
629 while(ancount) {
630 unsigned short class;
631 unsigned int ttl;
632
633 rc = skipqname(doh, dohlen, &index);
634 if(rc)
635 return rc; /* bad qname */
636
637 if(dohlen < (index + 2))
638 return DOH_DNS_OUT_OF_RANGE;
639
640 type = get16bit(doh, index);
641 if((type != DNS_TYPE_CNAME) /* may be synthesized from DNAME */
642 && (type != DNS_TYPE_DNAME) /* if present, accept and ignore */
643 && (type != dnstype))
644 /* Not the same type as was asked for nor CNAME nor DNAME */
645 return DOH_DNS_UNEXPECTED_TYPE;
646 index += 2;
647
648 if(dohlen < (index + 2))
649 return DOH_DNS_OUT_OF_RANGE;
650 class = get16bit(doh, index);
651 if(DNS_CLASS_IN != class)
652 return DOH_DNS_UNEXPECTED_CLASS; /* unsupported */
653 index += 2;
654
655 if(dohlen < (index + 4))
656 return DOH_DNS_OUT_OF_RANGE;
657
658 ttl = get32bit(doh, index);
659 if(ttl < d->ttl)
660 d->ttl = ttl;
661 index += 4;
662
663 if(dohlen < (index + 2))
664 return DOH_DNS_OUT_OF_RANGE;
665
666 rdlength = get16bit(doh, index);
667 index += 2;
668 if(dohlen < (index + rdlength))
669 return DOH_DNS_OUT_OF_RANGE;
670
671 rc = rdata(doh, dohlen, rdlength, type, index, d);
672 if(rc)
673 return rc; /* bad rdata */
674 index += rdlength;
675 ancount--;
676 }
677
678 nscount = get16bit(doh, 8);
679 while(nscount) {
680 rc = skipqname(doh, dohlen, &index);
681 if(rc)
682 return rc; /* bad qname */
683
684 if(dohlen < (index + 8))
685 return DOH_DNS_OUT_OF_RANGE;
686
687 index += 2 + 2 + 4; /* type, class and ttl */
688
689 if(dohlen < (index + 2))
690 return DOH_DNS_OUT_OF_RANGE;
691
692 rdlength = get16bit(doh, index);
693 index += 2;
694 if(dohlen < (index + rdlength))
695 return DOH_DNS_OUT_OF_RANGE;
696 index += rdlength;
697 nscount--;
698 }
699
700 arcount = get16bit(doh, 10);
701 while(arcount) {
702 rc = skipqname(doh, dohlen, &index);
703 if(rc)
704 return rc; /* bad qname */
705
706 if(dohlen < (index + 8))
707 return DOH_DNS_OUT_OF_RANGE;
708
709 index += 2 + 2 + 4; /* type, class and ttl */
710
711 if(dohlen < (index + 2))
712 return DOH_DNS_OUT_OF_RANGE;
713
714 rdlength = get16bit(doh, index);
715 index += 2;
716 if(dohlen < (index + rdlength))
717 return DOH_DNS_OUT_OF_RANGE;
718 index += rdlength;
719 arcount--;
720 }
721
722 if(index != dohlen)
723 return DOH_DNS_MALFORMAT; /* something is wrong */
724
725 if((type != DNS_TYPE_NS) && !d->numcname && !d->numaddr)
726 /* nothing stored! */
727 return DOH_NO_CONTENT;
728
729 return DOH_OK; /* ok */
730 }
731
732 #ifndef CURL_DISABLE_VERBOSE_STRINGS
showdoh(struct Curl_easy * data,const struct dohentry * d)733 static void showdoh(struct Curl_easy *data,
734 const struct dohentry *d)
735 {
736 int i;
737 infof(data, "TTL: %u seconds", d->ttl);
738 for(i = 0; i < d->numaddr; i++) {
739 const struct dohaddr *a = &d->addr[i];
740 if(a->type == DNS_TYPE_A) {
741 infof(data, "DoH A: %u.%u.%u.%u",
742 a->ip.v4[0], a->ip.v4[1],
743 a->ip.v4[2], a->ip.v4[3]);
744 }
745 else if(a->type == DNS_TYPE_AAAA) {
746 int j;
747 char buffer[128];
748 char *ptr;
749 size_t len;
750 msnprintf(buffer, 128, "DoH AAAA: ");
751 ptr = &buffer[10];
752 len = 118;
753 for(j = 0; j < 16; j += 2) {
754 size_t l;
755 msnprintf(ptr, len, "%s%02x%02x", j?":":"", d->addr[i].ip.v6[j],
756 d->addr[i].ip.v6[j + 1]);
757 l = strlen(ptr);
758 len -= l;
759 ptr += l;
760 }
761 infof(data, "%s", buffer);
762 }
763 }
764 for(i = 0; i < d->numcname; i++) {
765 infof(data, "CNAME: %s", Curl_dyn_ptr(&d->cname[i]));
766 }
767 }
768 #else
769 #define showdoh(x,y)
770 #endif
771
772 /*
773 * doh2ai()
774 *
775 * This function returns a pointer to the first element of a newly allocated
776 * Curl_addrinfo struct linked list filled with the data from a set of DoH
777 * lookups. Curl_addrinfo is meant to work like the addrinfo struct does for
778 * a IPv6 stack, but usable also for IPv4, all hosts and environments.
779 *
780 * The memory allocated by this function *MUST* be free'd later on calling
781 * Curl_freeaddrinfo(). For each successful call to this function there
782 * must be an associated call later to Curl_freeaddrinfo().
783 */
784
785 static struct Curl_addrinfo *
doh2ai(const struct dohentry * de,const char * hostname,int port)786 doh2ai(const struct dohentry *de, const char *hostname, int port)
787 {
788 struct Curl_addrinfo *ai;
789 struct Curl_addrinfo *prevai = NULL;
790 struct Curl_addrinfo *firstai = NULL;
791 struct sockaddr_in *addr;
792 #ifdef ENABLE_IPV6
793 struct sockaddr_in6 *addr6;
794 #endif
795 CURLcode result = CURLE_OK;
796 int i;
797 size_t hostlen = strlen(hostname) + 1; /* include null-terminator */
798
799 if(!de)
800 /* no input == no output! */
801 return NULL;
802
803 for(i = 0; i < de->numaddr; i++) {
804 size_t ss_size;
805 CURL_SA_FAMILY_T addrtype;
806 if(de->addr[i].type == DNS_TYPE_AAAA) {
807 #ifndef ENABLE_IPV6
808 /* we can't handle IPv6 addresses */
809 continue;
810 #else
811 ss_size = sizeof(struct sockaddr_in6);
812 addrtype = AF_INET6;
813 #endif
814 }
815 else {
816 ss_size = sizeof(struct sockaddr_in);
817 addrtype = AF_INET;
818 }
819
820 ai = calloc(1, sizeof(struct Curl_addrinfo) + ss_size + hostlen);
821 if(!ai) {
822 result = CURLE_OUT_OF_MEMORY;
823 break;
824 }
825 ai->ai_addr = (void *)((char *)ai + sizeof(struct Curl_addrinfo));
826 ai->ai_canonname = (void *)((char *)ai->ai_addr + ss_size);
827 memcpy(ai->ai_canonname, hostname, hostlen);
828
829 if(!firstai)
830 /* store the pointer we want to return from this function */
831 firstai = ai;
832
833 if(prevai)
834 /* make the previous entry point to this */
835 prevai->ai_next = ai;
836
837 ai->ai_family = addrtype;
838
839 /* we return all names as STREAM, so when using this address for TFTP
840 the type must be ignored and conn->socktype be used instead! */
841 ai->ai_socktype = SOCK_STREAM;
842
843 ai->ai_addrlen = (curl_socklen_t)ss_size;
844
845 /* leave the rest of the struct filled with zero */
846
847 switch(ai->ai_family) {
848 case AF_INET:
849 addr = (void *)ai->ai_addr; /* storage area for this info */
850 DEBUGASSERT(sizeof(struct in_addr) == sizeof(de->addr[i].ip.v4));
851 memcpy(&addr->sin_addr, &de->addr[i].ip.v4, sizeof(struct in_addr));
852 addr->sin_family = addrtype;
853 addr->sin_port = htons((unsigned short)port);
854 break;
855
856 #ifdef ENABLE_IPV6
857 case AF_INET6:
858 addr6 = (void *)ai->ai_addr; /* storage area for this info */
859 DEBUGASSERT(sizeof(struct in6_addr) == sizeof(de->addr[i].ip.v6));
860 memcpy(&addr6->sin6_addr, &de->addr[i].ip.v6, sizeof(struct in6_addr));
861 addr6->sin6_family = addrtype;
862 addr6->sin6_port = htons((unsigned short)port);
863 break;
864 #endif
865 }
866
867 prevai = ai;
868 }
869
870 if(result) {
871 Curl_freeaddrinfo(firstai);
872 firstai = NULL;
873 }
874
875 return firstai;
876 }
877
878 #ifndef CURL_DISABLE_VERBOSE_STRINGS
type2name(DNStype dnstype)879 static const char *type2name(DNStype dnstype)
880 {
881 return (dnstype == DNS_TYPE_A)?"A":"AAAA";
882 }
883 #endif
884
de_cleanup(struct dohentry * d)885 UNITTEST void de_cleanup(struct dohentry *d)
886 {
887 int i = 0;
888 for(i = 0; i < d->numcname; i++) {
889 Curl_dyn_free(&d->cname[i]);
890 }
891 }
892
Curl_doh_is_resolved(struct Curl_easy * data,struct Curl_dns_entry ** dnsp)893 CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
894 struct Curl_dns_entry **dnsp)
895 {
896 CURLcode result;
897 struct dohdata *dohp = data->req.doh;
898 *dnsp = NULL; /* defaults to no response */
899 if(!dohp)
900 return CURLE_OUT_OF_MEMORY;
901
902 if(!dohp->probe[DOH_PROBE_SLOT_IPADDR_V4].easy &&
903 !dohp->probe[DOH_PROBE_SLOT_IPADDR_V6].easy) {
904 failf(data, "Could not DoH-resolve: %s", data->state.async.hostname);
905 return CONN_IS_PROXIED(data->conn)?CURLE_COULDNT_RESOLVE_PROXY:
906 CURLE_COULDNT_RESOLVE_HOST;
907 }
908 else if(!dohp->pending) {
909 DOHcode rc[DOH_PROBE_SLOTS] = {
910 DOH_OK, DOH_OK
911 };
912 struct dohentry de;
913 int slot;
914 /* remove DoH handles from multi handle and close them */
915 for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) {
916 curl_multi_remove_handle(data->multi, dohp->probe[slot].easy);
917 Curl_close(&dohp->probe[slot].easy);
918 }
919 /* parse the responses, create the struct and return it! */
920 de_init(&de);
921 for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) {
922 struct dnsprobe *p = &dohp->probe[slot];
923 if(!p->dnstype)
924 continue;
925 rc[slot] = doh_decode(Curl_dyn_uptr(&p->serverdoh),
926 Curl_dyn_len(&p->serverdoh),
927 p->dnstype,
928 &de);
929 Curl_dyn_free(&p->serverdoh);
930 if(rc[slot]) {
931 infof(data, "DoH: %s type %s for %s", doh_strerror(rc[slot]),
932 type2name(p->dnstype), dohp->host);
933 }
934 } /* next slot */
935
936 result = CURLE_COULDNT_RESOLVE_HOST; /* until we know better */
937 if(!rc[DOH_PROBE_SLOT_IPADDR_V4] || !rc[DOH_PROBE_SLOT_IPADDR_V6]) {
938 /* we have an address, of one kind or other */
939 struct Curl_dns_entry *dns;
940 struct Curl_addrinfo *ai;
941
942 infof(data, "DoH Host name: %s", dohp->host);
943 showdoh(data, &de);
944
945 ai = doh2ai(&de, dohp->host, dohp->port);
946 if(!ai) {
947 de_cleanup(&de);
948 return CURLE_OUT_OF_MEMORY;
949 }
950
951 if(data->share)
952 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
953
954 /* we got a response, store it in the cache */
955 dns = Curl_cache_addr(data, ai, dohp->host, 0, dohp->port);
956
957 if(data->share)
958 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
959
960 if(!dns) {
961 /* returned failure, bail out nicely */
962 Curl_freeaddrinfo(ai);
963 }
964 else {
965 data->state.async.dns = dns;
966 *dnsp = dns;
967 result = CURLE_OK; /* address resolution OK */
968 }
969 } /* address processing done */
970
971 /* Now process any build-specific attributes retrieved from DNS */
972
973 /* All done */
974 de_cleanup(&de);
975 Curl_safefree(data->req.doh);
976 return result;
977
978 } /* !dohp->pending */
979
980 /* else wait for pending DoH transactions to complete */
981 return CURLE_OK;
982 }
983
984 #endif /* CURL_DISABLE_DOH */
985