comparison mupdf-source/thirdparty/curl/lib/doh.c @ 2:b50eed0cc0ef upstream

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