VirtualBox

source: vbox/trunk/src/libs/curl-8.7.1/lib/doh.c@ 106165

最後變更 在這個檔案從106165是 104083,由 vboxsync 提交於 8 月 前

curl-8.7.1: Applied and adjusted our curl changes to 8.4.0. bugref:10639

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

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