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