1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 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.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 * SPDX-License-Identifier: curl
22 *
23 ***************************************************************************/
24
25 #include "curl_setup.h"
26
27 #ifndef CURL_DISABLE_DOH
28
29 #include "urldata.h"
30 #include "curl_addrinfo.h"
31 #include "doh.h"
32
33 #include "sendf.h"
34 #include "multiif.h"
35 #include "url.h"
36 #include "share.h"
37 #include "curl_base64.h"
38 #include "connect.h"
39 #include "strdup.h"
40 #include "dynbuf.h"
41 /* The last 3 #include files should be in this order */
42 #include "curl_printf.h"
43 #include "curl_memory.h"
44 #include "memdebug.h"
45 #include "escape.h"
46
47 #define DNS_CLASS_IN 0x01
48
49 /* doh_print_buf truncates if the hex string will be more than this */
50 #define LOCAL_PB_HEXMAX 400
51
52 #ifndef CURL_DISABLE_VERBOSE_STRINGS
53 static const char * const errors[]={
54 "",
55 "Bad label",
56 "Out of range",
57 "Label loop",
58 "Too small",
59 "Out of memory",
60 "RDATA length",
61 "Malformat",
62 "Bad RCODE",
63 "Unexpected TYPE",
64 "Unexpected CLASS",
65 "No content",
66 "Bad ID",
67 "Name too long"
68 };
69
doh_strerror(DOHcode code)70 static const char *doh_strerror(DOHcode code)
71 {
72 if((code >= DOH_OK) && (code <= DOH_DNS_NAME_TOO_LONG))
73 return errors[code];
74 return "bad error code";
75 }
76
77 struct curl_trc_feat Curl_doh_trc = {
78 "DoH",
79 CURL_LOG_LVL_NONE,
80 };
81 #endif /* !CURL_DISABLE_VERBOSE_STRINGS */
82
83 /* @unittest 1655
84 */
doh_req_encode(const char * host,DNStype dnstype,unsigned char * dnsp,size_t len,size_t * olen)85 UNITTEST DOHcode doh_req_encode(const char *host,
86 DNStype dnstype,
87 unsigned char *dnsp, /* buffer */
88 size_t len, /* buffer size */
89 size_t *olen) /* output length */
90 {
91 const size_t hostlen = strlen(host);
92 unsigned char *orig = dnsp;
93 const char *hostp = host;
94
95 /* The expected output length is 16 bytes more than the length of
96 * the QNAME-encoding of the hostname.
97 *
98 * A valid DNS name may not contain a zero-length label, except at
99 * the end. For this reason, a name beginning with a dot, or
100 * containing a sequence of two or more consecutive dots, is invalid
101 * and cannot be encoded as a QNAME.
102 *
103 * If the hostname ends with a trailing dot, the corresponding
104 * QNAME-encoding is one byte longer than the hostname. If (as is
105 * also valid) the hostname is shortened by the omission of the
106 * trailing dot, then its QNAME-encoding will be two bytes longer
107 * than the hostname.
108 *
109 * Each [ label, dot ] pair is encoded as [ length, label ],
110 * preserving overall length. A final [ label ] without a dot is
111 * also encoded as [ length, label ], increasing overall length
112 * by one. The encoding is completed by appending a zero byte,
113 * representing the zero-length root label, again increasing
114 * the overall length by one.
115 */
116
117 size_t expected_len;
118 DEBUGASSERT(hostlen);
119 expected_len = 12 + 1 + hostlen + 4;
120 if(host[hostlen-1]!='.')
121 expected_len++;
122
123 if(expected_len > (256 + 16)) /* RFCs 1034, 1035 */
124 return DOH_DNS_NAME_TOO_LONG;
125
126 if(len < expected_len)
127 return DOH_TOO_SMALL_BUFFER;
128
129 *dnsp++ = 0; /* 16 bit id */
130 *dnsp++ = 0;
131 *dnsp++ = 0x01; /* |QR| Opcode |AA|TC|RD| Set the RD bit */
132 *dnsp++ = '\0'; /* |RA| Z | RCODE | */
133 *dnsp++ = '\0';
134 *dnsp++ = 1; /* QDCOUNT (number of entries in the question section) */
135 *dnsp++ = '\0';
136 *dnsp++ = '\0'; /* ANCOUNT */
137 *dnsp++ = '\0';
138 *dnsp++ = '\0'; /* NSCOUNT */
139 *dnsp++ = '\0';
140 *dnsp++ = '\0'; /* ARCOUNT */
141
142 /* encode each label and store it in the QNAME */
143 while(*hostp) {
144 size_t labellen;
145 char *dot = strchr(hostp, '.');
146 if(dot)
147 labellen = dot - hostp;
148 else
149 labellen = strlen(hostp);
150 if((labellen > 63) || (!labellen)) {
151 /* label is too long or too short, error out */
152 *olen = 0;
153 return DOH_DNS_BAD_LABEL;
154 }
155 /* label is non-empty, process it */
156 *dnsp++ = (unsigned char)labellen;
157 memcpy(dnsp, hostp, labellen);
158 dnsp += labellen;
159 hostp += labellen;
160 /* advance past dot, but only if there is one */
161 if(dot)
162 hostp++;
163 } /* next label */
164
165 *dnsp++ = 0; /* append zero-length label for root */
166
167 /* There are assigned TYPE codes beyond 255: use range [1..65535] */
168 *dnsp++ = (unsigned char)(255 & (dnstype >> 8)); /* upper 8 bit TYPE */
169 *dnsp++ = (unsigned char)(255 & dnstype); /* lower 8 bit TYPE */
170
171 *dnsp++ = '\0'; /* upper 8 bit CLASS */
172 *dnsp++ = DNS_CLASS_IN; /* IN - "the Internet" */
173
174 *olen = dnsp - orig;
175
176 /* verify that our estimation of length is valid, since
177 * this has led to buffer overflows in this function */
178 DEBUGASSERT(*olen == expected_len);
179 return DOH_OK;
180 }
181
182 static size_t
doh_write_cb(char * contents,size_t size,size_t nmemb,void * userp)183 doh_write_cb(char *contents, size_t size, size_t nmemb, void *userp)
184 {
185 size_t realsize = size * nmemb;
186 struct dynbuf *mem = (struct dynbuf *)userp;
187
188 if(Curl_dyn_addn(mem, contents, realsize))
189 return 0;
190
191 return realsize;
192 }
193
194 #if defined(USE_HTTPSRR) && defined(DEBUGBUILD)
doh_print_buf(struct Curl_easy * data,const char * prefix,unsigned char * buf,size_t len)195 static void doh_print_buf(struct Curl_easy *data,
196 const char *prefix,
197 unsigned char *buf, size_t len)
198 {
199 unsigned char hexstr[LOCAL_PB_HEXMAX];
200 size_t hlen = LOCAL_PB_HEXMAX;
201 bool truncated = FALSE;
202
203 if(len > (LOCAL_PB_HEXMAX / 2))
204 truncated = TRUE;
205 Curl_hexencode(buf, len, hexstr, hlen);
206 if(!truncated)
207 infof(data, "%s: len=%d, val=%s", prefix, (int)len, hexstr);
208 else
209 infof(data, "%s: len=%d (truncated)val=%s", prefix, (int)len, hexstr);
210 return;
211 }
212 #endif
213
214 /* called from multi.c when this DoH transfer is complete */
doh_done(struct Curl_easy * doh,CURLcode result)215 static int doh_done(struct Curl_easy *doh, CURLcode result)
216 {
217 struct Curl_easy *data; /* the transfer that asked for the DoH probe */
218
219 data = Curl_multi_get_handle(doh->multi, doh->set.dohfor_mid);
220 if(!data) {
221 DEBUGF(infof(doh, "doh_done: xfer for mid=%" FMT_OFF_T
222 " not found", doh->set.dohfor_mid));
223 DEBUGASSERT(0);
224 }
225 else {
226 struct doh_probes *dohp = data->req.doh;
227 /* one of the DoH request done for the 'data' transfer is now complete! */
228 dohp->pending--;
229 infof(doh, "a DoH request is completed, %u to go", dohp->pending);
230 if(result)
231 infof(doh, "DoH request %s", curl_easy_strerror(result));
232
233 if(!dohp->pending) {
234 /* DoH completed, run the transfer picking up the results */
235 Curl_expire(data, 0, EXPIRE_RUN_NOW);
236 }
237 }
238 return 0;
239 }
240
241 #define ERROR_CHECK_SETOPT(x,y) \
242 do { \
243 result = curl_easy_setopt((CURL *)doh, x, y); \
244 if(result && \
245 result != CURLE_NOT_BUILT_IN && \
246 result != CURLE_UNKNOWN_OPTION) \
247 goto error; \
248 } while(0)
249
doh_run_probe(struct Curl_easy * data,struct doh_probe * p,DNStype dnstype,const char * host,const char * url,CURLM * multi,struct curl_slist * headers)250 static CURLcode doh_run_probe(struct Curl_easy *data,
251 struct doh_probe *p, DNStype dnstype,
252 const char *host,
253 const char *url, CURLM *multi,
254 struct curl_slist *headers)
255 {
256 struct Curl_easy *doh = NULL;
257 CURLcode result = CURLE_OK;
258 timediff_t timeout_ms;
259 DOHcode d = doh_req_encode(host, dnstype, p->req_body, sizeof(p->req_body),
260 &p->req_body_len);
261 if(d) {
262 failf(data, "Failed to encode DoH packet [%d]", d);
263 return CURLE_OUT_OF_MEMORY;
264 }
265
266 p->dnstype = dnstype;
267 Curl_dyn_init(&p->resp_body, DYN_DOH_RESPONSE);
268
269 timeout_ms = Curl_timeleft(data, NULL, TRUE);
270 if(timeout_ms <= 0) {
271 result = CURLE_OPERATION_TIMEDOUT;
272 goto error;
273 }
274 /* Curl_open() is the internal version of curl_easy_init() */
275 result = Curl_open(&doh);
276 if(result)
277 goto error;
278
279 /* pass in the struct pointer via a local variable to please coverity and
280 the gcc typecheck helpers */
281 doh->state.internal = TRUE;
282 #ifndef CURL_DISABLE_VERBOSE_STRINGS
283 doh->state.feat = &Curl_doh_trc;
284 #endif
285 ERROR_CHECK_SETOPT(CURLOPT_URL, url);
286 ERROR_CHECK_SETOPT(CURLOPT_DEFAULT_PROTOCOL, "https");
287 ERROR_CHECK_SETOPT(CURLOPT_WRITEFUNCTION, doh_write_cb);
288 ERROR_CHECK_SETOPT(CURLOPT_WRITEDATA, &p->resp_body);
289 ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDS, p->req_body);
290 ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDSIZE, (long)p->req_body_len);
291 ERROR_CHECK_SETOPT(CURLOPT_HTTPHEADER, headers);
292 #ifdef USE_HTTP2
293 ERROR_CHECK_SETOPT(CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS);
294 ERROR_CHECK_SETOPT(CURLOPT_PIPEWAIT, 1L);
295 #endif
296 #ifndef DEBUGBUILD
297 /* enforce HTTPS if not debug */
298 ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTPS);
299 #else
300 /* in debug mode, also allow http */
301 ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTP|CURLPROTO_HTTPS);
302 #endif
303 ERROR_CHECK_SETOPT(CURLOPT_TIMEOUT_MS, (long)timeout_ms);
304 ERROR_CHECK_SETOPT(CURLOPT_SHARE, (CURLSH *)data->share);
305 if(data->set.err && data->set.err != stderr)
306 ERROR_CHECK_SETOPT(CURLOPT_STDERR, data->set.err);
307 if(Curl_trc_ft_is_verbose(data, &Curl_doh_trc))
308 ERROR_CHECK_SETOPT(CURLOPT_VERBOSE, 1L);
309 if(data->set.no_signal)
310 ERROR_CHECK_SETOPT(CURLOPT_NOSIGNAL, 1L);
311
312 ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYHOST,
313 data->set.doh_verifyhost ? 2L : 0L);
314 ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYPEER,
315 data->set.doh_verifypeer ? 1L : 0L);
316 ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYSTATUS,
317 data->set.doh_verifystatus ? 1L : 0L);
318
319 /* Inherit *some* SSL options from the user's transfer. This is a
320 best-guess as to which options are needed for compatibility. #3661
321
322 Note DoH does not inherit the user's proxy server so proxy SSL settings
323 have no effect and are not inherited. If that changes then two new
324 options should be added to check doh proxy insecure separately,
325 CURLOPT_DOH_PROXY_SSL_VERIFYHOST and CURLOPT_DOH_PROXY_SSL_VERIFYPEER.
326 */
327 if(data->set.ssl.falsestart)
328 ERROR_CHECK_SETOPT(CURLOPT_SSL_FALSESTART, 1L);
329 if(data->set.str[STRING_SSL_CAFILE]) {
330 ERROR_CHECK_SETOPT(CURLOPT_CAINFO,
331 data->set.str[STRING_SSL_CAFILE]);
332 }
333 if(data->set.blobs[BLOB_CAINFO]) {
334 ERROR_CHECK_SETOPT(CURLOPT_CAINFO_BLOB,
335 data->set.blobs[BLOB_CAINFO]);
336 }
337 if(data->set.str[STRING_SSL_CAPATH]) {
338 ERROR_CHECK_SETOPT(CURLOPT_CAPATH,
339 data->set.str[STRING_SSL_CAPATH]);
340 }
341 if(data->set.str[STRING_SSL_CRLFILE]) {
342 ERROR_CHECK_SETOPT(CURLOPT_CRLFILE,
343 data->set.str[STRING_SSL_CRLFILE]);
344 }
345 if(data->set.ssl.certinfo)
346 ERROR_CHECK_SETOPT(CURLOPT_CERTINFO, 1L);
347 if(data->set.ssl.fsslctx)
348 ERROR_CHECK_SETOPT(CURLOPT_SSL_CTX_FUNCTION, data->set.ssl.fsslctx);
349 if(data->set.ssl.fsslctxp)
350 ERROR_CHECK_SETOPT(CURLOPT_SSL_CTX_DATA, data->set.ssl.fsslctxp);
351 if(data->set.fdebug)
352 ERROR_CHECK_SETOPT(CURLOPT_DEBUGFUNCTION, data->set.fdebug);
353 if(data->set.debugdata)
354 ERROR_CHECK_SETOPT(CURLOPT_DEBUGDATA, data->set.debugdata);
355 if(data->set.str[STRING_SSL_EC_CURVES]) {
356 ERROR_CHECK_SETOPT(CURLOPT_SSL_EC_CURVES,
357 data->set.str[STRING_SSL_EC_CURVES]);
358 }
359
360 {
361 long mask =
362 (data->set.ssl.enable_beast ?
363 CURLSSLOPT_ALLOW_BEAST : 0) |
364 (data->set.ssl.no_revoke ?
365 CURLSSLOPT_NO_REVOKE : 0) |
366 (data->set.ssl.no_partialchain ?
367 CURLSSLOPT_NO_PARTIALCHAIN : 0) |
368 (data->set.ssl.revoke_best_effort ?
369 CURLSSLOPT_REVOKE_BEST_EFFORT : 0) |
370 (data->set.ssl.native_ca_store ?
371 CURLSSLOPT_NATIVE_CA : 0) |
372 (data->set.ssl.auto_client_cert ?
373 CURLSSLOPT_AUTO_CLIENT_CERT : 0);
374
375 (void)curl_easy_setopt(doh, CURLOPT_SSL_OPTIONS, mask);
376 }
377
378 doh->set.fmultidone = doh_done;
379 doh->set.dohfor_mid = data->mid; /* for which transfer this is done */
380
381 /* DoH handles must not inherit private_data. The handles may be passed to
382 the user via callbacks and the user will be able to identify them as
383 internal handles because private data is not set. The user can then set
384 private_data via CURLOPT_PRIVATE if they so choose. */
385 DEBUGASSERT(!doh->set.private_data);
386
387 if(curl_multi_add_handle(multi, doh))
388 goto error;
389
390 p->easy_mid = doh->mid;
391 return CURLE_OK;
392
393 error:
394 Curl_close(&doh);
395 p->easy_mid = -1;
396 return result;
397 }
398
399 /*
400 * Curl_doh() resolves a name using DoH. It resolves a name and returns a
401 * 'Curl_addrinfo *' with the address information.
402 */
403
Curl_doh(struct Curl_easy * data,const char * hostname,int port,int * waitp)404 struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
405 const char *hostname,
406 int port,
407 int *waitp)
408 {
409 CURLcode result = CURLE_OK;
410 struct doh_probes *dohp;
411 struct connectdata *conn = data->conn;
412 size_t i;
413 *waitp = FALSE;
414 (void)hostname;
415 (void)port;
416
417 DEBUGASSERT(!data->req.doh);
418 DEBUGASSERT(conn);
419
420 /* start clean, consider allocating this struct on demand */
421 dohp = data->req.doh = calloc(1, sizeof(struct doh_probes));
422 if(!dohp)
423 return NULL;
424
425 for(i = 0; i < DOH_SLOT_COUNT; ++i) {
426 dohp->probe[i].easy_mid = -1;
427 }
428
429 conn->bits.doh = TRUE;
430 dohp->host = hostname;
431 dohp->port = port;
432 dohp->req_hds =
433 curl_slist_append(NULL,
434 "Content-Type: application/dns-message");
435 if(!dohp->req_hds)
436 goto error;
437
438 /* create IPv4 DoH request */
439 result = doh_run_probe(data, &dohp->probe[DOH_SLOT_IPV4],
440 DNS_TYPE_A, hostname, data->set.str[STRING_DOH],
441 data->multi, dohp->req_hds);
442 if(result)
443 goto error;
444 dohp->pending++;
445
446 #ifdef USE_IPV6
447 if((conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
448 /* create IPv6 DoH request */
449 result = doh_run_probe(data, &dohp->probe[DOH_SLOT_IPV6],
450 DNS_TYPE_AAAA, hostname, data->set.str[STRING_DOH],
451 data->multi, dohp->req_hds);
452 if(result)
453 goto error;
454 dohp->pending++;
455 }
456 #endif
457
458 #ifdef USE_HTTPSRR
459 if(conn->handler->protocol & PROTO_FAMILY_HTTP) {
460 /* Only use HTTPS RR for HTTP(S) transfers */
461 char *qname = NULL;
462 if(port != PORT_HTTPS) {
463 qname = aprintf("_%d._https.%s", port, hostname);
464 if(!qname)
465 goto error;
466 }
467 result = doh_run_probe(data, &dohp->probe[DOH_SLOT_HTTPS_RR],
468 DNS_TYPE_HTTPS,
469 qname ? qname : hostname, data->set.str[STRING_DOH],
470 data->multi, dohp->req_hds);
471 free(qname);
472 if(result)
473 goto error;
474 dohp->pending++;
475 }
476 #endif
477 *waitp = TRUE; /* this never returns synchronously */
478 return NULL;
479
480 error:
481 Curl_doh_cleanup(data);
482 return NULL;
483 }
484
doh_skipqname(const unsigned char * doh,size_t dohlen,unsigned int * indexp)485 static DOHcode doh_skipqname(const unsigned char *doh, size_t dohlen,
486 unsigned int *indexp)
487 {
488 unsigned char length;
489 do {
490 if(dohlen < (*indexp + 1))
491 return DOH_DNS_OUT_OF_RANGE;
492 length = doh[*indexp];
493 if((length & 0xc0) == 0xc0) {
494 /* name pointer, advance over it and be done */
495 if(dohlen < (*indexp + 2))
496 return DOH_DNS_OUT_OF_RANGE;
497 *indexp += 2;
498 break;
499 }
500 if(length & 0xc0)
501 return DOH_DNS_BAD_LABEL;
502 if(dohlen < (*indexp + 1 + length))
503 return DOH_DNS_OUT_OF_RANGE;
504 *indexp += (unsigned int)(1 + length);
505 } while(length);
506 return DOH_OK;
507 }
508
doh_get16bit(const unsigned char * doh,unsigned int index)509 static unsigned short doh_get16bit(const unsigned char *doh,
510 unsigned int index)
511 {
512 return (unsigned short)((doh[index] << 8) | doh[index + 1]);
513 }
514
doh_get32bit(const unsigned char * doh,unsigned int index)515 static unsigned int doh_get32bit(const unsigned char *doh, unsigned int index)
516 {
517 /* make clang and gcc optimize this to bswap by incrementing
518 the pointer first. */
519 doh += index;
520
521 /* avoid undefined behavior by casting to unsigned before shifting
522 24 bits, possibly into the sign bit. codegen is same, but
523 ub sanitizer will not be upset */
524 return ((unsigned)doh[0] << 24) | ((unsigned)doh[1] << 16) |
525 ((unsigned)doh[2] << 8) | doh[3];
526 }
527
doh_store_a(const unsigned char * doh,int index,struct dohentry * d)528 static void doh_store_a(const unsigned char *doh, int index,
529 struct dohentry *d)
530 {
531 /* silently ignore addresses over the limit */
532 if(d->numaddr < DOH_MAX_ADDR) {
533 struct dohaddr *a = &d->addr[d->numaddr];
534 a->type = DNS_TYPE_A;
535 memcpy(&a->ip.v4, &doh[index], 4);
536 d->numaddr++;
537 }
538 }
539
doh_store_aaaa(const unsigned char * doh,int index,struct dohentry * d)540 static void doh_store_aaaa(const unsigned char *doh, int index,
541 struct dohentry *d)
542 {
543 /* silently ignore addresses over the limit */
544 if(d->numaddr < DOH_MAX_ADDR) {
545 struct dohaddr *a = &d->addr[d->numaddr];
546 a->type = DNS_TYPE_AAAA;
547 memcpy(&a->ip.v6, &doh[index], 16);
548 d->numaddr++;
549 }
550 }
551
552 #ifdef USE_HTTPSRR
doh_store_https(const unsigned char * doh,int index,struct dohentry * d,uint16_t len)553 static DOHcode doh_store_https(const unsigned char *doh, int index,
554 struct dohentry *d, uint16_t len)
555 {
556 /* silently ignore RRs over the limit */
557 if(d->numhttps_rrs < DOH_MAX_HTTPS) {
558 struct dohhttps_rr *h = &d->https_rrs[d->numhttps_rrs];
559 h->val = Curl_memdup(&doh[index], len);
560 if(!h->val)
561 return DOH_OUT_OF_MEM;
562 h->len = len;
563 d->numhttps_rrs++;
564 }
565 return DOH_OK;
566 }
567 #endif
568
doh_store_cname(const unsigned char * doh,size_t dohlen,unsigned int index,struct dohentry * d)569 static DOHcode doh_store_cname(const unsigned char *doh, size_t dohlen,
570 unsigned int index, struct dohentry *d)
571 {
572 struct dynbuf *c;
573 unsigned int loop = 128; /* a valid DNS name can never loop this much */
574 unsigned char length;
575
576 if(d->numcname == DOH_MAX_CNAME)
577 return DOH_OK; /* skip! */
578
579 c = &d->cname[d->numcname++];
580 do {
581 if(index >= dohlen)
582 return DOH_DNS_OUT_OF_RANGE;
583 length = doh[index];
584 if((length & 0xc0) == 0xc0) {
585 int newpos;
586 /* name pointer, get the new offset (14 bits) */
587 if((index + 1) >= dohlen)
588 return DOH_DNS_OUT_OF_RANGE;
589
590 /* move to the new index */
591 newpos = (length & 0x3f) << 8 | doh[index + 1];
592 index = (unsigned int)newpos;
593 continue;
594 }
595 else if(length & 0xc0)
596 return DOH_DNS_BAD_LABEL; /* bad input */
597 else
598 index++;
599
600 if(length) {
601 if(Curl_dyn_len(c)) {
602 if(Curl_dyn_addn(c, STRCONST(".")))
603 return DOH_OUT_OF_MEM;
604 }
605 if((index + length) > dohlen)
606 return DOH_DNS_BAD_LABEL;
607
608 if(Curl_dyn_addn(c, &doh[index], length))
609 return DOH_OUT_OF_MEM;
610 index += length;
611 }
612 } while(length && --loop);
613
614 if(!loop)
615 return DOH_DNS_LABEL_LOOP;
616 return DOH_OK;
617 }
618
doh_rdata(const unsigned char * doh,size_t dohlen,unsigned short rdlength,unsigned short type,int index,struct dohentry * d)619 static DOHcode doh_rdata(const unsigned char *doh,
620 size_t dohlen,
621 unsigned short rdlength,
622 unsigned short type,
623 int index,
624 struct dohentry *d)
625 {
626 /* RDATA
627 - A (TYPE 1): 4 bytes
628 - AAAA (TYPE 28): 16 bytes
629 - NS (TYPE 2): N bytes
630 - HTTPS (TYPE 65): N bytes */
631 DOHcode rc;
632
633 switch(type) {
634 case DNS_TYPE_A:
635 if(rdlength != 4)
636 return DOH_DNS_RDATA_LEN;
637 doh_store_a(doh, index, d);
638 break;
639 case DNS_TYPE_AAAA:
640 if(rdlength != 16)
641 return DOH_DNS_RDATA_LEN;
642 doh_store_aaaa(doh, index, d);
643 break;
644 #ifdef USE_HTTPSRR
645 case DNS_TYPE_HTTPS:
646 rc = doh_store_https(doh, index, d, rdlength);
647 if(rc)
648 return rc;
649 break;
650 #endif
651 case DNS_TYPE_CNAME:
652 rc = doh_store_cname(doh, dohlen, (unsigned int)index, d);
653 if(rc)
654 return rc;
655 break;
656 case DNS_TYPE_DNAME:
657 /* explicit for clarity; just skip; rely on synthesized CNAME */
658 break;
659 default:
660 /* unsupported type, just skip it */
661 break;
662 }
663 return DOH_OK;
664 }
665
de_init(struct dohentry * de)666 UNITTEST void de_init(struct dohentry *de)
667 {
668 int i;
669 memset(de, 0, sizeof(*de));
670 de->ttl = INT_MAX;
671 for(i = 0; i < DOH_MAX_CNAME; i++)
672 Curl_dyn_init(&de->cname[i], DYN_DOH_CNAME);
673 }
674
675
doh_resp_decode(const unsigned char * doh,size_t dohlen,DNStype dnstype,struct dohentry * d)676 UNITTEST DOHcode doh_resp_decode(const unsigned char *doh,
677 size_t dohlen,
678 DNStype dnstype,
679 struct dohentry *d)
680 {
681 unsigned char rcode;
682 unsigned short qdcount;
683 unsigned short ancount;
684 unsigned short type = 0;
685 unsigned short rdlength;
686 unsigned short nscount;
687 unsigned short arcount;
688 unsigned int index = 12;
689 DOHcode rc;
690
691 if(dohlen < 12)
692 return DOH_TOO_SMALL_BUFFER; /* too small */
693 if(!doh || doh[0] || doh[1])
694 return DOH_DNS_BAD_ID; /* bad ID */
695 rcode = doh[3] & 0x0f;
696 if(rcode)
697 return DOH_DNS_BAD_RCODE; /* bad rcode */
698
699 qdcount = doh_get16bit(doh, 4);
700 while(qdcount) {
701 rc = doh_skipqname(doh, dohlen, &index);
702 if(rc)
703 return rc; /* bad qname */
704 if(dohlen < (index + 4))
705 return DOH_DNS_OUT_OF_RANGE;
706 index += 4; /* skip question's type and class */
707 qdcount--;
708 }
709
710 ancount = doh_get16bit(doh, 6);
711 while(ancount) {
712 unsigned short class;
713 unsigned int ttl;
714
715 rc = doh_skipqname(doh, dohlen, &index);
716 if(rc)
717 return rc; /* bad qname */
718
719 if(dohlen < (index + 2))
720 return DOH_DNS_OUT_OF_RANGE;
721
722 type = doh_get16bit(doh, index);
723 if((type != DNS_TYPE_CNAME) /* may be synthesized from DNAME */
724 && (type != DNS_TYPE_DNAME) /* if present, accept and ignore */
725 && (type != dnstype))
726 /* Not the same type as was asked for nor CNAME nor DNAME */
727 return DOH_DNS_UNEXPECTED_TYPE;
728 index += 2;
729
730 if(dohlen < (index + 2))
731 return DOH_DNS_OUT_OF_RANGE;
732 class = doh_get16bit(doh, index);
733 if(DNS_CLASS_IN != class)
734 return DOH_DNS_UNEXPECTED_CLASS; /* unsupported */
735 index += 2;
736
737 if(dohlen < (index + 4))
738 return DOH_DNS_OUT_OF_RANGE;
739
740 ttl = doh_get32bit(doh, index);
741 if(ttl < d->ttl)
742 d->ttl = ttl;
743 index += 4;
744
745 if(dohlen < (index + 2))
746 return DOH_DNS_OUT_OF_RANGE;
747
748 rdlength = doh_get16bit(doh, index);
749 index += 2;
750 if(dohlen < (index + rdlength))
751 return DOH_DNS_OUT_OF_RANGE;
752
753 rc = doh_rdata(doh, dohlen, rdlength, type, (int)index, d);
754 if(rc)
755 return rc; /* bad doh_rdata */
756 index += rdlength;
757 ancount--;
758 }
759
760 nscount = doh_get16bit(doh, 8);
761 while(nscount) {
762 rc = doh_skipqname(doh, dohlen, &index);
763 if(rc)
764 return rc; /* bad qname */
765
766 if(dohlen < (index + 8))
767 return DOH_DNS_OUT_OF_RANGE;
768
769 index += 2 + 2 + 4; /* type, class and ttl */
770
771 if(dohlen < (index + 2))
772 return DOH_DNS_OUT_OF_RANGE;
773
774 rdlength = doh_get16bit(doh, index);
775 index += 2;
776 if(dohlen < (index + rdlength))
777 return DOH_DNS_OUT_OF_RANGE;
778 index += rdlength;
779 nscount--;
780 }
781
782 arcount = doh_get16bit(doh, 10);
783 while(arcount) {
784 rc = doh_skipqname(doh, dohlen, &index);
785 if(rc)
786 return rc; /* bad qname */
787
788 if(dohlen < (index + 8))
789 return DOH_DNS_OUT_OF_RANGE;
790
791 index += 2 + 2 + 4; /* type, class and ttl */
792
793 if(dohlen < (index + 2))
794 return DOH_DNS_OUT_OF_RANGE;
795
796 rdlength = doh_get16bit(doh, index);
797 index += 2;
798 if(dohlen < (index + rdlength))
799 return DOH_DNS_OUT_OF_RANGE;
800 index += rdlength;
801 arcount--;
802 }
803
804 if(index != dohlen)
805 return DOH_DNS_MALFORMAT; /* something is wrong */
806
807 #ifdef USE_HTTTPS
808 if((type != DNS_TYPE_NS) && !d->numcname && !d->numaddr && !d->numhttps_rrs)
809 #else
810 if((type != DNS_TYPE_NS) && !d->numcname && !d->numaddr)
811 #endif
812 /* nothing stored! */
813 return DOH_NO_CONTENT;
814
815 return DOH_OK; /* ok */
816 }
817
818 #ifndef CURL_DISABLE_VERBOSE_STRINGS
doh_show(struct Curl_easy * data,const struct dohentry * d)819 static void doh_show(struct Curl_easy *data,
820 const struct dohentry *d)
821 {
822 int i;
823 infof(data, "[DoH] TTL: %u seconds", d->ttl);
824 for(i = 0; i < d->numaddr; i++) {
825 const struct dohaddr *a = &d->addr[i];
826 if(a->type == DNS_TYPE_A) {
827 infof(data, "[DoH] A: %u.%u.%u.%u",
828 a->ip.v4[0], a->ip.v4[1],
829 a->ip.v4[2], a->ip.v4[3]);
830 }
831 else if(a->type == DNS_TYPE_AAAA) {
832 int j;
833 char buffer[128] = "[DoH] AAAA: ";
834 size_t len = strlen(buffer);
835 char *ptr = &buffer[len];
836 len = sizeof(buffer) - len;
837 for(j = 0; j < 16; j += 2) {
838 size_t l;
839 msnprintf(ptr, len, "%s%02x%02x", j ? ":" : "", d->addr[i].ip.v6[j],
840 d->addr[i].ip.v6[j + 1]);
841 l = strlen(ptr);
842 len -= l;
843 ptr += l;
844 }
845 infof(data, "%s", buffer);
846 }
847 }
848 #ifdef USE_HTTPSRR
849 for(i = 0; i < d->numhttps_rrs; i++) {
850 # ifdef DEBUGBUILD
851 doh_print_buf(data, "DoH HTTPS",
852 d->https_rrs[i].val, d->https_rrs[i].len);
853 # else
854 infof(data, "DoH HTTPS RR: length %d", d->https_rrs[i].len);
855 # endif
856 }
857 #endif
858 for(i = 0; i < d->numcname; i++) {
859 infof(data, "CNAME: %s", Curl_dyn_ptr(&d->cname[i]));
860 }
861 }
862 #else
863 #define doh_show(x,y)
864 #endif
865
866 /*
867 * doh2ai()
868 *
869 * This function returns a pointer to the first element of a newly allocated
870 * Curl_addrinfo struct linked list filled with the data from a set of DoH
871 * lookups. Curl_addrinfo is meant to work like the addrinfo struct does for
872 * a IPv6 stack, but usable also for IPv4, all hosts and environments.
873 *
874 * The memory allocated by this function *MUST* be free'd later on calling
875 * Curl_freeaddrinfo(). For each successful call to this function there
876 * must be an associated call later to Curl_freeaddrinfo().
877 */
878
doh2ai(const struct dohentry * de,const char * hostname,int port,struct Curl_addrinfo ** aip)879 static CURLcode doh2ai(const struct dohentry *de, const char *hostname,
880 int port, struct Curl_addrinfo **aip)
881 {
882 struct Curl_addrinfo *ai;
883 struct Curl_addrinfo *prevai = NULL;
884 struct Curl_addrinfo *firstai = NULL;
885 struct sockaddr_in *addr;
886 #ifdef USE_IPV6
887 struct sockaddr_in6 *addr6;
888 #endif
889 CURLcode result = CURLE_OK;
890 int i;
891 size_t hostlen = strlen(hostname) + 1; /* include null-terminator */
892
893 DEBUGASSERT(de);
894
895 if(!de->numaddr)
896 return CURLE_COULDNT_RESOLVE_HOST;
897
898 for(i = 0; i < de->numaddr; i++) {
899 size_t ss_size;
900 CURL_SA_FAMILY_T addrtype;
901 if(de->addr[i].type == DNS_TYPE_AAAA) {
902 #ifndef USE_IPV6
903 /* we cannot handle IPv6 addresses */
904 continue;
905 #else
906 ss_size = sizeof(struct sockaddr_in6);
907 addrtype = AF_INET6;
908 #endif
909 }
910 else {
911 ss_size = sizeof(struct sockaddr_in);
912 addrtype = AF_INET;
913 }
914
915 ai = calloc(1, sizeof(struct Curl_addrinfo) + ss_size + hostlen);
916 if(!ai) {
917 result = CURLE_OUT_OF_MEMORY;
918 break;
919 }
920 ai->ai_addr = (void *)((char *)ai + sizeof(struct Curl_addrinfo));
921 ai->ai_canonname = (void *)((char *)ai->ai_addr + ss_size);
922 memcpy(ai->ai_canonname, hostname, hostlen);
923
924 if(!firstai)
925 /* store the pointer we want to return from this function */
926 firstai = ai;
927
928 if(prevai)
929 /* make the previous entry point to this */
930 prevai->ai_next = ai;
931
932 ai->ai_family = addrtype;
933
934 /* we return all names as STREAM, so when using this address for TFTP
935 the type must be ignored and conn->socktype be used instead! */
936 ai->ai_socktype = SOCK_STREAM;
937
938 ai->ai_addrlen = (curl_socklen_t)ss_size;
939
940 /* leave the rest of the struct filled with zero */
941
942 switch(ai->ai_family) {
943 case AF_INET:
944 addr = (void *)ai->ai_addr; /* storage area for this info */
945 DEBUGASSERT(sizeof(struct in_addr) == sizeof(de->addr[i].ip.v4));
946 memcpy(&addr->sin_addr, &de->addr[i].ip.v4, sizeof(struct in_addr));
947 addr->sin_family = (CURL_SA_FAMILY_T)addrtype;
948 addr->sin_port = htons((unsigned short)port);
949 break;
950
951 #ifdef USE_IPV6
952 case AF_INET6:
953 addr6 = (void *)ai->ai_addr; /* storage area for this info */
954 DEBUGASSERT(sizeof(struct in6_addr) == sizeof(de->addr[i].ip.v6));
955 memcpy(&addr6->sin6_addr, &de->addr[i].ip.v6, sizeof(struct in6_addr));
956 addr6->sin6_family = (CURL_SA_FAMILY_T)addrtype;
957 addr6->sin6_port = htons((unsigned short)port);
958 break;
959 #endif
960 }
961
962 prevai = ai;
963 }
964
965 if(result) {
966 Curl_freeaddrinfo(firstai);
967 firstai = NULL;
968 }
969 *aip = firstai;
970
971 return result;
972 }
973
974 #ifndef CURL_DISABLE_VERBOSE_STRINGS
doh_type2name(DNStype dnstype)975 static const char *doh_type2name(DNStype dnstype)
976 {
977 switch(dnstype) {
978 case DNS_TYPE_A:
979 return "A";
980 case DNS_TYPE_AAAA:
981 return "AAAA";
982 #ifdef USE_HTTPSRR
983 case DNS_TYPE_HTTPS:
984 return "HTTPS";
985 #endif
986 default:
987 return "unknown";
988 }
989 }
990 #endif
991
de_cleanup(struct dohentry * d)992 UNITTEST void de_cleanup(struct dohentry *d)
993 {
994 int i = 0;
995 for(i = 0; i < d->numcname; i++) {
996 Curl_dyn_free(&d->cname[i]);
997 }
998 #ifdef USE_HTTPSRR
999 for(i = 0; i < d->numhttps_rrs; i++)
1000 Curl_safefree(d->https_rrs[i].val);
1001 #endif
1002 }
1003
1004 #ifdef USE_HTTPSRR
1005
1006 /*
1007 * @brief decode the DNS name in a binary RRData
1008 * @param buf points to the buffer (in/out)
1009 * @param remaining points to the remaining buffer length (in/out)
1010 * @param dnsname returns the string form name on success
1011 * @return is 1 for success, error otherwise
1012 *
1013 * The encoding here is defined in
1014 * https://tools.ietf.org/html/rfc1035#section-3.1
1015 *
1016 * The input buffer pointer will be modified so it points to
1017 * just after the end of the DNS name encoding on output. (And
1018 * that is why it is an "unsigned char **" :-)
1019 */
doh_decode_rdata_name(unsigned char ** buf,size_t * remaining,char ** dnsname)1020 static CURLcode doh_decode_rdata_name(unsigned char **buf, size_t *remaining,
1021 char **dnsname)
1022 {
1023 unsigned char *cp = NULL;
1024 int rem = 0;
1025 unsigned char clen = 0; /* chunk len */
1026 struct dynbuf thename;
1027
1028 DEBUGASSERT(buf && remaining && dnsname);
1029 if(!buf || !remaining || !dnsname)
1030 return CURLE_OUT_OF_MEMORY;
1031 rem = (int)*remaining;
1032 if(rem <= 0) {
1033 Curl_dyn_free(&thename);
1034 return CURLE_OUT_OF_MEMORY;
1035 }
1036 Curl_dyn_init(&thename, CURL_MAXLEN_host_name);
1037 cp = *buf;
1038 clen = *cp++;
1039 if(clen == 0) {
1040 /* special case - return "." as name */
1041 if(Curl_dyn_addn(&thename, ".", 1))
1042 return CURLE_OUT_OF_MEMORY;
1043 }
1044 while(clen) {
1045 if(clen >= rem) {
1046 Curl_dyn_free(&thename);
1047 return CURLE_OUT_OF_MEMORY;
1048 }
1049 if(Curl_dyn_addn(&thename, cp, clen) ||
1050 Curl_dyn_addn(&thename, ".", 1))
1051 return CURLE_TOO_LARGE;
1052
1053 cp += clen;
1054 rem -= (clen + 1);
1055 if(rem <= 0) {
1056 Curl_dyn_free(&thename);
1057 return CURLE_OUT_OF_MEMORY;
1058 }
1059 clen = *cp++;
1060 }
1061 *buf = cp;
1062 *remaining = rem - 1;
1063 *dnsname = Curl_dyn_ptr(&thename);
1064 return CURLE_OK;
1065 }
1066
1067 #ifdef DEBUGBUILD
doh_test_alpn_escapes(void)1068 static CURLcode doh_test_alpn_escapes(void)
1069 {
1070 /* we will use an example from draft-ietf-dnsop-svcb, figure 10 */
1071 static unsigned char example[] = {
1072 0x08, /* length 8 */
1073 0x66, 0x5c, 0x6f, 0x6f, 0x2c, 0x62, 0x61, 0x72, /* value "f\\oo,bar" */
1074 0x02, /* length 2 */
1075 0x68, 0x32 /* value "h2" */
1076 };
1077 size_t example_len = sizeof(example);
1078 unsigned char aval[MAX_HTTPSRR_ALPNS] = { 0 };
1079 static const char expected[2] = { ALPN_h2, ALPN_none };
1080
1081 if(Curl_httpsrr_decode_alpn(example, example_len, aval) != CURLE_OK)
1082 return CURLE_BAD_CONTENT_ENCODING;
1083 if(memcmp(aval, expected, sizeof(expected)))
1084 return CURLE_BAD_CONTENT_ENCODING;
1085 return CURLE_OK;
1086 }
1087 #endif
1088
doh_resp_decode_httpsrr(unsigned char * cp,size_t len,struct Curl_https_rrinfo ** hrr)1089 static CURLcode doh_resp_decode_httpsrr(unsigned char *cp, size_t len,
1090 struct Curl_https_rrinfo **hrr)
1091 {
1092 uint16_t pcode = 0, plen = 0;
1093 struct Curl_https_rrinfo *lhrr = NULL;
1094 char *dnsname = NULL;
1095
1096 #ifdef DEBUGBUILD
1097 /* a few tests of escaping, should not be here but ok for now */
1098 if(doh_test_alpn_escapes() != CURLE_OK)
1099 return CURLE_OUT_OF_MEMORY;
1100 #endif
1101 if(len <= 2)
1102 return CURLE_BAD_FUNCTION_ARGUMENT;
1103 lhrr = calloc(1, sizeof(struct Curl_https_rrinfo));
1104 if(!lhrr)
1105 return CURLE_OUT_OF_MEMORY;
1106 lhrr->priority = doh_get16bit(cp, 0);
1107 cp += 2;
1108 len -= 2;
1109 if(doh_decode_rdata_name(&cp, &len, &dnsname) != CURLE_OK)
1110 goto err;
1111 lhrr->target = dnsname;
1112 lhrr->port = -1; /* until set */
1113 while(len >= 4) {
1114 pcode = doh_get16bit(cp, 0);
1115 plen = doh_get16bit(cp, 2);
1116 cp += 4;
1117 len -= 4;
1118 switch(pcode) {
1119 case HTTPS_RR_CODE_ALPN:
1120 if(Curl_httpsrr_decode_alpn(cp, plen, lhrr->alpns) != CURLE_OK)
1121 goto err;
1122 break;
1123 case HTTPS_RR_CODE_NO_DEF_ALPN:
1124 lhrr->no_def_alpn = TRUE;
1125 break;
1126 case HTTPS_RR_CODE_IPV4:
1127 if(!plen)
1128 goto err;
1129 lhrr->ipv4hints = Curl_memdup(cp, plen);
1130 if(!lhrr->ipv4hints)
1131 goto err;
1132 lhrr->ipv4hints_len = (size_t)plen;
1133 break;
1134 case HTTPS_RR_CODE_ECH:
1135 if(!plen)
1136 goto err;
1137 lhrr->echconfiglist = Curl_memdup(cp, plen);
1138 if(!lhrr->echconfiglist)
1139 goto err;
1140 lhrr->echconfiglist_len = (size_t)plen;
1141 break;
1142 case HTTPS_RR_CODE_IPV6:
1143 if(!plen)
1144 goto err;
1145 lhrr->ipv6hints = Curl_memdup(cp, plen);
1146 if(!lhrr->ipv6hints)
1147 goto err;
1148 lhrr->ipv6hints_len = (size_t)plen;
1149 break;
1150 case HTTPS_RR_CODE_PORT:
1151 lhrr->port = doh_get16bit(cp, 0);
1152 break;
1153 default:
1154 break;
1155 }
1156 if(plen > 0 && plen <= len) {
1157 cp += plen;
1158 len -= plen;
1159 }
1160 }
1161 DEBUGASSERT(!len);
1162 *hrr = lhrr;
1163 return CURLE_OK;
1164 err:
1165 Curl_safefree(lhrr->target);
1166 Curl_safefree(lhrr->echconfiglist);
1167 Curl_safefree(lhrr);
1168 return CURLE_OUT_OF_MEMORY;
1169 }
1170
1171 # ifdef DEBUGBUILD
doh_print_httpsrr(struct Curl_easy * data,struct Curl_https_rrinfo * hrr)1172 static void doh_print_httpsrr(struct Curl_easy *data,
1173 struct Curl_https_rrinfo *hrr)
1174 {
1175 DEBUGASSERT(hrr);
1176 infof(data, "HTTPS RR: priority %d, target: %s",
1177 hrr->priority, hrr->target);
1178 if(hrr->alpns[0] != ALPN_none)
1179 infof(data, "HTTPS RR: alpns %u %u %u %u",
1180 hrr->alpns[0], hrr->alpns[1], hrr->alpns[2], hrr->alpns[3]);
1181 else
1182 infof(data, "HTTPS RR: no alpns");
1183 if(hrr->no_def_alpn)
1184 infof(data, "HTTPS RR: no_def_alpn set");
1185 else
1186 infof(data, "HTTPS RR: no_def_alpn not set");
1187 if(hrr->ipv4hints) {
1188 doh_print_buf(data, "HTTPS RR: ipv4hints",
1189 hrr->ipv4hints, hrr->ipv4hints_len);
1190 }
1191 else
1192 infof(data, "HTTPS RR: no ipv4hints");
1193 if(hrr->echconfiglist) {
1194 doh_print_buf(data, "HTTPS RR: ECHConfigList",
1195 hrr->echconfiglist, hrr->echconfiglist_len);
1196 }
1197 else
1198 infof(data, "HTTPS RR: no ECHConfigList");
1199 if(hrr->ipv6hints) {
1200 doh_print_buf(data, "HTTPS RR: ipv6hint",
1201 hrr->ipv6hints, hrr->ipv6hints_len);
1202 }
1203 else
1204 infof(data, "HTTPS RR: no ipv6hints");
1205 return;
1206 }
1207 # endif
1208 #endif
1209
Curl_doh_is_resolved(struct Curl_easy * data,struct Curl_dns_entry ** dnsp)1210 CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
1211 struct Curl_dns_entry **dnsp)
1212 {
1213 CURLcode result;
1214 struct doh_probes *dohp = data->req.doh;
1215 *dnsp = NULL; /* defaults to no response */
1216 if(!dohp)
1217 return CURLE_OUT_OF_MEMORY;
1218
1219 if(dohp->probe[DOH_SLOT_IPV4].easy_mid < 0 &&
1220 dohp->probe[DOH_SLOT_IPV6].easy_mid < 0) {
1221 failf(data, "Could not DoH-resolve: %s", data->state.async.hostname);
1222 return CONN_IS_PROXIED(data->conn) ? CURLE_COULDNT_RESOLVE_PROXY :
1223 CURLE_COULDNT_RESOLVE_HOST;
1224 }
1225 else if(!dohp->pending) {
1226 DOHcode rc[DOH_SLOT_COUNT];
1227 struct dohentry de;
1228 int slot;
1229
1230 memset(rc, 0, sizeof(rc));
1231 /* remove DoH handles from multi handle and close them */
1232 Curl_doh_close(data);
1233 /* parse the responses, create the struct and return it! */
1234 de_init(&de);
1235 for(slot = 0; slot < DOH_SLOT_COUNT; slot++) {
1236 struct doh_probe *p = &dohp->probe[slot];
1237 if(!p->dnstype)
1238 continue;
1239 rc[slot] = doh_resp_decode(Curl_dyn_uptr(&p->resp_body),
1240 Curl_dyn_len(&p->resp_body),
1241 p->dnstype, &de);
1242 Curl_dyn_free(&p->resp_body);
1243 #ifndef CURL_DISABLE_VERBOSE_STRINGS
1244 if(rc[slot]) {
1245 infof(data, "DoH: %s type %s for %s", doh_strerror(rc[slot]),
1246 doh_type2name(p->dnstype), dohp->host);
1247 }
1248 #endif
1249 } /* next slot */
1250
1251 result = CURLE_COULDNT_RESOLVE_HOST; /* until we know better */
1252 if(!rc[DOH_SLOT_IPV4] || !rc[DOH_SLOT_IPV6]) {
1253 /* we have an address, of one kind or other */
1254 struct Curl_dns_entry *dns;
1255 struct Curl_addrinfo *ai;
1256
1257
1258 if(Curl_trc_ft_is_verbose(data, &Curl_doh_trc)) {
1259 infof(data, "[DoH] hostname: %s", dohp->host);
1260 doh_show(data, &de);
1261 }
1262
1263 result = doh2ai(&de, dohp->host, dohp->port, &ai);
1264 if(result) {
1265 de_cleanup(&de);
1266 return result;
1267 }
1268
1269 if(data->share)
1270 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
1271
1272 /* we got a response, store it in the cache */
1273 dns = Curl_cache_addr(data, ai, dohp->host, 0, dohp->port, FALSE);
1274
1275 if(data->share)
1276 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
1277
1278 if(!dns) {
1279 /* returned failure, bail out nicely */
1280 Curl_freeaddrinfo(ai);
1281 }
1282 else {
1283 data->state.async.dns = dns;
1284 *dnsp = dns;
1285 result = CURLE_OK; /* address resolution OK */
1286 }
1287 } /* address processing done */
1288
1289 /* Now process any build-specific attributes retrieved from DNS */
1290 #ifdef USE_HTTPSRR
1291 if(de.numhttps_rrs > 0 && result == CURLE_OK && *dnsp) {
1292 struct Curl_https_rrinfo *hrr = NULL;
1293 result = doh_resp_decode_httpsrr(de.https_rrs->val, de.https_rrs->len,
1294 &hrr);
1295 if(result) {
1296 infof(data, "Failed to decode HTTPS RR");
1297 return result;
1298 }
1299 infof(data, "Some HTTPS RR to process");
1300 # ifdef DEBUGBUILD
1301 doh_print_httpsrr(data, hrr);
1302 # endif
1303 (*dnsp)->hinfo = hrr;
1304 }
1305 #endif
1306
1307 /* All done */
1308 de_cleanup(&de);
1309 Curl_doh_cleanup(data);
1310 return result;
1311
1312 } /* !dohp->pending */
1313
1314 /* else wait for pending DoH transactions to complete */
1315 return CURLE_OK;
1316 }
1317
Curl_doh_close(struct Curl_easy * data)1318 void Curl_doh_close(struct Curl_easy *data)
1319 {
1320 struct doh_probes *doh = data->req.doh;
1321 if(doh && data->multi) {
1322 struct Curl_easy *probe_data;
1323 curl_off_t mid;
1324 size_t slot;
1325 for(slot = 0; slot < DOH_SLOT_COUNT; slot++) {
1326 mid = doh->probe[slot].easy_mid;
1327 if(mid < 0)
1328 continue;
1329 doh->probe[slot].easy_mid = -1;
1330 /* should have been called before data is removed from multi handle */
1331 DEBUGASSERT(data->multi);
1332 probe_data = data->multi ? Curl_multi_get_handle(data->multi, mid) :
1333 NULL;
1334 if(!probe_data) {
1335 DEBUGF(infof(data, "Curl_doh_close: xfer for mid=%"
1336 FMT_OFF_T " not found!",
1337 doh->probe[slot].easy_mid));
1338 continue;
1339 }
1340 /* data->multi might already be reset at this time */
1341 curl_multi_remove_handle(data->multi, probe_data);
1342 Curl_close(&probe_data);
1343 }
1344 }
1345 }
1346
Curl_doh_cleanup(struct Curl_easy * data)1347 void Curl_doh_cleanup(struct Curl_easy *data)
1348 {
1349 struct doh_probes *doh = data->req.doh;
1350 if(doh) {
1351 Curl_doh_close(data);
1352 curl_slist_free_all(doh->req_hds);
1353 data->req.doh->req_hds = NULL;
1354 Curl_safefree(data->req.doh);
1355 }
1356 }
1357
1358 #endif /* CURL_DISABLE_DOH */
1359