VirtualBox

source: vbox/trunk/src/libs/curl-7.64.0/lib/doh.c@ 86643

Last change on this file since 86643 was 85671, checked in by vboxsync, 4 years ago

Export out internal curl copy to make it a lot simpler to build VBox (OSE) on Windows. bugref:9814

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

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