1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 2018 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
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.
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.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 * SPDX-License-Identifier: curl
23 ***************************************************************************/
25 #include "curl_setup.h"
27 #ifndef CURL_DISABLE_DOH
30 #include "curl_addrinfo.h"
37 #include "curl_base64.h"
41 /* The last 3 #include files should be in this order */
42 #include "curl_printf.h"
43 #include "curl_memory.h"
46 #define DNS_CLASS_IN 0x01
48 #ifndef CURL_DISABLE_VERBOSE_STRINGS
49 static const char * const errors[]={
66 static const char *doh_strerror(DOHcode code)
68 if((code >= DOH_OK) && (code <= DOH_DNS_NAME_TOO_LONG))
70 return "bad error code";
76 UNITTEST DOHcode doh_encode(const char *host,
78 unsigned char *dnsp, /* buffer */
79 size_t len, /* buffer size */
80 size_t *olen) /* output length */
82 const size_t hostlen = strlen(host);
83 unsigned char *orig = dnsp;
84 const char *hostp = host;
86 /* The expected output length is 16 bytes more than the length of
87 * the QNAME-encoding of the host name.
89 * A valid DNS name may not contain a zero-length label, except at
90 * the end. For this reason, a name beginning with a dot, or
91 * containing a sequence of two or more consecutive dots, is invalid
92 * and cannot be encoded as a QNAME.
94 * If the host name ends with a trailing dot, the corresponding
95 * QNAME-encoding is one byte longer than the host name. If (as is
96 * also valid) the hostname is shortened by the omission of the
97 * trailing dot, then its QNAME-encoding will be two bytes longer
100 * Each [ label, dot ] pair is encoded as [ length, label ],
101 * preserving overall length. A final [ label ] without a dot is
102 * also encoded as [ length, label ], increasing overall length
103 * by one. The encoding is completed by appending a zero byte,
104 * representing the zero-length root label, again increasing
105 * the overall length by one.
109 DEBUGASSERT(hostlen);
110 expected_len = 12 + 1 + hostlen + 4;
111 if(host[hostlen-1]!='.')
114 if(expected_len > (256 + 16)) /* RFCs 1034, 1035 */
115 return DOH_DNS_NAME_TOO_LONG;
117 if(len < expected_len)
118 return DOH_TOO_SMALL_BUFFER;
120 *dnsp++ = 0; /* 16 bit id */
122 *dnsp++ = 0x01; /* |QR| Opcode |AA|TC|RD| Set the RD bit */
123 *dnsp++ = '\0'; /* |RA| Z | RCODE | */
125 *dnsp++ = 1; /* QDCOUNT (number of entries in the question section) */
127 *dnsp++ = '\0'; /* ANCOUNT */
129 *dnsp++ = '\0'; /* NSCOUNT */
131 *dnsp++ = '\0'; /* ARCOUNT */
133 /* encode each label and store it in the QNAME */
136 char *dot = strchr(hostp, '.');
138 labellen = dot - hostp;
140 labellen = strlen(hostp);
141 if((labellen > 63) || (!labellen)) {
142 /* label is too long or too short, error out */
144 return DOH_DNS_BAD_LABEL;
146 /* label is non-empty, process it */
147 *dnsp++ = (unsigned char)labellen;
148 memcpy(dnsp, hostp, labellen);
151 /* advance past dot, but only if there is one */
156 *dnsp++ = 0; /* append zero-length label for root */
158 /* There are assigned TYPE codes beyond 255: use range [1..65535] */
159 *dnsp++ = (unsigned char)(255 & (dnstype>>8)); /* upper 8 bit TYPE */
160 *dnsp++ = (unsigned char)(255 & dnstype); /* lower 8 bit TYPE */
162 *dnsp++ = '\0'; /* upper 8 bit CLASS */
163 *dnsp++ = DNS_CLASS_IN; /* IN - "the Internet" */
167 /* verify that our estimation of length is valid, since
168 * this has led to buffer overflows in this function */
169 DEBUGASSERT(*olen == expected_len);
174 doh_write_cb(const void *contents, size_t size, size_t nmemb, void *userp)
176 size_t realsize = size * nmemb;
177 struct dynbuf *mem = (struct dynbuf *)userp;
179 if(Curl_dyn_addn(mem, contents, realsize))
185 /* called from multi.c when this DoH transfer is complete */
186 static int doh_done(struct Curl_easy *doh, CURLcode result)
188 struct Curl_easy *data = doh->set.dohfor;
189 struct dohdata *dohp = data->req.doh;
190 /* so one of the DoH request done for the 'data' transfer is now complete! */
192 infof(data, "a DoH request is completed, %u to go", dohp->pending);
194 infof(data, "DoH request %s", curl_easy_strerror(result));
198 curl_slist_free_all(dohp->headers);
199 dohp->headers = NULL;
200 Curl_expire(data, 0, EXPIRE_RUN_NOW);
205 #define ERROR_CHECK_SETOPT(x,y) \
207 result = curl_easy_setopt(doh, x, y); \
209 result != CURLE_NOT_BUILT_IN && \
210 result != CURLE_UNKNOWN_OPTION) \
214 static CURLcode dohprobe(struct Curl_easy *data,
215 struct dnsprobe *p, DNStype dnstype,
217 const char *url, CURLM *multi,
218 struct curl_slist *headers)
220 struct Curl_easy *doh = NULL;
222 CURLcode result = CURLE_OK;
223 timediff_t timeout_ms;
224 DOHcode d = doh_encode(host, dnstype, p->dohbuffer, sizeof(p->dohbuffer),
227 failf(data, "Failed to encode DoH packet [%d]", d);
228 return CURLE_OUT_OF_MEMORY;
231 p->dnstype = dnstype;
232 Curl_dyn_init(&p->serverdoh, DYN_DOH_RESPONSE);
234 timeout_ms = Curl_timeleft(data, NULL, TRUE);
235 if(timeout_ms <= 0) {
236 result = CURLE_OPERATION_TIMEDOUT;
239 /* Curl_open() is the internal version of curl_easy_init() */
240 result = Curl_open(&doh);
242 /* pass in the struct pointer via a local variable to please coverity and
243 the gcc typecheck helpers */
244 struct dynbuf *resp = &p->serverdoh;
245 ERROR_CHECK_SETOPT(CURLOPT_URL, url);
246 ERROR_CHECK_SETOPT(CURLOPT_DEFAULT_PROTOCOL, "https");
247 ERROR_CHECK_SETOPT(CURLOPT_WRITEFUNCTION, doh_write_cb);
248 ERROR_CHECK_SETOPT(CURLOPT_WRITEDATA, resp);
249 ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDS, p->dohbuffer);
250 ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDSIZE, (long)p->dohlen);
251 ERROR_CHECK_SETOPT(CURLOPT_HTTPHEADER, headers);
253 ERROR_CHECK_SETOPT(CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS);
256 /* enforce HTTPS if not debug */
257 ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTPS);
259 /* in debug mode, also allow http */
260 ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTP|CURLPROTO_HTTPS);
262 ERROR_CHECK_SETOPT(CURLOPT_TIMEOUT_MS, (long)timeout_ms);
263 ERROR_CHECK_SETOPT(CURLOPT_SHARE, data->share);
264 if(data->set.err && data->set.err != stderr)
265 ERROR_CHECK_SETOPT(CURLOPT_STDERR, data->set.err);
266 if(data->set.verbose)
267 ERROR_CHECK_SETOPT(CURLOPT_VERBOSE, 1L);
268 if(data->set.no_signal)
269 ERROR_CHECK_SETOPT(CURLOPT_NOSIGNAL, 1L);
271 ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYHOST,
272 data->set.doh_verifyhost ? 2L : 0L);
273 ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYPEER,
274 data->set.doh_verifypeer ? 1L : 0L);
275 ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYSTATUS,
276 data->set.doh_verifystatus ? 1L : 0L);
278 /* Inherit *some* SSL options from the user's transfer. This is a
279 best-guess as to which options are needed for compatibility. #3661
281 Note DoH does not inherit the user's proxy server so proxy SSL settings
282 have no effect and are not inherited. If that changes then two new
283 options should be added to check doh proxy insecure separately,
284 CURLOPT_DOH_PROXY_SSL_VERIFYHOST and CURLOPT_DOH_PROXY_SSL_VERIFYPEER.
286 if(data->set.ssl.falsestart)
287 ERROR_CHECK_SETOPT(CURLOPT_SSL_FALSESTART, 1L);
288 if(data->set.str[STRING_SSL_CAFILE]) {
289 ERROR_CHECK_SETOPT(CURLOPT_CAINFO,
290 data->set.str[STRING_SSL_CAFILE]);
292 if(data->set.blobs[BLOB_CAINFO]) {
293 ERROR_CHECK_SETOPT(CURLOPT_CAINFO_BLOB,
294 data->set.blobs[BLOB_CAINFO]);
296 if(data->set.str[STRING_SSL_CAPATH]) {
297 ERROR_CHECK_SETOPT(CURLOPT_CAPATH,
298 data->set.str[STRING_SSL_CAPATH]);
300 if(data->set.str[STRING_SSL_CRLFILE]) {
301 ERROR_CHECK_SETOPT(CURLOPT_CRLFILE,
302 data->set.str[STRING_SSL_CRLFILE]);
304 if(data->set.ssl.certinfo)
305 ERROR_CHECK_SETOPT(CURLOPT_CERTINFO, 1L);
306 if(data->set.ssl.fsslctx)
307 ERROR_CHECK_SETOPT(CURLOPT_SSL_CTX_FUNCTION, data->set.ssl.fsslctx);
308 if(data->set.ssl.fsslctxp)
309 ERROR_CHECK_SETOPT(CURLOPT_SSL_CTX_DATA, data->set.ssl.fsslctxp);
310 if(data->set.str[STRING_SSL_EC_CURVES]) {
311 ERROR_CHECK_SETOPT(CURLOPT_SSL_EC_CURVES,
312 data->set.str[STRING_SSL_EC_CURVES]);
317 (data->set.ssl.enable_beast ?
318 CURLSSLOPT_ALLOW_BEAST : 0) |
319 (data->set.ssl.no_revoke ?
320 CURLSSLOPT_NO_REVOKE : 0) |
321 (data->set.ssl.no_partialchain ?
322 CURLSSLOPT_NO_PARTIALCHAIN : 0) |
323 (data->set.ssl.revoke_best_effort ?
324 CURLSSLOPT_REVOKE_BEST_EFFORT : 0) |
325 (data->set.ssl.native_ca_store ?
326 CURLSSLOPT_NATIVE_CA : 0) |
327 (data->set.ssl.auto_client_cert ?
328 CURLSSLOPT_AUTO_CLIENT_CERT : 0);
330 (void)curl_easy_setopt(doh, CURLOPT_SSL_OPTIONS, mask);
333 doh->set.fmultidone = doh_done;
334 doh->set.dohfor = data; /* identify for which transfer this is done */
337 /* DoH private_data must be null because the user must have a way to
338 distinguish their transfer's handle from DoH handles in user
339 callbacks (ie SSL CTX callback). */
340 DEBUGASSERT(!doh->set.private_data);
342 if(curl_multi_add_handle(multi, doh))
357 * Curl_doh() resolves a name using DoH. It resolves a name and returns a
358 * 'Curl_addrinfo *' with the address information.
361 struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
362 const char *hostname,
366 CURLcode result = CURLE_OK;
368 struct dohdata *dohp;
369 struct connectdata *conn = data->conn;
370 *waitp = TRUE; /* this never returns synchronously */
374 DEBUGASSERT(!data->req.doh);
377 /* start clean, consider allocating this struct on demand */
378 dohp = data->req.doh = calloc(sizeof(struct dohdata), 1);
382 conn->bits.doh = TRUE;
383 dohp->host = hostname;
386 curl_slist_append(NULL,
387 "Content-Type: application/dns-message");
391 /* create IPv4 DoH request */
392 result = dohprobe(data, &dohp->probe[DOH_PROBE_SLOT_IPADDR_V4],
393 DNS_TYPE_A, hostname, data->set.str[STRING_DOH],
394 data->multi, dohp->headers);
399 if((conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
400 /* create IPv6 DoH request */
401 result = dohprobe(data, &dohp->probe[DOH_PROBE_SLOT_IPADDR_V6],
402 DNS_TYPE_AAAA, hostname, data->set.str[STRING_DOH],
403 data->multi, dohp->headers);
411 curl_slist_free_all(dohp->headers);
412 data->req.doh->headers = NULL;
413 for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) {
414 Curl_close(&dohp->probe[slot].easy);
416 Curl_safefree(data->req.doh);
420 static DOHcode skipqname(const unsigned char *doh, size_t dohlen,
421 unsigned int *indexp)
423 unsigned char length;
425 if(dohlen < (*indexp + 1))
426 return DOH_DNS_OUT_OF_RANGE;
427 length = doh[*indexp];
428 if((length & 0xc0) == 0xc0) {
429 /* name pointer, advance over it and be done */
430 if(dohlen < (*indexp + 2))
431 return DOH_DNS_OUT_OF_RANGE;
436 return DOH_DNS_BAD_LABEL;
437 if(dohlen < (*indexp + 1 + length))
438 return DOH_DNS_OUT_OF_RANGE;
439 *indexp += 1 + length;
444 static unsigned short get16bit(const unsigned char *doh, int index)
446 return (unsigned short)((doh[index] << 8) | doh[index + 1]);
449 static unsigned int get32bit(const unsigned char *doh, int index)
451 /* make clang and gcc optimize this to bswap by incrementing
452 the pointer first. */
455 /* avoid undefined behavior by casting to unsigned before shifting
456 24 bits, possibly into the sign bit. codegen is same, but
457 ub sanitizer won't be upset */
458 return ( (unsigned)doh[0] << 24) | (doh[1] << 16) |(doh[2] << 8) | doh[3];
461 static DOHcode store_a(const unsigned char *doh, int index, struct dohentry *d)
463 /* silently ignore addresses over the limit */
464 if(d->numaddr < DOH_MAX_ADDR) {
465 struct dohaddr *a = &d->addr[d->numaddr];
466 a->type = DNS_TYPE_A;
467 memcpy(&a->ip.v4, &doh[index], 4);
473 static DOHcode store_aaaa(const unsigned char *doh,
477 /* silently ignore addresses over the limit */
478 if(d->numaddr < DOH_MAX_ADDR) {
479 struct dohaddr *a = &d->addr[d->numaddr];
480 a->type = DNS_TYPE_AAAA;
481 memcpy(&a->ip.v6, &doh[index], 16);
487 static DOHcode store_cname(const unsigned char *doh,
493 unsigned int loop = 128; /* a valid DNS name can never loop this much */
494 unsigned char length;
496 if(d->numcname == DOH_MAX_CNAME)
497 return DOH_OK; /* skip! */
499 c = &d->cname[d->numcname++];
502 return DOH_DNS_OUT_OF_RANGE;
504 if((length & 0xc0) == 0xc0) {
506 /* name pointer, get the new offset (14 bits) */
507 if((index + 1) >= dohlen)
508 return DOH_DNS_OUT_OF_RANGE;
510 /* move to the new index */
511 newpos = (length & 0x3f) << 8 | doh[index + 1];
515 else if(length & 0xc0)
516 return DOH_DNS_BAD_LABEL; /* bad input */
521 if(Curl_dyn_len(c)) {
522 if(Curl_dyn_addn(c, STRCONST(".")))
523 return DOH_OUT_OF_MEM;
525 if((index + length) > dohlen)
526 return DOH_DNS_BAD_LABEL;
528 if(Curl_dyn_addn(c, &doh[index], length))
529 return DOH_OUT_OF_MEM;
532 } while(length && --loop);
535 return DOH_DNS_LABEL_LOOP;
539 static DOHcode rdata(const unsigned char *doh,
541 unsigned short rdlength,
547 - A (TYPE 1): 4 bytes
548 - AAAA (TYPE 28): 16 bytes
549 - NS (TYPE 2): N bytes */
555 return DOH_DNS_RDATA_LEN;
556 rc = store_a(doh, index, d);
562 return DOH_DNS_RDATA_LEN;
563 rc = store_aaaa(doh, index, d);
568 rc = store_cname(doh, dohlen, index, d);
573 /* explicit for clarity; just skip; rely on synthesized CNAME */
576 /* unsupported type, just skip it */
582 UNITTEST void de_init(struct dohentry *de)
585 memset(de, 0, sizeof(*de));
587 for(i = 0; i < DOH_MAX_CNAME; i++)
588 Curl_dyn_init(&de->cname[i], DYN_DOH_CNAME);
592 UNITTEST DOHcode doh_decode(const unsigned char *doh,
598 unsigned short qdcount;
599 unsigned short ancount;
600 unsigned short type = 0;
601 unsigned short rdlength;
602 unsigned short nscount;
603 unsigned short arcount;
604 unsigned int index = 12;
608 return DOH_TOO_SMALL_BUFFER; /* too small */
609 if(!doh || doh[0] || doh[1])
610 return DOH_DNS_BAD_ID; /* bad ID */
611 rcode = doh[3] & 0x0f;
613 return DOH_DNS_BAD_RCODE; /* bad rcode */
615 qdcount = get16bit(doh, 4);
617 rc = skipqname(doh, dohlen, &index);
619 return rc; /* bad qname */
620 if(dohlen < (index + 4))
621 return DOH_DNS_OUT_OF_RANGE;
622 index += 4; /* skip question's type and class */
626 ancount = get16bit(doh, 6);
628 unsigned short class;
631 rc = skipqname(doh, dohlen, &index);
633 return rc; /* bad qname */
635 if(dohlen < (index + 2))
636 return DOH_DNS_OUT_OF_RANGE;
638 type = get16bit(doh, index);
639 if((type != DNS_TYPE_CNAME) /* may be synthesized from DNAME */
640 && (type != DNS_TYPE_DNAME) /* if present, accept and ignore */
641 && (type != dnstype))
642 /* Not the same type as was asked for nor CNAME nor DNAME */
643 return DOH_DNS_UNEXPECTED_TYPE;
646 if(dohlen < (index + 2))
647 return DOH_DNS_OUT_OF_RANGE;
648 class = get16bit(doh, index);
649 if(DNS_CLASS_IN != class)
650 return DOH_DNS_UNEXPECTED_CLASS; /* unsupported */
653 if(dohlen < (index + 4))
654 return DOH_DNS_OUT_OF_RANGE;
656 ttl = get32bit(doh, index);
661 if(dohlen < (index + 2))
662 return DOH_DNS_OUT_OF_RANGE;
664 rdlength = get16bit(doh, index);
666 if(dohlen < (index + rdlength))
667 return DOH_DNS_OUT_OF_RANGE;
669 rc = rdata(doh, dohlen, rdlength, type, index, d);
671 return rc; /* bad rdata */
676 nscount = get16bit(doh, 8);
678 rc = skipqname(doh, dohlen, &index);
680 return rc; /* bad qname */
682 if(dohlen < (index + 8))
683 return DOH_DNS_OUT_OF_RANGE;
685 index += 2 + 2 + 4; /* type, class and ttl */
687 if(dohlen < (index + 2))
688 return DOH_DNS_OUT_OF_RANGE;
690 rdlength = get16bit(doh, index);
692 if(dohlen < (index + rdlength))
693 return DOH_DNS_OUT_OF_RANGE;
698 arcount = get16bit(doh, 10);
700 rc = skipqname(doh, dohlen, &index);
702 return rc; /* bad qname */
704 if(dohlen < (index + 8))
705 return DOH_DNS_OUT_OF_RANGE;
707 index += 2 + 2 + 4; /* type, class and ttl */
709 if(dohlen < (index + 2))
710 return DOH_DNS_OUT_OF_RANGE;
712 rdlength = get16bit(doh, index);
714 if(dohlen < (index + rdlength))
715 return DOH_DNS_OUT_OF_RANGE;
721 return DOH_DNS_MALFORMAT; /* something is wrong */
723 if((type != DNS_TYPE_NS) && !d->numcname && !d->numaddr)
724 /* nothing stored! */
725 return DOH_NO_CONTENT;
727 return DOH_OK; /* ok */
730 #ifndef CURL_DISABLE_VERBOSE_STRINGS
731 static void showdoh(struct Curl_easy *data,
732 const struct dohentry *d)
735 infof(data, "TTL: %u seconds", d->ttl);
736 for(i = 0; i < d->numaddr; i++) {
737 const struct dohaddr *a = &d->addr[i];
738 if(a->type == DNS_TYPE_A) {
739 infof(data, "DoH A: %u.%u.%u.%u",
740 a->ip.v4[0], a->ip.v4[1],
741 a->ip.v4[2], a->ip.v4[3]);
743 else if(a->type == DNS_TYPE_AAAA) {
748 msnprintf(buffer, 128, "DoH AAAA: ");
751 for(j = 0; j < 16; j += 2) {
753 msnprintf(ptr, len, "%s%02x%02x", j?":":"", d->addr[i].ip.v6[j],
754 d->addr[i].ip.v6[j + 1]);
759 infof(data, "%s", buffer);
762 for(i = 0; i < d->numcname; i++) {
763 infof(data, "CNAME: %s", Curl_dyn_ptr(&d->cname[i]));
773 * This function returns a pointer to the first element of a newly allocated
774 * Curl_addrinfo struct linked list filled with the data from a set of DoH
775 * lookups. Curl_addrinfo is meant to work like the addrinfo struct does for
776 * a IPv6 stack, but usable also for IPv4, all hosts and environments.
778 * The memory allocated by this function *MUST* be free'd later on calling
779 * Curl_freeaddrinfo(). For each successful call to this function there
780 * must be an associated call later to Curl_freeaddrinfo().
783 static struct Curl_addrinfo *
784 doh2ai(const struct dohentry *de, const char *hostname, int port)
786 struct Curl_addrinfo *ai;
787 struct Curl_addrinfo *prevai = NULL;
788 struct Curl_addrinfo *firstai = NULL;
789 struct sockaddr_in *addr;
791 struct sockaddr_in6 *addr6;
793 CURLcode result = CURLE_OK;
795 size_t hostlen = strlen(hostname) + 1; /* include null-terminator */
798 /* no input == no output! */
801 for(i = 0; i < de->numaddr; i++) {
803 CURL_SA_FAMILY_T addrtype;
804 if(de->addr[i].type == DNS_TYPE_AAAA) {
806 /* we can't handle IPv6 addresses */
809 ss_size = sizeof(struct sockaddr_in6);
814 ss_size = sizeof(struct sockaddr_in);
818 ai = calloc(1, sizeof(struct Curl_addrinfo) + ss_size + hostlen);
820 result = CURLE_OUT_OF_MEMORY;
823 ai->ai_addr = (void *)((char *)ai + sizeof(struct Curl_addrinfo));
824 ai->ai_canonname = (void *)((char *)ai->ai_addr + ss_size);
825 memcpy(ai->ai_canonname, hostname, hostlen);
828 /* store the pointer we want to return from this function */
832 /* make the previous entry point to this */
833 prevai->ai_next = ai;
835 ai->ai_family = addrtype;
837 /* we return all names as STREAM, so when using this address for TFTP
838 the type must be ignored and conn->socktype be used instead! */
839 ai->ai_socktype = SOCK_STREAM;
841 ai->ai_addrlen = (curl_socklen_t)ss_size;
843 /* leave the rest of the struct filled with zero */
845 switch(ai->ai_family) {
847 addr = (void *)ai->ai_addr; /* storage area for this info */
848 DEBUGASSERT(sizeof(struct in_addr) == sizeof(de->addr[i].ip.v4));
849 memcpy(&addr->sin_addr, &de->addr[i].ip.v4, sizeof(struct in_addr));
850 addr->sin_family = addrtype;
851 addr->sin_port = htons((unsigned short)port);
856 addr6 = (void *)ai->ai_addr; /* storage area for this info */
857 DEBUGASSERT(sizeof(struct in6_addr) == sizeof(de->addr[i].ip.v6));
858 memcpy(&addr6->sin6_addr, &de->addr[i].ip.v6, sizeof(struct in6_addr));
859 addr6->sin6_family = addrtype;
860 addr6->sin6_port = htons((unsigned short)port);
869 Curl_freeaddrinfo(firstai);
876 #ifndef CURL_DISABLE_VERBOSE_STRINGS
877 static const char *type2name(DNStype dnstype)
879 return (dnstype == DNS_TYPE_A)?"A":"AAAA";
883 UNITTEST void de_cleanup(struct dohentry *d)
886 for(i = 0; i < d->numcname; i++) {
887 Curl_dyn_free(&d->cname[i]);
891 CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
892 struct Curl_dns_entry **dnsp)
895 struct dohdata *dohp = data->req.doh;
896 *dnsp = NULL; /* defaults to no response */
898 return CURLE_OUT_OF_MEMORY;
900 if(!dohp->probe[DOH_PROBE_SLOT_IPADDR_V4].easy &&
901 !dohp->probe[DOH_PROBE_SLOT_IPADDR_V6].easy) {
902 failf(data, "Could not DoH-resolve: %s", data->state.async.hostname);
903 return CONN_IS_PROXIED(data->conn)?CURLE_COULDNT_RESOLVE_PROXY:
904 CURLE_COULDNT_RESOLVE_HOST;
906 else if(!dohp->pending) {
907 DOHcode rc[DOH_PROBE_SLOTS] = {
912 /* remove DoH handles from multi handle and close them */
913 for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) {
914 curl_multi_remove_handle(data->multi, dohp->probe[slot].easy);
915 Curl_close(&dohp->probe[slot].easy);
917 /* parse the responses, create the struct and return it! */
919 for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) {
920 struct dnsprobe *p = &dohp->probe[slot];
923 rc[slot] = doh_decode(Curl_dyn_uptr(&p->serverdoh),
924 Curl_dyn_len(&p->serverdoh),
927 Curl_dyn_free(&p->serverdoh);
929 infof(data, "DoH: %s type %s for %s", doh_strerror(rc[slot]),
930 type2name(p->dnstype), dohp->host);
934 result = CURLE_COULDNT_RESOLVE_HOST; /* until we know better */
935 if(!rc[DOH_PROBE_SLOT_IPADDR_V4] || !rc[DOH_PROBE_SLOT_IPADDR_V6]) {
936 /* we have an address, of one kind or other */
937 struct Curl_dns_entry *dns;
938 struct Curl_addrinfo *ai;
940 infof(data, "DoH Host name: %s", dohp->host);
943 ai = doh2ai(&de, dohp->host, dohp->port);
946 return CURLE_OUT_OF_MEMORY;
950 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
952 /* we got a response, store it in the cache */
953 dns = Curl_cache_addr(data, ai, dohp->host, dohp->port);
956 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
959 /* returned failure, bail out nicely */
960 Curl_freeaddrinfo(ai);
963 data->state.async.dns = dns;
965 result = CURLE_OK; /* address resolution OK */
967 } /* address processing done */
969 /* Now process any build-specific attributes retrieved from DNS */
973 Curl_safefree(data->req.doh);
976 } /* !dohp->pending */
978 /* else wait for pending DoH transactions to complete */
982 #endif /* CURL_DISABLE_DOH */