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 ***************************************************************************/
23 #include "curl_setup.h"
25 #ifndef CURL_DISABLE_DOH
28 #include "curl_addrinfo.h"
35 #include "curl_base64.h"
39 /* The last 3 #include files should be in this order */
40 #include "curl_printf.h"
41 #include "curl_memory.h"
44 #define DNS_CLASS_IN 0x01
46 #ifndef CURL_DISABLE_VERBOSE_STRINGS
47 static const char * const errors[]={
64 static const char *doh_strerror(DOHcode code)
66 if((code >= DOH_OK) && (code <= DOH_DNS_NAME_TOO_LONG))
68 return "bad error code";
75 #define UNITTEST static
80 UNITTEST DOHcode doh_encode(const char *host,
82 unsigned char *dnsp, /* buffer */
83 size_t len, /* buffer size */
84 size_t *olen) /* output length */
86 const size_t hostlen = strlen(host);
87 unsigned char *orig = dnsp;
88 const char *hostp = host;
90 /* The expected output length is 16 bytes more than the length of
91 * the QNAME-encoding of the host name.
93 * A valid DNS name may not contain a zero-length label, except at
94 * the end. For this reason, a name beginning with a dot, or
95 * containing a sequence of two or more consecutive dots, is invalid
96 * and cannot be encoded as a QNAME.
98 * If the host name ends with a trailing dot, the corresponding
99 * QNAME-encoding is one byte longer than the host name. If (as is
100 * also valid) the hostname is shortened by the omission of the
101 * trailing dot, then its QNAME-encoding will be two bytes longer
102 * than the host name.
104 * Each [ label, dot ] pair is encoded as [ length, label ],
105 * preserving overall length. A final [ label ] without a dot is
106 * also encoded as [ length, label ], increasing overall length
107 * by one. The encoding is completed by appending a zero byte,
108 * representing the zero-length root label, again increasing
109 * the overall length by one.
113 DEBUGASSERT(hostlen);
114 expected_len = 12 + 1 + hostlen + 4;
115 if(host[hostlen-1]!='.')
118 if(expected_len > (256 + 16)) /* RFCs 1034, 1035 */
119 return DOH_DNS_NAME_TOO_LONG;
121 if(len < expected_len)
122 return DOH_TOO_SMALL_BUFFER;
124 *dnsp++ = 0; /* 16 bit id */
126 *dnsp++ = 0x01; /* |QR| Opcode |AA|TC|RD| Set the RD bit */
127 *dnsp++ = '\0'; /* |RA| Z | RCODE | */
129 *dnsp++ = 1; /* QDCOUNT (number of entries in the question section) */
131 *dnsp++ = '\0'; /* ANCOUNT */
133 *dnsp++ = '\0'; /* NSCOUNT */
135 *dnsp++ = '\0'; /* ARCOUNT */
137 /* encode each label and store it in the QNAME */
140 char *dot = strchr(hostp, '.');
142 labellen = dot - hostp;
144 labellen = strlen(hostp);
145 if((labellen > 63) || (!labellen)) {
146 /* label is too long or too short, error out */
148 return DOH_DNS_BAD_LABEL;
150 /* label is non-empty, process it */
151 *dnsp++ = (unsigned char)labellen;
152 memcpy(dnsp, hostp, labellen);
155 /* advance past dot, but only if there is one */
160 *dnsp++ = 0; /* append zero-length label for root */
162 /* There are assigned TYPE codes beyond 255: use range [1..65535] */
163 *dnsp++ = (unsigned char)(255 & (dnstype>>8)); /* upper 8 bit TYPE */
164 *dnsp++ = (unsigned char)(255 & dnstype); /* lower 8 bit TYPE */
166 *dnsp++ = '\0'; /* upper 8 bit CLASS */
167 *dnsp++ = DNS_CLASS_IN; /* IN - "the Internet" */
171 /* verify that our estimation of length is valid, since
172 * this has led to buffer overflows in this function */
173 DEBUGASSERT(*olen == expected_len);
178 doh_write_cb(const void *contents, size_t size, size_t nmemb, void *userp)
180 size_t realsize = size * nmemb;
181 struct dynbuf *mem = (struct dynbuf *)userp;
183 if(Curl_dyn_addn(mem, contents, realsize))
189 /* called from multi.c when this DoH transfer is complete */
190 static int doh_done(struct Curl_easy *doh, CURLcode result)
192 struct Curl_easy *data = doh->set.dohfor;
193 struct dohdata *dohp = data->req.doh;
194 /* so one of the DoH request done for the 'data' transfer is now complete! */
196 infof(data, "a DoH request is completed, %u to go", dohp->pending);
198 infof(data, "DoH request %s", curl_easy_strerror(result));
202 curl_slist_free_all(dohp->headers);
203 dohp->headers = NULL;
204 Curl_expire(data, 0, EXPIRE_RUN_NOW);
209 #define ERROR_CHECK_SETOPT(x,y) \
211 result = curl_easy_setopt(doh, x, y); \
213 result != CURLE_NOT_BUILT_IN && \
214 result != CURLE_UNKNOWN_OPTION) \
218 static CURLcode dohprobe(struct Curl_easy *data,
219 struct dnsprobe *p, DNStype dnstype,
221 const char *url, CURLM *multi,
222 struct curl_slist *headers)
224 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),
231 failf(data, "Failed to encode DoH packet [%d]", d);
232 return CURLE_OUT_OF_MEMORY;
235 p->dnstype = dnstype;
236 Curl_dyn_init(&p->serverdoh, DYN_DOH_RESPONSE);
238 timeout_ms = Curl_timeleft(data, NULL, TRUE);
239 if(timeout_ms <= 0) {
240 result = CURLE_OPERATION_TIMEDOUT;
243 /* Curl_open() is the internal version of curl_easy_init() */
244 result = Curl_open(&doh);
246 /* pass in the struct pointer via a local variable to please coverity and
247 the gcc typecheck helpers */
248 struct dynbuf *resp = &p->serverdoh;
249 ERROR_CHECK_SETOPT(CURLOPT_URL, url);
250 ERROR_CHECK_SETOPT(CURLOPT_WRITEFUNCTION, doh_write_cb);
251 ERROR_CHECK_SETOPT(CURLOPT_WRITEDATA, resp);
252 ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDS, p->dohbuffer);
253 ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDSIZE, (long)p->dohlen);
254 ERROR_CHECK_SETOPT(CURLOPT_HTTPHEADER, headers);
256 ERROR_CHECK_SETOPT(CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS);
259 /* enforce HTTPS if not debug */
260 ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTPS);
262 /* in debug mode, also allow http */
263 ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTP|CURLPROTO_HTTPS);
265 ERROR_CHECK_SETOPT(CURLOPT_TIMEOUT_MS, (long)timeout_ms);
266 ERROR_CHECK_SETOPT(CURLOPT_SHARE, data->share);
267 if(data->set.err && data->set.err != stderr)
268 ERROR_CHECK_SETOPT(CURLOPT_STDERR, data->set.err);
269 if(data->set.verbose)
270 ERROR_CHECK_SETOPT(CURLOPT_VERBOSE, 1L);
271 if(data->set.no_signal)
272 ERROR_CHECK_SETOPT(CURLOPT_NOSIGNAL, 1L);
274 ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYHOST,
275 data->set.doh_verifyhost ? 2L : 0L);
276 ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYPEER,
277 data->set.doh_verifypeer ? 1L : 0L);
278 ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYSTATUS,
279 data->set.doh_verifystatus ? 1L : 0L);
281 /* Inherit *some* SSL options from the user's transfer. This is a
282 best-guess as to which options are needed for compatibility. #3661
284 Note DoH does not inherit the user's proxy server so proxy SSL settings
285 have no effect and are not inherited. If that changes then two new
286 options should be added to check doh proxy insecure separately,
287 CURLOPT_DOH_PROXY_SSL_VERIFYHOST and CURLOPT_DOH_PROXY_SSL_VERIFYPEER.
289 if(data->set.ssl.falsestart)
290 ERROR_CHECK_SETOPT(CURLOPT_SSL_FALSESTART, 1L);
291 if(data->set.str[STRING_SSL_CAFILE]) {
292 ERROR_CHECK_SETOPT(CURLOPT_CAINFO,
293 data->set.str[STRING_SSL_CAFILE]);
295 if(data->set.blobs[BLOB_CAINFO]) {
296 ERROR_CHECK_SETOPT(CURLOPT_CAINFO_BLOB,
297 data->set.blobs[BLOB_CAINFO]);
299 if(data->set.str[STRING_SSL_CAPATH]) {
300 ERROR_CHECK_SETOPT(CURLOPT_CAPATH,
301 data->set.str[STRING_SSL_CAPATH]);
303 if(data->set.str[STRING_SSL_CRLFILE]) {
304 ERROR_CHECK_SETOPT(CURLOPT_CRLFILE,
305 data->set.str[STRING_SSL_CRLFILE]);
307 if(data->set.ssl.certinfo)
308 ERROR_CHECK_SETOPT(CURLOPT_CERTINFO, 1L);
309 if(data->set.str[STRING_SSL_RANDOM_FILE]) {
310 ERROR_CHECK_SETOPT(CURLOPT_RANDOM_FILE,
311 data->set.str[STRING_SSL_RANDOM_FILE]);
313 if(data->set.str[STRING_SSL_EGDSOCKET]) {
314 ERROR_CHECK_SETOPT(CURLOPT_EGDSOCKET,
315 data->set.str[STRING_SSL_EGDSOCKET]);
317 if(data->set.ssl.fsslctx)
318 ERROR_CHECK_SETOPT(CURLOPT_SSL_CTX_FUNCTION, data->set.ssl.fsslctx);
319 if(data->set.ssl.fsslctxp)
320 ERROR_CHECK_SETOPT(CURLOPT_SSL_CTX_DATA, data->set.ssl.fsslctxp);
321 if(data->set.str[STRING_SSL_EC_CURVES]) {
322 ERROR_CHECK_SETOPT(CURLOPT_SSL_EC_CURVES,
323 data->set.str[STRING_SSL_EC_CURVES]);
328 (data->set.ssl.enable_beast ?
329 CURLSSLOPT_ALLOW_BEAST : 0) |
330 (data->set.ssl.no_revoke ?
331 CURLSSLOPT_NO_REVOKE : 0) |
332 (data->set.ssl.no_partialchain ?
333 CURLSSLOPT_NO_PARTIALCHAIN : 0) |
334 (data->set.ssl.revoke_best_effort ?
335 CURLSSLOPT_REVOKE_BEST_EFFORT : 0) |
336 (data->set.ssl.native_ca_store ?
337 CURLSSLOPT_NATIVE_CA : 0) |
338 (data->set.ssl.auto_client_cert ?
339 CURLSSLOPT_AUTO_CLIENT_CERT : 0);
341 (void)curl_easy_setopt(doh, CURLOPT_SSL_OPTIONS, mask);
344 doh->set.fmultidone = doh_done;
345 doh->set.dohfor = data; /* identify for which transfer this is done */
348 /* DoH private_data must be null because the user must have a way to
349 distinguish their transfer's handle from DoH handles in user
350 callbacks (ie SSL CTX callback). */
351 DEBUGASSERT(!doh->set.private_data);
353 if(curl_multi_add_handle(multi, doh))
368 * Curl_doh() resolves a name using DoH. It resolves a name and returns a
369 * 'Curl_addrinfo *' with the address information.
372 struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
373 const char *hostname,
377 CURLcode result = CURLE_OK;
379 struct dohdata *dohp;
380 struct connectdata *conn = data->conn;
381 *waitp = TRUE; /* this never returns synchronously */
385 DEBUGASSERT(!data->req.doh);
388 /* start clean, consider allocating this struct on demand */
389 dohp = data->req.doh = calloc(sizeof(struct dohdata), 1);
393 conn->bits.doh = TRUE;
394 dohp->host = hostname;
397 curl_slist_append(NULL,
398 "Content-Type: application/dns-message");
402 /* create IPv4 DoH request */
403 result = dohprobe(data, &dohp->probe[DOH_PROBE_SLOT_IPADDR_V4],
404 DNS_TYPE_A, hostname, data->set.str[STRING_DOH],
405 data->multi, dohp->headers);
410 if(Curl_ipv6works(data)) {
411 /* create IPv6 DoH request */
412 result = dohprobe(data, &dohp->probe[DOH_PROBE_SLOT_IPADDR_V6],
413 DNS_TYPE_AAAA, hostname, data->set.str[STRING_DOH],
414 data->multi, dohp->headers);
422 curl_slist_free_all(dohp->headers);
423 data->req.doh->headers = NULL;
424 for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) {
425 Curl_close(&dohp->probe[slot].easy);
427 Curl_safefree(data->req.doh);
431 static DOHcode skipqname(const unsigned char *doh, size_t dohlen,
432 unsigned int *indexp)
434 unsigned char length;
436 if(dohlen < (*indexp + 1))
437 return DOH_DNS_OUT_OF_RANGE;
438 length = doh[*indexp];
439 if((length & 0xc0) == 0xc0) {
440 /* name pointer, advance over it and be done */
441 if(dohlen < (*indexp + 2))
442 return DOH_DNS_OUT_OF_RANGE;
447 return DOH_DNS_BAD_LABEL;
448 if(dohlen < (*indexp + 1 + length))
449 return DOH_DNS_OUT_OF_RANGE;
450 *indexp += 1 + length;
455 static unsigned short get16bit(const unsigned char *doh, int index)
457 return (unsigned short)((doh[index] << 8) | doh[index + 1]);
460 static unsigned int get32bit(const unsigned char *doh, int index)
462 /* make clang and gcc optimize this to bswap by incrementing
463 the pointer first. */
466 /* avoid undefined behavior by casting to unsigned before shifting
467 24 bits, possibly into the sign bit. codegen is same, but
468 ub sanitizer won't be upset */
469 return ( (unsigned)doh[0] << 24) | (doh[1] << 16) |(doh[2] << 8) | doh[3];
472 static DOHcode store_a(const unsigned char *doh, int index, struct dohentry *d)
474 /* silently ignore addresses over the limit */
475 if(d->numaddr < DOH_MAX_ADDR) {
476 struct dohaddr *a = &d->addr[d->numaddr];
477 a->type = DNS_TYPE_A;
478 memcpy(&a->ip.v4, &doh[index], 4);
484 static DOHcode store_aaaa(const unsigned char *doh,
488 /* silently ignore addresses over the limit */
489 if(d->numaddr < DOH_MAX_ADDR) {
490 struct dohaddr *a = &d->addr[d->numaddr];
491 a->type = DNS_TYPE_AAAA;
492 memcpy(&a->ip.v6, &doh[index], 16);
498 static DOHcode store_cname(const unsigned char *doh,
504 unsigned int loop = 128; /* a valid DNS name can never loop this much */
505 unsigned char length;
507 if(d->numcname == DOH_MAX_CNAME)
508 return DOH_OK; /* skip! */
510 c = &d->cname[d->numcname++];
513 return DOH_DNS_OUT_OF_RANGE;
515 if((length & 0xc0) == 0xc0) {
517 /* name pointer, get the new offset (14 bits) */
518 if((index + 1) >= dohlen)
519 return DOH_DNS_OUT_OF_RANGE;
521 /* move to the new index */
522 newpos = (length & 0x3f) << 8 | doh[index + 1];
526 else if(length & 0xc0)
527 return DOH_DNS_BAD_LABEL; /* bad input */
532 if(Curl_dyn_len(c)) {
533 if(Curl_dyn_addn(c, STRCONST(".")))
534 return DOH_OUT_OF_MEM;
536 if((index + length) > dohlen)
537 return DOH_DNS_BAD_LABEL;
539 if(Curl_dyn_addn(c, &doh[index], length))
540 return DOH_OUT_OF_MEM;
543 } while(length && --loop);
546 return DOH_DNS_LABEL_LOOP;
550 static DOHcode rdata(const unsigned char *doh,
552 unsigned short rdlength,
558 - A (TYPE 1): 4 bytes
559 - AAAA (TYPE 28): 16 bytes
560 - NS (TYPE 2): N bytes */
566 return DOH_DNS_RDATA_LEN;
567 rc = store_a(doh, index, d);
573 return DOH_DNS_RDATA_LEN;
574 rc = store_aaaa(doh, index, d);
579 rc = store_cname(doh, dohlen, index, d);
584 /* explicit for clarity; just skip; rely on synthesized CNAME */
587 /* unsupported type, just skip it */
593 UNITTEST void de_init(struct dohentry *de)
596 memset(de, 0, sizeof(*de));
598 for(i = 0; i < DOH_MAX_CNAME; i++)
599 Curl_dyn_init(&de->cname[i], DYN_DOH_CNAME);
603 UNITTEST DOHcode doh_decode(const unsigned char *doh,
609 unsigned short qdcount;
610 unsigned short ancount;
611 unsigned short type = 0;
612 unsigned short rdlength;
613 unsigned short nscount;
614 unsigned short arcount;
615 unsigned int index = 12;
619 return DOH_TOO_SMALL_BUFFER; /* too small */
620 if(!doh || doh[0] || doh[1])
621 return DOH_DNS_BAD_ID; /* bad ID */
622 rcode = doh[3] & 0x0f;
624 return DOH_DNS_BAD_RCODE; /* bad rcode */
626 qdcount = get16bit(doh, 4);
628 rc = skipqname(doh, dohlen, &index);
630 return rc; /* bad qname */
631 if(dohlen < (index + 4))
632 return DOH_DNS_OUT_OF_RANGE;
633 index += 4; /* skip question's type and class */
637 ancount = get16bit(doh, 6);
639 unsigned short class;
642 rc = skipqname(doh, dohlen, &index);
644 return rc; /* bad qname */
646 if(dohlen < (index + 2))
647 return DOH_DNS_OUT_OF_RANGE;
649 type = get16bit(doh, index);
650 if((type != DNS_TYPE_CNAME) /* may be synthesized from DNAME */
651 && (type != DNS_TYPE_DNAME) /* if present, accept and ignore */
652 && (type != dnstype))
653 /* Not the same type as was asked for nor CNAME nor DNAME */
654 return DOH_DNS_UNEXPECTED_TYPE;
657 if(dohlen < (index + 2))
658 return DOH_DNS_OUT_OF_RANGE;
659 class = get16bit(doh, index);
660 if(DNS_CLASS_IN != class)
661 return DOH_DNS_UNEXPECTED_CLASS; /* unsupported */
664 if(dohlen < (index + 4))
665 return DOH_DNS_OUT_OF_RANGE;
667 ttl = get32bit(doh, index);
672 if(dohlen < (index + 2))
673 return DOH_DNS_OUT_OF_RANGE;
675 rdlength = get16bit(doh, index);
677 if(dohlen < (index + rdlength))
678 return DOH_DNS_OUT_OF_RANGE;
680 rc = rdata(doh, dohlen, rdlength, type, index, d);
682 return rc; /* bad rdata */
687 nscount = get16bit(doh, 8);
689 rc = skipqname(doh, dohlen, &index);
691 return rc; /* bad qname */
693 if(dohlen < (index + 8))
694 return DOH_DNS_OUT_OF_RANGE;
696 index += 2 + 2 + 4; /* type, class and ttl */
698 if(dohlen < (index + 2))
699 return DOH_DNS_OUT_OF_RANGE;
701 rdlength = get16bit(doh, index);
703 if(dohlen < (index + rdlength))
704 return DOH_DNS_OUT_OF_RANGE;
709 arcount = get16bit(doh, 10);
711 rc = skipqname(doh, dohlen, &index);
713 return rc; /* bad qname */
715 if(dohlen < (index + 8))
716 return DOH_DNS_OUT_OF_RANGE;
718 index += 2 + 2 + 4; /* type, class and ttl */
720 if(dohlen < (index + 2))
721 return DOH_DNS_OUT_OF_RANGE;
723 rdlength = get16bit(doh, index);
725 if(dohlen < (index + rdlength))
726 return DOH_DNS_OUT_OF_RANGE;
732 return DOH_DNS_MALFORMAT; /* something is wrong */
734 if((type != DNS_TYPE_NS) && !d->numcname && !d->numaddr)
735 /* nothing stored! */
736 return DOH_NO_CONTENT;
738 return DOH_OK; /* ok */
741 #ifndef CURL_DISABLE_VERBOSE_STRINGS
742 static void showdoh(struct Curl_easy *data,
743 const struct dohentry *d)
746 infof(data, "TTL: %u seconds", d->ttl);
747 for(i = 0; i < d->numaddr; i++) {
748 const struct dohaddr *a = &d->addr[i];
749 if(a->type == DNS_TYPE_A) {
750 infof(data, "DoH A: %u.%u.%u.%u",
751 a->ip.v4[0], a->ip.v4[1],
752 a->ip.v4[2], a->ip.v4[3]);
754 else if(a->type == DNS_TYPE_AAAA) {
759 msnprintf(buffer, 128, "DoH AAAA: ");
762 for(j = 0; j < 16; j += 2) {
764 msnprintf(ptr, len, "%s%02x%02x", j?":":"", d->addr[i].ip.v6[j],
765 d->addr[i].ip.v6[j + 1]);
770 infof(data, "%s", buffer);
773 for(i = 0; i < d->numcname; i++) {
774 infof(data, "CNAME: %s", Curl_dyn_ptr(&d->cname[i]));
784 * This function returns a pointer to the first element of a newly allocated
785 * Curl_addrinfo struct linked list filled with the data from a set of DoH
786 * lookups. Curl_addrinfo is meant to work like the addrinfo struct does for
787 * a IPv6 stack, but usable also for IPv4, all hosts and environments.
789 * The memory allocated by this function *MUST* be free'd later on calling
790 * Curl_freeaddrinfo(). For each successful call to this function there
791 * must be an associated call later to Curl_freeaddrinfo().
794 static struct Curl_addrinfo *
795 doh2ai(const struct dohentry *de, const char *hostname, int port)
797 struct Curl_addrinfo *ai;
798 struct Curl_addrinfo *prevai = NULL;
799 struct Curl_addrinfo *firstai = NULL;
800 struct sockaddr_in *addr;
802 struct sockaddr_in6 *addr6;
804 CURLcode result = CURLE_OK;
806 size_t hostlen = strlen(hostname) + 1; /* include zero terminator */
809 /* no input == no output! */
812 for(i = 0; i < de->numaddr; i++) {
814 CURL_SA_FAMILY_T addrtype;
815 if(de->addr[i].type == DNS_TYPE_AAAA) {
817 /* we can't handle IPv6 addresses */
820 ss_size = sizeof(struct sockaddr_in6);
825 ss_size = sizeof(struct sockaddr_in);
829 ai = calloc(1, sizeof(struct Curl_addrinfo) + ss_size + hostlen);
831 result = CURLE_OUT_OF_MEMORY;
834 ai->ai_addr = (void *)((char *)ai + sizeof(struct Curl_addrinfo));
835 ai->ai_canonname = (void *)((char *)ai->ai_addr + ss_size);
836 memcpy(ai->ai_canonname, hostname, hostlen);
839 /* store the pointer we want to return from this function */
843 /* make the previous entry point to this */
844 prevai->ai_next = ai;
846 ai->ai_family = addrtype;
848 /* we return all names as STREAM, so when using this address for TFTP
849 the type must be ignored and conn->socktype be used instead! */
850 ai->ai_socktype = SOCK_STREAM;
852 ai->ai_addrlen = (curl_socklen_t)ss_size;
854 /* leave the rest of the struct filled with zero */
856 switch(ai->ai_family) {
858 addr = (void *)ai->ai_addr; /* storage area for this info */
859 DEBUGASSERT(sizeof(struct in_addr) == sizeof(de->addr[i].ip.v4));
860 memcpy(&addr->sin_addr, &de->addr[i].ip.v4, sizeof(struct in_addr));
861 addr->sin_family = addrtype;
862 addr->sin_port = htons((unsigned short)port);
867 addr6 = (void *)ai->ai_addr; /* storage area for this info */
868 DEBUGASSERT(sizeof(struct in6_addr) == sizeof(de->addr[i].ip.v6));
869 memcpy(&addr6->sin6_addr, &de->addr[i].ip.v6, sizeof(struct in6_addr));
870 addr6->sin6_family = addrtype;
871 addr6->sin6_port = htons((unsigned short)port);
880 Curl_freeaddrinfo(firstai);
887 #ifndef CURL_DISABLE_VERBOSE_STRINGS
888 static const char *type2name(DNStype dnstype)
890 return (dnstype == DNS_TYPE_A)?"A":"AAAA";
894 UNITTEST void de_cleanup(struct dohentry *d)
897 for(i = 0; i < d->numcname; i++) {
898 Curl_dyn_free(&d->cname[i]);
902 CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
903 struct Curl_dns_entry **dnsp)
906 struct dohdata *dohp = data->req.doh;
907 *dnsp = NULL; /* defaults to no response */
909 return CURLE_OUT_OF_MEMORY;
911 if(!dohp->probe[DOH_PROBE_SLOT_IPADDR_V4].easy &&
912 !dohp->probe[DOH_PROBE_SLOT_IPADDR_V6].easy) {
913 failf(data, "Could not DoH-resolve: %s", data->state.async.hostname);
914 return CONN_IS_PROXIED(data->conn)?CURLE_COULDNT_RESOLVE_PROXY:
915 CURLE_COULDNT_RESOLVE_HOST;
917 else if(!dohp->pending) {
918 DOHcode rc[DOH_PROBE_SLOTS] = {
923 /* remove DoH handles from multi handle and close them */
924 for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) {
925 curl_multi_remove_handle(data->multi, dohp->probe[slot].easy);
926 Curl_close(&dohp->probe[slot].easy);
928 /* parse the responses, create the struct and return it! */
930 for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) {
931 struct dnsprobe *p = &dohp->probe[slot];
934 rc[slot] = doh_decode(Curl_dyn_uptr(&p->serverdoh),
935 Curl_dyn_len(&p->serverdoh),
938 Curl_dyn_free(&p->serverdoh);
940 infof(data, "DoH: %s type %s for %s", doh_strerror(rc[slot]),
941 type2name(p->dnstype), dohp->host);
945 result = CURLE_COULDNT_RESOLVE_HOST; /* until we know better */
946 if(!rc[DOH_PROBE_SLOT_IPADDR_V4] || !rc[DOH_PROBE_SLOT_IPADDR_V6]) {
947 /* we have an address, of one kind or other */
948 struct Curl_dns_entry *dns;
949 struct Curl_addrinfo *ai;
951 infof(data, "DoH Host name: %s", dohp->host);
954 ai = doh2ai(&de, dohp->host, dohp->port);
957 return CURLE_OUT_OF_MEMORY;
961 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
963 /* we got a response, store it in the cache */
964 dns = Curl_cache_addr(data, ai, dohp->host, dohp->port);
967 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
970 /* returned failure, bail out nicely */
971 Curl_freeaddrinfo(ai);
974 data->state.async.dns = dns;
976 result = CURLE_OK; /* address resolution OK */
978 } /* address processing done */
980 /* Now process any build-specific attributes retrieved from DNS */
984 Curl_safefree(data->req.doh);
987 } /* !dohp->pending */
989 /* else wait for pending DoH transactions to complete */
993 #endif /* CURL_DISABLE_DOH */