VirtualBox

source: vbox/trunk/src/libs/curl-7.83.1/lib/doh.c@ 97122

最後變更 在這個檔案從97122是 95312,由 vboxsync 提交於 3 年 前

libs/{curl,libxml2}: OSE export fixes, bugref:8515

  • 屬性 svn:eol-style 設為 native
檔案大小: 28.0 KB
 
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 ***************************************************************************/
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
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 "Name too long"
62};
63
64static 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 */
80UNITTEST 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
177static size_t
178doh_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 */
190static int doh_done(struct Curl_easy *doh, CURLcode result)
191{
192 struct Curl_easy *data = doh->set.dohfor;
193 struct dohdata *dohp = data->req.doh;
194 /* so one of the DoH request done for the 'data' transfer is now complete! */
195 dohp->pending--;
196 infof(data, "a DoH request is completed, %u to go", dohp->pending);
197 if(result)
198 infof(data, "DoH request %s", curl_easy_strerror(result));
199
200 if(!dohp->pending) {
201 /* DoH completed */
202 curl_slist_free_all(dohp->headers);
203 dohp->headers = NULL;
204 Curl_expire(data, 0, EXPIRE_RUN_NOW);
205 }
206 return 0;
207}
208
209#define ERROR_CHECK_SETOPT(x,y) \
210do { \
211 result = curl_easy_setopt(doh, x, y); \
212 if(result && \
213 result != CURLE_NOT_BUILT_IN && \
214 result != CURLE_UNKNOWN_OPTION) \
215 goto error; \
216} while(0)
217
218static CURLcode dohprobe(struct Curl_easy *data,
219 struct dnsprobe *p, DNStype dnstype,
220 const char *host,
221 const char *url, CURLM *multi,
222 struct curl_slist *headers)
223{
224 struct Curl_easy *doh = NULL;
225 char *nurl = NULL;
226 CURLcode result = CURLE_OK;
227 timediff_t timeout_ms;
228 DOHcode d = doh_encode(host, dnstype, p->dohbuffer, sizeof(p->dohbuffer),
229 &p->dohlen);
230 if(d) {
231 failf(data, "Failed to encode DoH packet [%d]", d);
232 return CURLE_OUT_OF_MEMORY;
233 }
234
235 p->dnstype = dnstype;
236 Curl_dyn_init(&p->serverdoh, DYN_DOH_RESPONSE);
237
238 timeout_ms = Curl_timeleft(data, NULL, TRUE);
239 if(timeout_ms <= 0) {
240 result = CURLE_OPERATION_TIMEDOUT;
241 goto error;
242 }
243 /* Curl_open() is the internal version of curl_easy_init() */
244 result = Curl_open(&doh);
245 if(!result) {
246 /* pass in the struct pointer via a local variable to please coverity and
247 the gcc typecheck helpers */
248 struct dynbuf *resp = &p->serverdoh;
249 ERROR_CHECK_SETOPT(CURLOPT_URL, url);
250 ERROR_CHECK_SETOPT(CURLOPT_WRITEFUNCTION, doh_write_cb);
251 ERROR_CHECK_SETOPT(CURLOPT_WRITEDATA, resp);
252 ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDS, p->dohbuffer);
253 ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDSIZE, (long)p->dohlen);
254 ERROR_CHECK_SETOPT(CURLOPT_HTTPHEADER, headers);
255#ifdef USE_HTTP2
256 ERROR_CHECK_SETOPT(CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS);
257#endif
258#ifndef CURLDEBUG
259 /* enforce HTTPS if not debug */
260 ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTPS);
261#else
262 /* in debug mode, also allow http */
263 ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTP|CURLPROTO_HTTPS);
264#endif
265 ERROR_CHECK_SETOPT(CURLOPT_TIMEOUT_MS, (long)timeout_ms);
266 ERROR_CHECK_SETOPT(CURLOPT_SHARE, data->share);
267 if(data->set.err && data->set.err != stderr)
268 ERROR_CHECK_SETOPT(CURLOPT_STDERR, data->set.err);
269 if(data->set.verbose)
270 ERROR_CHECK_SETOPT(CURLOPT_VERBOSE, 1L);
271 if(data->set.no_signal)
272 ERROR_CHECK_SETOPT(CURLOPT_NOSIGNAL, 1L);
273
274 ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYHOST,
275 data->set.doh_verifyhost ? 2L : 0L);
276 ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYPEER,
277 data->set.doh_verifypeer ? 1L : 0L);
278 ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYSTATUS,
279 data->set.doh_verifystatus ? 1L : 0L);
280
281 /* Inherit *some* SSL options from the user's transfer. This is a
282 best-guess as to which options are needed for compatibility. #3661
283
284 Note DoH does not inherit the user's proxy server so proxy SSL settings
285 have no effect and are not inherited. If that changes then two new
286 options should be added to check doh proxy insecure separately,
287 CURLOPT_DOH_PROXY_SSL_VERIFYHOST and CURLOPT_DOH_PROXY_SSL_VERIFYPEER.
288 */
289 if(data->set.ssl.falsestart)
290 ERROR_CHECK_SETOPT(CURLOPT_SSL_FALSESTART, 1L);
291 if(data->set.str[STRING_SSL_CAFILE]) {
292 ERROR_CHECK_SETOPT(CURLOPT_CAINFO,
293 data->set.str[STRING_SSL_CAFILE]);
294 }
295 if(data->set.blobs[BLOB_CAINFO]) {
296 ERROR_CHECK_SETOPT(CURLOPT_CAINFO_BLOB,
297 data->set.blobs[BLOB_CAINFO]);
298 }
299 if(data->set.str[STRING_SSL_CAPATH]) {
300 ERROR_CHECK_SETOPT(CURLOPT_CAPATH,
301 data->set.str[STRING_SSL_CAPATH]);
302 }
303 if(data->set.str[STRING_SSL_CRLFILE]) {
304 ERROR_CHECK_SETOPT(CURLOPT_CRLFILE,
305 data->set.str[STRING_SSL_CRLFILE]);
306 }
307 if(data->set.ssl.certinfo)
308 ERROR_CHECK_SETOPT(CURLOPT_CERTINFO, 1L);
309 if(data->set.str[STRING_SSL_RANDOM_FILE]) {
310 ERROR_CHECK_SETOPT(CURLOPT_RANDOM_FILE,
311 data->set.str[STRING_SSL_RANDOM_FILE]);
312 }
313 if(data->set.str[STRING_SSL_EGDSOCKET]) {
314 ERROR_CHECK_SETOPT(CURLOPT_EGDSOCKET,
315 data->set.str[STRING_SSL_EGDSOCKET]);
316 }
317 if(data->set.ssl.fsslctx)
318 ERROR_CHECK_SETOPT(CURLOPT_SSL_CTX_FUNCTION, data->set.ssl.fsslctx);
319 if(data->set.ssl.fsslctxp)
320 ERROR_CHECK_SETOPT(CURLOPT_SSL_CTX_DATA, data->set.ssl.fsslctxp);
321 if(data->set.str[STRING_SSL_EC_CURVES]) {
322 ERROR_CHECK_SETOPT(CURLOPT_SSL_EC_CURVES,
323 data->set.str[STRING_SSL_EC_CURVES]);
324 }
325
326 {
327 long mask =
328 (data->set.ssl.enable_beast ?
329 CURLSSLOPT_ALLOW_BEAST : 0) |
330 (data->set.ssl.no_revoke ?
331 CURLSSLOPT_NO_REVOKE : 0) |
332 (data->set.ssl.no_partialchain ?
333 CURLSSLOPT_NO_PARTIALCHAIN : 0) |
334 (data->set.ssl.revoke_best_effort ?
335 CURLSSLOPT_REVOKE_BEST_EFFORT : 0) |
336 (data->set.ssl.native_ca_store ?
337 CURLSSLOPT_NATIVE_CA : 0) |
338 (data->set.ssl.auto_client_cert ?
339 CURLSSLOPT_AUTO_CLIENT_CERT : 0);
340
341 (void)curl_easy_setopt(doh, CURLOPT_SSL_OPTIONS, mask);
342 }
343
344 doh->set.fmultidone = doh_done;
345 doh->set.dohfor = data; /* identify for which transfer this is done */
346 p->easy = doh;
347
348 /* DoH private_data must be null because the user must have a way to
349 distinguish their transfer's handle from DoH handles in user
350 callbacks (ie SSL CTX callback). */
351 DEBUGASSERT(!doh->set.private_data);
352
353 if(curl_multi_add_handle(multi, doh))
354 goto error;
355 }
356 else
357 goto error;
358 free(nurl);
359 return CURLE_OK;
360
361 error:
362 free(nurl);
363 Curl_close(&doh);
364 return result;
365}
366
367/*
368 * Curl_doh() resolves a name using DoH. It resolves a name and returns a
369 * 'Curl_addrinfo *' with the address information.
370 */
371
372struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
373 const char *hostname,
374 int port,
375 int *waitp)
376{
377 CURLcode result = CURLE_OK;
378 int slot;
379 struct dohdata *dohp;
380 struct connectdata *conn = data->conn;
381 *waitp = TRUE; /* this never returns synchronously */
382 (void)hostname;
383 (void)port;
384
385 DEBUGASSERT(!data->req.doh);
386 DEBUGASSERT(conn);
387
388 /* start clean, consider allocating this struct on demand */
389 dohp = data->req.doh = calloc(sizeof(struct dohdata), 1);
390 if(!dohp)
391 return NULL;
392
393 conn->bits.doh = TRUE;
394 dohp->host = hostname;
395 dohp->port = port;
396 dohp->headers =
397 curl_slist_append(NULL,
398 "Content-Type: application/dns-message");
399 if(!dohp->headers)
400 goto error;
401
402 /* create IPv4 DoH request */
403 result = dohprobe(data, &dohp->probe[DOH_PROBE_SLOT_IPADDR_V4],
404 DNS_TYPE_A, hostname, data->set.str[STRING_DOH],
405 data->multi, dohp->headers);
406 if(result)
407 goto error;
408 dohp->pending++;
409
410 if(Curl_ipv6works(data)) {
411 /* create IPv6 DoH request */
412 result = dohprobe(data, &dohp->probe[DOH_PROBE_SLOT_IPADDR_V6],
413 DNS_TYPE_AAAA, hostname, data->set.str[STRING_DOH],
414 data->multi, dohp->headers);
415 if(result)
416 goto error;
417 dohp->pending++;
418 }
419 return NULL;
420
421 error:
422 curl_slist_free_all(dohp->headers);
423 data->req.doh->headers = NULL;
424 for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) {
425 Curl_close(&dohp->probe[slot].easy);
426 }
427 Curl_safefree(data->req.doh);
428 return NULL;
429}
430
431static DOHcode skipqname(const unsigned char *doh, size_t dohlen,
432 unsigned int *indexp)
433{
434 unsigned char length;
435 do {
436 if(dohlen < (*indexp + 1))
437 return DOH_DNS_OUT_OF_RANGE;
438 length = doh[*indexp];
439 if((length & 0xc0) == 0xc0) {
440 /* name pointer, advance over it and be done */
441 if(dohlen < (*indexp + 2))
442 return DOH_DNS_OUT_OF_RANGE;
443 *indexp += 2;
444 break;
445 }
446 if(length & 0xc0)
447 return DOH_DNS_BAD_LABEL;
448 if(dohlen < (*indexp + 1 + length))
449 return DOH_DNS_OUT_OF_RANGE;
450 *indexp += 1 + length;
451 } while(length);
452 return DOH_OK;
453}
454
455static unsigned short get16bit(const unsigned char *doh, int index)
456{
457 return (unsigned short)((doh[index] << 8) | doh[index + 1]);
458}
459
460static unsigned int get32bit(const unsigned char *doh, int index)
461{
462 /* make clang and gcc optimize this to bswap by incrementing
463 the pointer first. */
464 doh += index;
465
466 /* avoid undefined behavior by casting to unsigned before shifting
467 24 bits, possibly into the sign bit. codegen is same, but
468 ub sanitizer won't be upset */
469 return ( (unsigned)doh[0] << 24) | (doh[1] << 16) |(doh[2] << 8) | doh[3];
470}
471
472static DOHcode store_a(const unsigned char *doh, int index, struct dohentry *d)
473{
474 /* silently ignore addresses over the limit */
475 if(d->numaddr < DOH_MAX_ADDR) {
476 struct dohaddr *a = &d->addr[d->numaddr];
477 a->type = DNS_TYPE_A;
478 memcpy(&a->ip.v4, &doh[index], 4);
479 d->numaddr++;
480 }
481 return DOH_OK;
482}
483
484static DOHcode store_aaaa(const unsigned char *doh,
485 int index,
486 struct dohentry *d)
487{
488 /* silently ignore addresses over the limit */
489 if(d->numaddr < DOH_MAX_ADDR) {
490 struct dohaddr *a = &d->addr[d->numaddr];
491 a->type = DNS_TYPE_AAAA;
492 memcpy(&a->ip.v6, &doh[index], 16);
493 d->numaddr++;
494 }
495 return DOH_OK;
496}
497
498static DOHcode store_cname(const unsigned char *doh,
499 size_t dohlen,
500 unsigned int index,
501 struct dohentry *d)
502{
503 struct dynbuf *c;
504 unsigned int loop = 128; /* a valid DNS name can never loop this much */
505 unsigned char length;
506
507 if(d->numcname == DOH_MAX_CNAME)
508 return DOH_OK; /* skip! */
509
510 c = &d->cname[d->numcname++];
511 do {
512 if(index >= dohlen)
513 return DOH_DNS_OUT_OF_RANGE;
514 length = doh[index];
515 if((length & 0xc0) == 0xc0) {
516 int newpos;
517 /* name pointer, get the new offset (14 bits) */
518 if((index + 1) >= dohlen)
519 return DOH_DNS_OUT_OF_RANGE;
520
521 /* move to the new index */
522 newpos = (length & 0x3f) << 8 | doh[index + 1];
523 index = newpos;
524 continue;
525 }
526 else if(length & 0xc0)
527 return DOH_DNS_BAD_LABEL; /* bad input */
528 else
529 index++;
530
531 if(length) {
532 if(Curl_dyn_len(c)) {
533 if(Curl_dyn_addn(c, STRCONST(".")))
534 return DOH_OUT_OF_MEM;
535 }
536 if((index + length) > dohlen)
537 return DOH_DNS_BAD_LABEL;
538
539 if(Curl_dyn_addn(c, &doh[index], length))
540 return DOH_OUT_OF_MEM;
541 index += length;
542 }
543 } while(length && --loop);
544
545 if(!loop)
546 return DOH_DNS_LABEL_LOOP;
547 return DOH_OK;
548}
549
550static DOHcode rdata(const unsigned char *doh,
551 size_t dohlen,
552 unsigned short rdlength,
553 unsigned short type,
554 int index,
555 struct dohentry *d)
556{
557 /* RDATA
558 - A (TYPE 1): 4 bytes
559 - AAAA (TYPE 28): 16 bytes
560 - NS (TYPE 2): N bytes */
561 DOHcode rc;
562
563 switch(type) {
564 case DNS_TYPE_A:
565 if(rdlength != 4)
566 return DOH_DNS_RDATA_LEN;
567 rc = store_a(doh, index, d);
568 if(rc)
569 return rc;
570 break;
571 case DNS_TYPE_AAAA:
572 if(rdlength != 16)
573 return DOH_DNS_RDATA_LEN;
574 rc = store_aaaa(doh, index, d);
575 if(rc)
576 return rc;
577 break;
578 case DNS_TYPE_CNAME:
579 rc = store_cname(doh, dohlen, index, d);
580 if(rc)
581 return rc;
582 break;
583 case DNS_TYPE_DNAME:
584 /* explicit for clarity; just skip; rely on synthesized CNAME */
585 break;
586 default:
587 /* unsupported type, just skip it */
588 break;
589 }
590 return DOH_OK;
591}
592
593UNITTEST void de_init(struct dohentry *de)
594{
595 int i;
596 memset(de, 0, sizeof(*de));
597 de->ttl = INT_MAX;
598 for(i = 0; i < DOH_MAX_CNAME; i++)
599 Curl_dyn_init(&de->cname[i], DYN_DOH_CNAME);
600}
601
602
603UNITTEST DOHcode doh_decode(const unsigned char *doh,
604 size_t dohlen,
605 DNStype dnstype,
606 struct dohentry *d)
607{
608 unsigned char rcode;
609 unsigned short qdcount;
610 unsigned short ancount;
611 unsigned short type = 0;
612 unsigned short rdlength;
613 unsigned short nscount;
614 unsigned short arcount;
615 unsigned int index = 12;
616 DOHcode rc;
617
618 if(dohlen < 12)
619 return DOH_TOO_SMALL_BUFFER; /* too small */
620 if(!doh || doh[0] || doh[1])
621 return DOH_DNS_BAD_ID; /* bad ID */
622 rcode = doh[3] & 0x0f;
623 if(rcode)
624 return DOH_DNS_BAD_RCODE; /* bad rcode */
625
626 qdcount = get16bit(doh, 4);
627 while(qdcount) {
628 rc = skipqname(doh, dohlen, &index);
629 if(rc)
630 return rc; /* bad qname */
631 if(dohlen < (index + 4))
632 return DOH_DNS_OUT_OF_RANGE;
633 index += 4; /* skip question's type and class */
634 qdcount--;
635 }
636
637 ancount = get16bit(doh, 6);
638 while(ancount) {
639 unsigned short class;
640 unsigned int ttl;
641
642 rc = skipqname(doh, dohlen, &index);
643 if(rc)
644 return rc; /* bad qname */
645
646 if(dohlen < (index + 2))
647 return DOH_DNS_OUT_OF_RANGE;
648
649 type = get16bit(doh, index);
650 if((type != DNS_TYPE_CNAME) /* may be synthesized from DNAME */
651 && (type != DNS_TYPE_DNAME) /* if present, accept and ignore */
652 && (type != dnstype))
653 /* Not the same type as was asked for nor CNAME nor DNAME */
654 return DOH_DNS_UNEXPECTED_TYPE;
655 index += 2;
656
657 if(dohlen < (index + 2))
658 return DOH_DNS_OUT_OF_RANGE;
659 class = get16bit(doh, index);
660 if(DNS_CLASS_IN != class)
661 return DOH_DNS_UNEXPECTED_CLASS; /* unsupported */
662 index += 2;
663
664 if(dohlen < (index + 4))
665 return DOH_DNS_OUT_OF_RANGE;
666
667 ttl = get32bit(doh, index);
668 if(ttl < d->ttl)
669 d->ttl = ttl;
670 index += 4;
671
672 if(dohlen < (index + 2))
673 return DOH_DNS_OUT_OF_RANGE;
674
675 rdlength = get16bit(doh, index);
676 index += 2;
677 if(dohlen < (index + rdlength))
678 return DOH_DNS_OUT_OF_RANGE;
679
680 rc = rdata(doh, dohlen, rdlength, type, index, d);
681 if(rc)
682 return rc; /* bad rdata */
683 index += rdlength;
684 ancount--;
685 }
686
687 nscount = get16bit(doh, 8);
688 while(nscount) {
689 rc = skipqname(doh, dohlen, &index);
690 if(rc)
691 return rc; /* bad qname */
692
693 if(dohlen < (index + 8))
694 return DOH_DNS_OUT_OF_RANGE;
695
696 index += 2 + 2 + 4; /* type, class and ttl */
697
698 if(dohlen < (index + 2))
699 return DOH_DNS_OUT_OF_RANGE;
700
701 rdlength = get16bit(doh, index);
702 index += 2;
703 if(dohlen < (index + rdlength))
704 return DOH_DNS_OUT_OF_RANGE;
705 index += rdlength;
706 nscount--;
707 }
708
709 arcount = get16bit(doh, 10);
710 while(arcount) {
711 rc = skipqname(doh, dohlen, &index);
712 if(rc)
713 return rc; /* bad qname */
714
715 if(dohlen < (index + 8))
716 return DOH_DNS_OUT_OF_RANGE;
717
718 index += 2 + 2 + 4; /* type, class and ttl */
719
720 if(dohlen < (index + 2))
721 return DOH_DNS_OUT_OF_RANGE;
722
723 rdlength = get16bit(doh, index);
724 index += 2;
725 if(dohlen < (index + rdlength))
726 return DOH_DNS_OUT_OF_RANGE;
727 index += rdlength;
728 arcount--;
729 }
730
731 if(index != dohlen)
732 return DOH_DNS_MALFORMAT; /* something is wrong */
733
734 if((type != DNS_TYPE_NS) && !d->numcname && !d->numaddr)
735 /* nothing stored! */
736 return DOH_NO_CONTENT;
737
738 return DOH_OK; /* ok */
739}
740
741#ifndef CURL_DISABLE_VERBOSE_STRINGS
742static void showdoh(struct Curl_easy *data,
743 const struct dohentry *d)
744{
745 int i;
746 infof(data, "TTL: %u seconds", d->ttl);
747 for(i = 0; i < d->numaddr; i++) {
748 const struct dohaddr *a = &d->addr[i];
749 if(a->type == DNS_TYPE_A) {
750 infof(data, "DoH A: %u.%u.%u.%u",
751 a->ip.v4[0], a->ip.v4[1],
752 a->ip.v4[2], a->ip.v4[3]);
753 }
754 else if(a->type == DNS_TYPE_AAAA) {
755 int j;
756 char buffer[128];
757 char *ptr;
758 size_t len;
759 msnprintf(buffer, 128, "DoH AAAA: ");
760 ptr = &buffer[10];
761 len = 118;
762 for(j = 0; j < 16; j += 2) {
763 size_t l;
764 msnprintf(ptr, len, "%s%02x%02x", j?":":"", d->addr[i].ip.v6[j],
765 d->addr[i].ip.v6[j + 1]);
766 l = strlen(ptr);
767 len -= l;
768 ptr += l;
769 }
770 infof(data, "%s", buffer);
771 }
772 }
773 for(i = 0; i < d->numcname; i++) {
774 infof(data, "CNAME: %s", Curl_dyn_ptr(&d->cname[i]));
775 }
776}
777#else
778#define showdoh(x,y)
779#endif
780
781/*
782 * doh2ai()
783 *
784 * This function returns a pointer to the first element of a newly allocated
785 * Curl_addrinfo struct linked list filled with the data from a set of DoH
786 * lookups. Curl_addrinfo is meant to work like the addrinfo struct does for
787 * a IPv6 stack, but usable also for IPv4, all hosts and environments.
788 *
789 * The memory allocated by this function *MUST* be free'd later on calling
790 * Curl_freeaddrinfo(). For each successful call to this function there
791 * must be an associated call later to Curl_freeaddrinfo().
792 */
793
794static struct Curl_addrinfo *
795doh2ai(const struct dohentry *de, const char *hostname, int port)
796{
797 struct Curl_addrinfo *ai;
798 struct Curl_addrinfo *prevai = NULL;
799 struct Curl_addrinfo *firstai = NULL;
800 struct sockaddr_in *addr;
801#ifdef ENABLE_IPV6
802 struct sockaddr_in6 *addr6;
803#endif
804 CURLcode result = CURLE_OK;
805 int i;
806 size_t hostlen = strlen(hostname) + 1; /* include zero terminator */
807
808 if(!de)
809 /* no input == no output! */
810 return NULL;
811
812 for(i = 0; i < de->numaddr; i++) {
813 size_t ss_size;
814 CURL_SA_FAMILY_T addrtype;
815 if(de->addr[i].type == DNS_TYPE_AAAA) {
816#ifndef ENABLE_IPV6
817 /* we can't handle IPv6 addresses */
818 continue;
819#else
820 ss_size = sizeof(struct sockaddr_in6);
821 addrtype = AF_INET6;
822#endif
823 }
824 else {
825 ss_size = sizeof(struct sockaddr_in);
826 addrtype = AF_INET;
827 }
828
829 ai = calloc(1, sizeof(struct Curl_addrinfo) + ss_size + hostlen);
830 if(!ai) {
831 result = CURLE_OUT_OF_MEMORY;
832 break;
833 }
834 ai->ai_addr = (void *)((char *)ai + sizeof(struct Curl_addrinfo));
835 ai->ai_canonname = (void *)((char *)ai->ai_addr + ss_size);
836 memcpy(ai->ai_canonname, hostname, hostlen);
837
838 if(!firstai)
839 /* store the pointer we want to return from this function */
840 firstai = ai;
841
842 if(prevai)
843 /* make the previous entry point to this */
844 prevai->ai_next = ai;
845
846 ai->ai_family = addrtype;
847
848 /* we return all names as STREAM, so when using this address for TFTP
849 the type must be ignored and conn->socktype be used instead! */
850 ai->ai_socktype = SOCK_STREAM;
851
852 ai->ai_addrlen = (curl_socklen_t)ss_size;
853
854 /* leave the rest of the struct filled with zero */
855
856 switch(ai->ai_family) {
857 case AF_INET:
858 addr = (void *)ai->ai_addr; /* storage area for this info */
859 DEBUGASSERT(sizeof(struct in_addr) == sizeof(de->addr[i].ip.v4));
860 memcpy(&addr->sin_addr, &de->addr[i].ip.v4, sizeof(struct in_addr));
861 addr->sin_family = addrtype;
862 addr->sin_port = htons((unsigned short)port);
863 break;
864
865#ifdef ENABLE_IPV6
866 case AF_INET6:
867 addr6 = (void *)ai->ai_addr; /* storage area for this info */
868 DEBUGASSERT(sizeof(struct in6_addr) == sizeof(de->addr[i].ip.v6));
869 memcpy(&addr6->sin6_addr, &de->addr[i].ip.v6, sizeof(struct in6_addr));
870 addr6->sin6_family = addrtype;
871 addr6->sin6_port = htons((unsigned short)port);
872 break;
873#endif
874 }
875
876 prevai = ai;
877 }
878
879 if(result) {
880 Curl_freeaddrinfo(firstai);
881 firstai = NULL;
882 }
883
884 return firstai;
885}
886
887#ifndef CURL_DISABLE_VERBOSE_STRINGS
888static const char *type2name(DNStype dnstype)
889{
890 return (dnstype == DNS_TYPE_A)?"A":"AAAA";
891}
892#endif
893
894UNITTEST void de_cleanup(struct dohentry *d)
895{
896 int i = 0;
897 for(i = 0; i < d->numcname; i++) {
898 Curl_dyn_free(&d->cname[i]);
899 }
900}
901
902CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
903 struct Curl_dns_entry **dnsp)
904{
905 CURLcode result;
906 struct dohdata *dohp = data->req.doh;
907 *dnsp = NULL; /* defaults to no response */
908 if(!dohp)
909 return CURLE_OUT_OF_MEMORY;
910
911 if(!dohp->probe[DOH_PROBE_SLOT_IPADDR_V4].easy &&
912 !dohp->probe[DOH_PROBE_SLOT_IPADDR_V6].easy) {
913 failf(data, "Could not DoH-resolve: %s", data->state.async.hostname);
914 return CONN_IS_PROXIED(data->conn)?CURLE_COULDNT_RESOLVE_PROXY:
915 CURLE_COULDNT_RESOLVE_HOST;
916 }
917 else if(!dohp->pending) {
918 DOHcode rc[DOH_PROBE_SLOTS] = {
919 DOH_OK, DOH_OK
920 };
921 struct dohentry de;
922 int slot;
923 /* remove DoH handles from multi handle and close them */
924 for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) {
925 curl_multi_remove_handle(data->multi, dohp->probe[slot].easy);
926 Curl_close(&dohp->probe[slot].easy);
927 }
928 /* parse the responses, create the struct and return it! */
929 de_init(&de);
930 for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) {
931 struct dnsprobe *p = &dohp->probe[slot];
932 if(!p->dnstype)
933 continue;
934 rc[slot] = doh_decode(Curl_dyn_uptr(&p->serverdoh),
935 Curl_dyn_len(&p->serverdoh),
936 p->dnstype,
937 &de);
938 Curl_dyn_free(&p->serverdoh);
939 if(rc[slot]) {
940 infof(data, "DoH: %s type %s for %s", doh_strerror(rc[slot]),
941 type2name(p->dnstype), dohp->host);
942 }
943 } /* next slot */
944
945 result = CURLE_COULDNT_RESOLVE_HOST; /* until we know better */
946 if(!rc[DOH_PROBE_SLOT_IPADDR_V4] || !rc[DOH_PROBE_SLOT_IPADDR_V6]) {
947 /* we have an address, of one kind or other */
948 struct Curl_dns_entry *dns;
949 struct Curl_addrinfo *ai;
950
951 infof(data, "DoH Host name: %s", dohp->host);
952 showdoh(data, &de);
953
954 ai = doh2ai(&de, dohp->host, dohp->port);
955 if(!ai) {
956 de_cleanup(&de);
957 return CURLE_OUT_OF_MEMORY;
958 }
959
960 if(data->share)
961 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
962
963 /* we got a response, store it in the cache */
964 dns = Curl_cache_addr(data, ai, dohp->host, dohp->port);
965
966 if(data->share)
967 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
968
969 if(!dns) {
970 /* returned failure, bail out nicely */
971 Curl_freeaddrinfo(ai);
972 }
973 else {
974 data->state.async.dns = dns;
975 *dnsp = dns;
976 result = CURLE_OK; /* address resolution OK */
977 }
978 } /* address processing done */
979
980 /* Now process any build-specific attributes retrieved from DNS */
981
982 /* All done */
983 de_cleanup(&de);
984 Curl_safefree(data->req.doh);
985 return result;
986
987 } /* !dohp->pending */
988
989 /* else wait for pending DoH transactions to complete */
990 return CURLE_OK;
991}
992
993#endif /* CURL_DISABLE_DOH */
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette