1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 2018 - 2020, 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.haxx.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 Curl_doh_done(struct Curl_easy *doh, CURLcode result)
192 struct Curl_easy *data = doh->set.dohfor;
193 /* so one of the DOH request done for the 'data' transfer is now complete! */
194 data->req.doh.pending--;
195 infof(data, "a DOH request is completed, %u to go\n", data->req.doh.pending);
197 infof(data, "DOH request %s\n", curl_easy_strerror(result));
199 if(!data->req.doh.pending) {
201 curl_slist_free_all(data->req.doh.headers);
202 data->req.doh.headers = NULL;
203 Curl_expire(data, 0, EXPIRE_RUN_NOW);
208 #define ERROR_CHECK_SETOPT(x,y) \
210 result = curl_easy_setopt(doh, x, y); \
215 static CURLcode dohprobe(struct Curl_easy *data,
216 struct dnsprobe *p, DNStype dnstype,
218 const char *url, CURLM *multi,
219 struct curl_slist *headers)
221 struct Curl_easy *doh = NULL;
223 CURLcode result = CURLE_OK;
224 timediff_t timeout_ms;
225 DOHcode d = doh_encode(host, dnstype, p->dohbuffer, sizeof(p->dohbuffer),
228 failf(data, "Failed to encode DOH packet [%d]\n", d);
229 return CURLE_OUT_OF_MEMORY;
232 p->dnstype = dnstype;
233 Curl_dyn_init(&p->serverdoh, DYN_DOH_RESPONSE);
235 /* Note: this is code for sending the DoH request with GET but there's still
236 no logic that actually enables this. We should either add that ability or
237 yank out the GET code. Discuss! */
238 if(data->set.doh_get) {
241 result = Curl_base64url_encode(data, (char *)p->dohbuffer, p->dohlen,
245 nurl = aprintf("%s?dns=%s", url, b64);
248 result = CURLE_OUT_OF_MEMORY;
254 timeout_ms = Curl_timeleft(data, NULL, TRUE);
255 if(timeout_ms <= 0) {
256 result = CURLE_OPERATION_TIMEDOUT;
259 /* Curl_open() is the internal version of curl_easy_init() */
260 result = Curl_open(&doh);
262 /* pass in the struct pointer via a local variable to please coverity and
263 the gcc typecheck helpers */
264 struct dynbuf *resp = &p->serverdoh;
265 ERROR_CHECK_SETOPT(CURLOPT_URL, url);
266 ERROR_CHECK_SETOPT(CURLOPT_WRITEFUNCTION, doh_write_cb);
267 ERROR_CHECK_SETOPT(CURLOPT_WRITEDATA, resp);
268 if(!data->set.doh_get) {
269 ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDS, p->dohbuffer);
270 ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDSIZE, (long)p->dohlen);
272 ERROR_CHECK_SETOPT(CURLOPT_HTTPHEADER, headers);
274 ERROR_CHECK_SETOPT(CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS);
277 /* enforce HTTPS if not debug */
278 ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTPS);
280 /* in debug mode, also allow http */
281 ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTP|CURLPROTO_HTTPS);
283 ERROR_CHECK_SETOPT(CURLOPT_TIMEOUT_MS, (long)timeout_ms);
284 if(data->set.verbose)
285 ERROR_CHECK_SETOPT(CURLOPT_VERBOSE, 1L);
286 if(data->set.no_signal)
287 ERROR_CHECK_SETOPT(CURLOPT_NOSIGNAL, 1L);
289 /* Inherit *some* SSL options from the user's transfer. This is a
290 best-guess as to which options are needed for compatibility. #3661 */
291 if(data->set.ssl.falsestart)
292 ERROR_CHECK_SETOPT(CURLOPT_SSL_FALSESTART, 1L);
293 if(data->set.ssl.primary.verifyhost)
294 ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYHOST, 2L);
295 #ifndef CURL_DISABLE_PROXY
296 if(data->set.proxy_ssl.primary.verifyhost)
297 ERROR_CHECK_SETOPT(CURLOPT_PROXY_SSL_VERIFYHOST, 2L);
298 if(data->set.proxy_ssl.primary.verifypeer)
299 ERROR_CHECK_SETOPT(CURLOPT_PROXY_SSL_VERIFYPEER, 1L);
300 if(data->set.str[STRING_SSL_CAFILE_PROXY]) {
301 ERROR_CHECK_SETOPT(CURLOPT_PROXY_CAINFO,
302 data->set.str[STRING_SSL_CAFILE_PROXY]);
304 if(data->set.str[STRING_SSL_CRLFILE_PROXY]) {
305 ERROR_CHECK_SETOPT(CURLOPT_PROXY_CRLFILE,
306 data->set.str[STRING_SSL_CRLFILE_PROXY]);
308 if(data->set.proxy_ssl.no_revoke)
309 ERROR_CHECK_SETOPT(CURLOPT_PROXY_SSL_OPTIONS, CURLSSLOPT_NO_REVOKE);
310 else if(data->set.proxy_ssl.revoke_best_effort)
311 ERROR_CHECK_SETOPT(CURLOPT_PROXY_SSL_OPTIONS,
312 CURLSSLOPT_REVOKE_BEST_EFFORT);
313 if(data->set.str[STRING_SSL_CAPATH_PROXY]) {
314 ERROR_CHECK_SETOPT(CURLOPT_PROXY_CAPATH,
315 data->set.str[STRING_SSL_CAPATH_PROXY]);
318 if(data->set.ssl.primary.verifypeer)
319 ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYPEER, 1L);
320 if(data->set.ssl.primary.verifystatus)
321 ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYSTATUS, 1L);
322 if(data->set.str[STRING_SSL_CAFILE_ORIG]) {
323 ERROR_CHECK_SETOPT(CURLOPT_CAINFO,
324 data->set.str[STRING_SSL_CAFILE_ORIG]);
326 if(data->set.str[STRING_SSL_CAPATH_ORIG]) {
327 ERROR_CHECK_SETOPT(CURLOPT_CAPATH,
328 data->set.str[STRING_SSL_CAPATH_ORIG]);
330 if(data->set.str[STRING_SSL_CRLFILE_ORIG]) {
331 ERROR_CHECK_SETOPT(CURLOPT_CRLFILE,
332 data->set.str[STRING_SSL_CRLFILE_ORIG]);
334 if(data->set.ssl.certinfo)
335 ERROR_CHECK_SETOPT(CURLOPT_CERTINFO, 1L);
336 if(data->set.str[STRING_SSL_RANDOM_FILE]) {
337 ERROR_CHECK_SETOPT(CURLOPT_RANDOM_FILE,
338 data->set.str[STRING_SSL_RANDOM_FILE]);
340 if(data->set.str[STRING_SSL_EGDSOCKET]) {
341 ERROR_CHECK_SETOPT(CURLOPT_EGDSOCKET,
342 data->set.str[STRING_SSL_EGDSOCKET]);
344 if(data->set.ssl.no_revoke)
345 ERROR_CHECK_SETOPT(CURLOPT_SSL_OPTIONS, CURLSSLOPT_NO_REVOKE);
346 else if(data->set.ssl.revoke_best_effort)
347 ERROR_CHECK_SETOPT(CURLOPT_SSL_OPTIONS, CURLSSLOPT_REVOKE_BEST_EFFORT);
348 if(data->set.ssl.fsslctx)
349 ERROR_CHECK_SETOPT(CURLOPT_SSL_CTX_FUNCTION, data->set.ssl.fsslctx);
350 if(data->set.ssl.fsslctxp)
351 ERROR_CHECK_SETOPT(CURLOPT_SSL_CTX_DATA, data->set.ssl.fsslctxp);
352 if(data->set.str[STRING_SSL_EC_CURVES]) {
353 ERROR_CHECK_SETOPT(CURLOPT_SSL_EC_CURVES,
354 data->set.str[STRING_SSL_EC_CURVES]);
357 doh->set.fmultidone = Curl_doh_done;
358 doh->set.dohfor = data; /* identify for which transfer this is done */
361 /* add this transfer to the multi handle */
362 if(curl_multi_add_handle(multi, doh))
377 * Curl_doh() resolves a name using DOH. It resolves a name and returns a
378 * 'Curl_addrinfo *' with the address information.
381 struct Curl_addrinfo *Curl_doh(struct connectdata *conn,
382 const char *hostname,
386 struct Curl_easy *data = conn->data;
387 CURLcode result = CURLE_OK;
389 *waitp = TRUE; /* this never returns synchronously */
394 /* start clean, consider allocating this struct on demand */
395 memset(&data->req.doh, 0, sizeof(struct dohdata));
397 conn->bits.doh = TRUE;
398 data->req.doh.host = hostname;
399 data->req.doh.port = port;
400 data->req.doh.headers =
401 curl_slist_append(NULL,
402 "Content-Type: application/dns-message");
403 if(!data->req.doh.headers)
406 if(conn->ip_version != CURL_IPRESOLVE_V6) {
407 /* create IPv4 DOH request */
408 result = dohprobe(data, &data->req.doh.probe[DOH_PROBE_SLOT_IPADDR_V4],
409 DNS_TYPE_A, hostname, data->set.str[STRING_DOH],
410 data->multi, data->req.doh.headers);
413 data->req.doh.pending++;
416 if(conn->ip_version != CURL_IPRESOLVE_V4) {
417 /* create IPv6 DOH request */
418 result = dohprobe(data, &data->req.doh.probe[DOH_PROBE_SLOT_IPADDR_V6],
419 DNS_TYPE_AAAA, hostname, data->set.str[STRING_DOH],
420 data->multi, data->req.doh.headers);
423 data->req.doh.pending++;
428 curl_slist_free_all(data->req.doh.headers);
429 data->req.doh.headers = NULL;
430 for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) {
431 Curl_close(&data->req.doh.probe[slot].easy);
436 static DOHcode skipqname(const unsigned char *doh, size_t dohlen,
437 unsigned int *indexp)
439 unsigned char length;
441 if(dohlen < (*indexp + 1))
442 return DOH_DNS_OUT_OF_RANGE;
443 length = doh[*indexp];
444 if((length & 0xc0) == 0xc0) {
445 /* name pointer, advance over it and be done */
446 if(dohlen < (*indexp + 2))
447 return DOH_DNS_OUT_OF_RANGE;
452 return DOH_DNS_BAD_LABEL;
453 if(dohlen < (*indexp + 1 + length))
454 return DOH_DNS_OUT_OF_RANGE;
455 *indexp += 1 + length;
460 static unsigned short get16bit(const unsigned char *doh, int index)
462 return (unsigned short)((doh[index] << 8) | doh[index + 1]);
465 static unsigned int get32bit(const unsigned char *doh, int index)
467 /* make clang and gcc optimize this to bswap by incrementing
468 the pointer first. */
471 /* avoid undefined behaviour by casting to unsigned before shifting
472 24 bits, possibly into the sign bit. codegen is same, but
473 ub sanitizer won't be upset */
474 return ( (unsigned)doh[0] << 24) | (doh[1] << 16) |(doh[2] << 8) | doh[3];
477 static DOHcode store_a(const unsigned char *doh, int index, struct dohentry *d)
479 /* silently ignore addresses over the limit */
480 if(d->numaddr < DOH_MAX_ADDR) {
481 struct dohaddr *a = &d->addr[d->numaddr];
482 a->type = DNS_TYPE_A;
483 memcpy(&a->ip.v4, &doh[index], 4);
489 static DOHcode store_aaaa(const unsigned char *doh,
493 /* silently ignore addresses over the limit */
494 if(d->numaddr < DOH_MAX_ADDR) {
495 struct dohaddr *a = &d->addr[d->numaddr];
496 a->type = DNS_TYPE_AAAA;
497 memcpy(&a->ip.v6, &doh[index], 16);
503 static DOHcode store_cname(const unsigned char *doh,
509 unsigned int loop = 128; /* a valid DNS name can never loop this much */
510 unsigned char length;
512 if(d->numcname == DOH_MAX_CNAME)
513 return DOH_OK; /* skip! */
515 c = &d->cname[d->numcname++];
518 return DOH_DNS_OUT_OF_RANGE;
520 if((length & 0xc0) == 0xc0) {
522 /* name pointer, get the new offset (14 bits) */
523 if((index + 1) >= dohlen)
524 return DOH_DNS_OUT_OF_RANGE;
526 /* move to the new index */
527 newpos = (length & 0x3f) << 8 | doh[index + 1];
531 else if(length & 0xc0)
532 return DOH_DNS_BAD_LABEL; /* bad input */
537 if(Curl_dyn_len(c)) {
538 if(Curl_dyn_add(c, "."))
539 return DOH_OUT_OF_MEM;
541 if((index + length) > dohlen)
542 return DOH_DNS_BAD_LABEL;
544 if(Curl_dyn_addn(c, &doh[index], length))
545 return DOH_OUT_OF_MEM;
548 } while(length && --loop);
551 return DOH_DNS_LABEL_LOOP;
555 static DOHcode rdata(const unsigned char *doh,
557 unsigned short rdlength,
563 - A (TYPE 1): 4 bytes
564 - AAAA (TYPE 28): 16 bytes
565 - NS (TYPE 2): N bytes */
571 return DOH_DNS_RDATA_LEN;
572 rc = store_a(doh, index, d);
578 return DOH_DNS_RDATA_LEN;
579 rc = store_aaaa(doh, index, d);
584 rc = store_cname(doh, dohlen, index, d);
589 /* explicit for clarity; just skip; rely on synthesized CNAME */
592 /* unsupported type, just skip it */
598 UNITTEST void de_init(struct dohentry *de)
601 memset(de, 0, sizeof(*de));
603 for(i = 0; i < DOH_MAX_CNAME; i++)
604 Curl_dyn_init(&de->cname[i], DYN_DOH_CNAME);
608 UNITTEST DOHcode doh_decode(const unsigned char *doh,
614 unsigned short qdcount;
615 unsigned short ancount;
616 unsigned short type = 0;
617 unsigned short rdlength;
618 unsigned short nscount;
619 unsigned short arcount;
620 unsigned int index = 12;
624 return DOH_TOO_SMALL_BUFFER; /* too small */
625 if(!doh || doh[0] || doh[1])
626 return DOH_DNS_BAD_ID; /* bad ID */
627 rcode = doh[3] & 0x0f;
629 return DOH_DNS_BAD_RCODE; /* bad rcode */
631 qdcount = get16bit(doh, 4);
633 rc = skipqname(doh, dohlen, &index);
635 return rc; /* bad qname */
636 if(dohlen < (index + 4))
637 return DOH_DNS_OUT_OF_RANGE;
638 index += 4; /* skip question's type and class */
642 ancount = get16bit(doh, 6);
644 unsigned short class;
647 rc = skipqname(doh, dohlen, &index);
649 return rc; /* bad qname */
651 if(dohlen < (index + 2))
652 return DOH_DNS_OUT_OF_RANGE;
654 type = get16bit(doh, index);
655 if((type != DNS_TYPE_CNAME) /* may be synthesized from DNAME */
656 && (type != DNS_TYPE_DNAME) /* if present, accept and ignore */
657 && (type != dnstype))
658 /* Not the same type as was asked for nor CNAME nor DNAME */
659 return DOH_DNS_UNEXPECTED_TYPE;
662 if(dohlen < (index + 2))
663 return DOH_DNS_OUT_OF_RANGE;
664 class = get16bit(doh, index);
665 if(DNS_CLASS_IN != class)
666 return DOH_DNS_UNEXPECTED_CLASS; /* unsupported */
669 if(dohlen < (index + 4))
670 return DOH_DNS_OUT_OF_RANGE;
672 ttl = get32bit(doh, index);
677 if(dohlen < (index + 2))
678 return DOH_DNS_OUT_OF_RANGE;
680 rdlength = get16bit(doh, index);
682 if(dohlen < (index + rdlength))
683 return DOH_DNS_OUT_OF_RANGE;
685 rc = rdata(doh, dohlen, rdlength, type, index, d);
687 return rc; /* bad rdata */
692 nscount = get16bit(doh, 8);
694 rc = skipqname(doh, dohlen, &index);
696 return rc; /* bad qname */
698 if(dohlen < (index + 8))
699 return DOH_DNS_OUT_OF_RANGE;
701 index += 2 + 2 + 4; /* type, class and ttl */
703 if(dohlen < (index + 2))
704 return DOH_DNS_OUT_OF_RANGE;
706 rdlength = get16bit(doh, index);
708 if(dohlen < (index + rdlength))
709 return DOH_DNS_OUT_OF_RANGE;
714 arcount = get16bit(doh, 10);
716 rc = skipqname(doh, dohlen, &index);
718 return rc; /* bad qname */
720 if(dohlen < (index + 8))
721 return DOH_DNS_OUT_OF_RANGE;
723 index += 2 + 2 + 4; /* type, class and ttl */
725 if(dohlen < (index + 2))
726 return DOH_DNS_OUT_OF_RANGE;
728 rdlength = get16bit(doh, index);
730 if(dohlen < (index + rdlength))
731 return DOH_DNS_OUT_OF_RANGE;
737 return DOH_DNS_MALFORMAT; /* something is wrong */
739 if((type != DNS_TYPE_NS) && !d->numcname && !d->numaddr)
740 /* nothing stored! */
741 return DOH_NO_CONTENT;
743 return DOH_OK; /* ok */
746 #ifndef CURL_DISABLE_VERBOSE_STRINGS
747 static void showdoh(struct Curl_easy *data,
748 const struct dohentry *d)
751 infof(data, "TTL: %u seconds\n", d->ttl);
752 for(i = 0; i < d->numaddr; i++) {
753 const struct dohaddr *a = &d->addr[i];
754 if(a->type == DNS_TYPE_A) {
755 infof(data, "DOH A: %u.%u.%u.%u\n",
756 a->ip.v4[0], a->ip.v4[1],
757 a->ip.v4[2], a->ip.v4[3]);
759 else if(a->type == DNS_TYPE_AAAA) {
764 msnprintf(buffer, 128, "DOH AAAA: ");
767 for(j = 0; j < 16; j += 2) {
769 msnprintf(ptr, len, "%s%02x%02x", j?":":"", d->addr[i].ip.v6[j],
770 d->addr[i].ip.v6[j + 1]);
775 infof(data, "%s\n", buffer);
778 for(i = 0; i < d->numcname; i++) {
779 infof(data, "CNAME: %s\n", Curl_dyn_ptr(&d->cname[i]));
789 * This function returns a pointer to the first element of a newly allocated
790 * Curl_addrinfo struct linked list filled with the data from a set of DOH
791 * lookups. Curl_addrinfo is meant to work like the addrinfo struct does for
792 * a IPv6 stack, but usable also for IPv4, all hosts and environments.
794 * The memory allocated by this function *MUST* be free'd later on calling
795 * Curl_freeaddrinfo(). For each successful call to this function there
796 * must be an associated call later to Curl_freeaddrinfo().
799 static struct Curl_addrinfo *
800 doh2ai(const struct dohentry *de, const char *hostname, int port)
802 struct Curl_addrinfo *ai;
803 struct Curl_addrinfo *prevai = NULL;
804 struct Curl_addrinfo *firstai = NULL;
805 struct sockaddr_in *addr;
807 struct sockaddr_in6 *addr6;
809 CURLcode result = CURLE_OK;
811 size_t hostlen = strlen(hostname) + 1; /* include zero terminator */
814 /* no input == no output! */
817 for(i = 0; i < de->numaddr; i++) {
819 CURL_SA_FAMILY_T addrtype;
820 if(de->addr[i].type == DNS_TYPE_AAAA) {
822 /* we can't handle IPv6 addresses */
825 ss_size = sizeof(struct sockaddr_in6);
830 ss_size = sizeof(struct sockaddr_in);
834 ai = calloc(1, sizeof(struct Curl_addrinfo) + ss_size + hostlen);
836 result = CURLE_OUT_OF_MEMORY;
839 ai->ai_addr = (void *)((char *)ai + sizeof(struct Curl_addrinfo));
840 ai->ai_canonname = (void *)((char *)ai->ai_addr + ss_size);
841 memcpy(ai->ai_canonname, hostname, hostlen);
844 /* store the pointer we want to return from this function */
848 /* make the previous entry point to this */
849 prevai->ai_next = ai;
851 ai->ai_family = addrtype;
853 /* we return all names as STREAM, so when using this address for TFTP
854 the type must be ignored and conn->socktype be used instead! */
855 ai->ai_socktype = SOCK_STREAM;
857 ai->ai_addrlen = (curl_socklen_t)ss_size;
859 /* leave the rest of the struct filled with zero */
861 switch(ai->ai_family) {
863 addr = (void *)ai->ai_addr; /* storage area for this info */
864 DEBUGASSERT(sizeof(struct in_addr) == sizeof(de->addr[i].ip.v4));
865 memcpy(&addr->sin_addr, &de->addr[i].ip.v4, sizeof(struct in_addr));
866 addr->sin_family = addrtype;
867 addr->sin_port = htons((unsigned short)port);
872 addr6 = (void *)ai->ai_addr; /* storage area for this info */
873 DEBUGASSERT(sizeof(struct in6_addr) == sizeof(de->addr[i].ip.v6));
874 memcpy(&addr6->sin6_addr, &de->addr[i].ip.v6, sizeof(struct in6_addr));
875 addr6->sin6_family = addrtype;
876 addr6->sin6_port = htons((unsigned short)port);
885 Curl_freeaddrinfo(firstai);
892 #ifndef CURL_DISABLE_VERBOSE_STRINGS
893 static const char *type2name(DNStype dnstype)
895 return (dnstype == DNS_TYPE_A)?"A":"AAAA";
899 UNITTEST void de_cleanup(struct dohentry *d)
902 for(i = 0; i < d->numcname; i++) {
903 Curl_dyn_free(&d->cname[i]);
907 CURLcode Curl_doh_is_resolved(struct connectdata *conn,
908 struct Curl_dns_entry **dnsp)
911 struct Curl_easy *data = conn->data;
912 *dnsp = NULL; /* defaults to no response */
914 if(!data->req.doh.probe[DOH_PROBE_SLOT_IPADDR_V4].easy &&
915 !data->req.doh.probe[DOH_PROBE_SLOT_IPADDR_V6].easy) {
916 failf(data, "Could not DOH-resolve: %s", conn->async.hostname);
917 return conn->bits.proxy?CURLE_COULDNT_RESOLVE_PROXY:
918 CURLE_COULDNT_RESOLVE_HOST;
920 else if(!data->req.doh.pending) {
921 DOHcode rc[DOH_PROBE_SLOTS] = {
926 /* remove DOH handles from multi handle and close them */
927 for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) {
928 curl_multi_remove_handle(data->multi, data->req.doh.probe[slot].easy);
929 Curl_close(&data->req.doh.probe[slot].easy);
931 /* parse the responses, create the struct and return it! */
933 for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) {
934 struct dnsprobe *p = &data->req.doh.probe[slot];
937 rc[slot] = doh_decode(Curl_dyn_uptr(&p->serverdoh),
938 Curl_dyn_len(&p->serverdoh),
941 Curl_dyn_free(&p->serverdoh);
943 infof(data, "DOH: %s type %s for %s\n", doh_strerror(rc[slot]),
944 type2name(p->dnstype), data->req.doh.host);
948 result = CURLE_COULDNT_RESOLVE_HOST; /* until we know better */
949 if(!rc[DOH_PROBE_SLOT_IPADDR_V4] || !rc[DOH_PROBE_SLOT_IPADDR_V6]) {
950 /* we have an address, of one kind or other */
951 struct Curl_dns_entry *dns;
952 struct Curl_addrinfo *ai;
954 infof(data, "DOH Host name: %s\n", data->req.doh.host);
957 ai = doh2ai(&de, data->req.doh.host, data->req.doh.port);
960 return CURLE_OUT_OF_MEMORY;
964 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
966 /* we got a response, store it in the cache */
967 dns = Curl_cache_addr(data, ai, data->req.doh.host, data->req.doh.port);
970 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
973 /* returned failure, bail out nicely */
974 Curl_freeaddrinfo(ai);
977 conn->async.dns = dns;
979 result = CURLE_OK; /* address resolution OK */
981 } /* address processing done */
983 /* Now process any build-specific attributes retrieved from DNS */
989 } /* !data->req.doh.pending */
991 /* else wait for pending DOH transactions to complete */
995 #endif /* CURL_DISABLE_DOH */