1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 2018 - 2019, Daniel Stenberg, <daniel (at) 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.haxx.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 ***************************************************************************/ 22 23 #include "curl_setup.h" 24 25 #include "urldata.h" 26 #include "curl_addrinfo.h" 27 #include "doh.h" 28 29 #include "sendf.h" 30 #include "multiif.h" 31 #include "url.h" 32 #include "share.h" 33 #include "curl_base64.h" 34 #include "connect.h" 35 #include "strdup.h" 36 /* The last 3 #include files should be in this order */ 37 #include "curl_printf.h" 38 #include "curl_memory.h" 39 #include "memdebug.h" 40 41 #define DNS_CLASS_IN 0x01 42 #define DOH_MAX_RESPONSE_SIZE 3000 /* bytes */ 43 44 #ifndef CURL_DISABLE_VERBOSE_STRINGS 45 static const char * const errors[]={ 46 "", 47 "Bad label", 48 "Out of range", 49 "Label loop", 50 "Too small", 51 "Out of memory", 52 "RDATA length", 53 "Malformat", 54 "Bad RCODE", 55 "Unexpected TYPE", 56 "Unexpected CLASS", 57 "No content", 58 "Bad ID" 59 }; 60 61 static const char *doh_strerror(DOHcode code) 62 { 63 if((code >= DOH_OK) && (code <= DOH_DNS_BAD_ID)) 64 return errors[code]; 65 return "bad error code"; 66 } 67 #endif 68 69 #ifdef DEBUGBUILD 70 #define UNITTEST 71 #else 72 #define UNITTEST static 73 #endif 74 75 UNITTEST DOHcode doh_encode(const char *host, 76 DNStype dnstype, 77 unsigned char *dnsp, /* buffer */ 78 size_t len, /* buffer size */ 79 size_t *olen) /* output length */ 80 { 81 size_t hostlen = strlen(host); 82 unsigned char *orig = dnsp; 83 const char *hostp = host; 84 85 if(len < (12 + hostlen + 4)) 86 return DOH_TOO_SMALL_BUFFER; 87 88 *dnsp++ = 0; /* 16 bit id */ 89 *dnsp++ = 0; 90 *dnsp++ = 0x01; /* |QR| Opcode |AA|TC|RD| Set the RD bit */ 91 *dnsp++ = '\0'; /* |RA| Z | RCODE | */ 92 *dnsp++ = '\0'; 93 *dnsp++ = 1; /* QDCOUNT (number of entries in the question section) */ 94 *dnsp++ = '\0'; 95 *dnsp++ = '\0'; /* ANCOUNT */ 96 *dnsp++ = '\0'; 97 *dnsp++ = '\0'; /* NSCOUNT */ 98 *dnsp++ = '\0'; 99 *dnsp++ = '\0'; /* ARCOUNT */ 100 101 /* store a QNAME */ 102 do { 103 char *dot = strchr(hostp, '.'); 104 size_t labellen; 105 bool found = false; 106 if(dot) { 107 found = true; 108 labellen = dot - hostp; 109 } 110 else 111 labellen = strlen(hostp); 112 if(labellen > 63) { 113 /* too long label, error out */ 114 *olen = 0; 115 return DOH_DNS_BAD_LABEL; 116 } 117 *dnsp++ = (unsigned char)labellen; 118 memcpy(dnsp, hostp, labellen); 119 dnsp += labellen; 120 hostp += labellen + 1; 121 if(!found) { 122 *dnsp++ = 0; /* terminating zero */ 123 break; 124 } 125 } while(1); 126 127 *dnsp++ = '\0'; /* upper 8 bit TYPE */ 128 *dnsp++ = (unsigned char)dnstype; 129 *dnsp++ = '\0'; /* upper 8 bit CLASS */ 130 *dnsp++ = DNS_CLASS_IN; /* IN - "the Internet" */ 131 132 *olen = dnsp - orig; 133 return DOH_OK; 134 } 135 136 static size_t 137 doh_write_cb(void *contents, size_t size, size_t nmemb, void *userp) 138 { 139 size_t realsize = size * nmemb; 140 struct dohresponse *mem = (struct dohresponse *)userp; 141 142 if((mem->size + realsize) > DOH_MAX_RESPONSE_SIZE) 143 /* suspiciously much for us */ 144 return 0; 145 146 mem->memory = Curl_saferealloc(mem->memory, mem->size + realsize); 147 if(!mem->memory) 148 /* out of memory! */ 149 return 0; 150 151 memcpy(&(mem->memory[mem->size]), contents, realsize); 152 mem->size += realsize; 153 154 return realsize; 155 } 156 157 /* called from multi.c when this DOH transfer is complete */ 158 static int Curl_doh_done(struct Curl_easy *doh, CURLcode result) 159 { 160 struct Curl_easy *data = doh->set.dohfor; 161 /* so one of the DOH request done for the 'data' transfer is now complete! */ 162 data->req.doh.pending--; 163 infof(data, "a DOH request is completed, %u to go\n", data->req.doh.pending); 164 if(result) 165 infof(data, "DOH request %s\n", curl_easy_strerror(result)); 166 167 if(!data->req.doh.pending) { 168 /* DOH completed */ 169 curl_slist_free_all(data->req.doh.headers); 170 data->req.doh.headers = NULL; 171 Curl_expire(data, 0, EXPIRE_RUN_NOW); 172 } 173 return 0; 174 } 175 176 #define ERROR_CHECK_SETOPT(x,y) \ 177 do { \ 178 result = curl_easy_setopt(doh, x, y); \ 179 if(result) \ 180 goto error; \ 181 } WHILE_FALSE 182 183 static CURLcode dohprobe(struct Curl_easy *data, 184 struct dnsprobe *p, DNStype dnstype, 185 const char *host, 186 const char *url, CURLM *multi, 187 struct curl_slist *headers) 188 { 189 struct Curl_easy *doh = NULL; 190 char *nurl = NULL; 191 CURLcode result = CURLE_OK; 192 timediff_t timeout_ms; 193 DOHcode d = doh_encode(host, dnstype, p->dohbuffer, sizeof(p->dohbuffer), 194 &p->dohlen); 195 if(d) { 196 failf(data, "Failed to encode DOH packet [%d]\n", d); 197 return CURLE_OUT_OF_MEMORY; 198 } 199 200 p->dnstype = dnstype; 201 p->serverdoh.memory = NULL; 202 /* the memory will be grown as needed by realloc in the doh_write_cb 203 function */ 204 p->serverdoh.size = 0; 205 206 /* Note: this is code for sending the DoH request with GET but there's still 207 no logic that actually enables this. We should either add that ability or 208 yank out the GET code. Discuss! */ 209 if(data->set.doh_get) { 210 char *b64; 211 size_t b64len; 212 result = Curl_base64url_encode(data, (char *)p->dohbuffer, p->dohlen, 213 &b64, &b64len); 214 if(result) 215 goto error; 216 nurl = aprintf("%s?dns=%s", url, b64); 217 free(b64); 218 if(!nurl) { 219 result = CURLE_OUT_OF_MEMORY; 220 goto error; 221 } 222 url = nurl; 223 } 224 225 timeout_ms = Curl_timeleft(data, NULL, TRUE); 226 227 /* Curl_open() is the internal version of curl_easy_init() */ 228 result = Curl_open(&doh); 229 if(!result) { 230 /* pass in the struct pointer via a local variable to please coverity and 231 the gcc typecheck helpers */ 232 struct dohresponse *resp = &p->serverdoh; 233 ERROR_CHECK_SETOPT(CURLOPT_URL, url); 234 ERROR_CHECK_SETOPT(CURLOPT_WRITEFUNCTION, doh_write_cb); 235 ERROR_CHECK_SETOPT(CURLOPT_WRITEDATA, resp); 236 if(!data->set.doh_get) { 237 ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDS, p->dohbuffer); 238 ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDSIZE, (long)p->dohlen); 239 } 240 ERROR_CHECK_SETOPT(CURLOPT_HTTPHEADER, headers); 241 #ifdef USE_NGHTTP2 242 ERROR_CHECK_SETOPT(CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS); 243 #endif 244 #ifndef CURLDEBUG 245 /* enforce HTTPS if not debug */ 246 ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTPS); 247 #endif 248 ERROR_CHECK_SETOPT(CURLOPT_TIMEOUT_MS, (long)timeout_ms); 249 if(data->set.verbose) 250 ERROR_CHECK_SETOPT(CURLOPT_VERBOSE, 1L); 251 if(data->set.no_signal) 252 ERROR_CHECK_SETOPT(CURLOPT_NOSIGNAL, 1L); 253 254 /* Inherit *some* SSL options from the user's transfer. This is a 255 best-guess as to which options are needed for compatibility. #3661 */ 256 if(data->set.ssl.falsestart) 257 ERROR_CHECK_SETOPT(CURLOPT_SSL_FALSESTART, 1L); 258 if(data->set.ssl.primary.verifyhost) 259 ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYHOST, 2L); 260 if(data->set.proxy_ssl.primary.verifyhost) 261 ERROR_CHECK_SETOPT(CURLOPT_PROXY_SSL_VERIFYHOST, 2L); 262 if(data->set.ssl.primary.verifypeer) 263 ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYPEER, 1L); 264 if(data->set.proxy_ssl.primary.verifypeer) 265 ERROR_CHECK_SETOPT(CURLOPT_PROXY_SSL_VERIFYPEER, 1L); 266 if(data->set.ssl.primary.verifystatus) 267 ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYSTATUS, 1L); 268 if(data->set.str[STRING_SSL_CAFILE_ORIG]) { 269 ERROR_CHECK_SETOPT(CURLOPT_CAINFO, 270 data->set.str[STRING_SSL_CAFILE_ORIG]); 271 } 272 if(data->set.str[STRING_SSL_CAFILE_PROXY]) { 273 ERROR_CHECK_SETOPT(CURLOPT_PROXY_CAINFO, 274 data->set.str[STRING_SSL_CAFILE_PROXY]); 275 } 276 if(data->set.str[STRING_SSL_CAPATH_ORIG]) { 277 ERROR_CHECK_SETOPT(CURLOPT_CAPATH, 278 data->set.str[STRING_SSL_CAPATH_ORIG]); 279 } 280 if(data->set.str[STRING_SSL_CAPATH_PROXY]) { 281 ERROR_CHECK_SETOPT(CURLOPT_PROXY_CAPATH, 282 data->set.str[STRING_SSL_CAPATH_PROXY]); 283 } 284 if(data->set.str[STRING_SSL_CRLFILE_ORIG]) { 285 ERROR_CHECK_SETOPT(CURLOPT_CRLFILE, 286 data->set.str[STRING_SSL_CRLFILE_ORIG]); 287 } 288 if(data->set.str[STRING_SSL_CRLFILE_PROXY]) { 289 ERROR_CHECK_SETOPT(CURLOPT_PROXY_CRLFILE, 290 data->set.str[STRING_SSL_CRLFILE_PROXY]); 291 } 292 if(data->set.ssl.certinfo) 293 ERROR_CHECK_SETOPT(CURLOPT_CERTINFO, 1L); 294 if(data->set.str[STRING_SSL_RANDOM_FILE]) { 295 ERROR_CHECK_SETOPT(CURLOPT_RANDOM_FILE, 296 data->set.str[STRING_SSL_RANDOM_FILE]); 297 } 298 if(data->set.str[STRING_SSL_EGDSOCKET]) { 299 ERROR_CHECK_SETOPT(CURLOPT_EGDSOCKET, 300 data->set.str[STRING_SSL_EGDSOCKET]); 301 } 302 if(data->set.ssl.no_revoke) 303 ERROR_CHECK_SETOPT(CURLOPT_SSL_OPTIONS, CURLSSLOPT_NO_REVOKE); 304 if(data->set.proxy_ssl.no_revoke) 305 ERROR_CHECK_SETOPT(CURLOPT_PROXY_SSL_OPTIONS, CURLSSLOPT_NO_REVOKE); 306 if(data->set.ssl.fsslctx) 307 ERROR_CHECK_SETOPT(CURLOPT_SSL_CTX_FUNCTION, data->set.ssl.fsslctx); 308 if(data->set.ssl.fsslctxp) 309 ERROR_CHECK_SETOPT(CURLOPT_SSL_CTX_DATA, data->set.ssl.fsslctxp); 310 311 doh->set.fmultidone = Curl_doh_done; 312 doh->set.dohfor = data; /* identify for which transfer this is done */ 313 p->easy = doh; 314 315 /* add this transfer to the multi handle */ 316 if(curl_multi_add_handle(multi, doh)) 317 goto error; 318 } 319 else 320 goto error; 321 free(nurl); 322 return CURLE_OK; 323 324 error: 325 free(nurl); 326 Curl_close(doh); 327 return result; 328 } 329 330 /* 331 * Curl_doh() resolves a name using DOH. It resolves a name and returns a 332 * 'Curl_addrinfo *' with the address information. 333 */ 334 335 Curl_addrinfo *Curl_doh(struct connectdata *conn, 336 const char *hostname, 337 int port, 338 int *waitp) 339 { 340 struct Curl_easy *data = conn->data; 341 CURLcode result = CURLE_OK; 342 *waitp = TRUE; /* this never returns synchronously */ 343 (void)conn; 344 (void)hostname; 345 (void)port; 346 347 /* start clean, consider allocating this struct on demand */ 348 memset(&data->req.doh, 0, sizeof(struct dohdata)); 349 350 data->req.doh.host = hostname; 351 data->req.doh.port = port; 352 data->req.doh.headers = 353 curl_slist_append(NULL, 354 "Content-Type: application/dns-message"); 355 if(!data->req.doh.headers) 356 goto error; 357 358 if(conn->ip_version != CURL_IPRESOLVE_V6) { 359 /* create IPv4 DOH request */ 360 result = dohprobe(data, &data->req.doh.probe[0], DNS_TYPE_A, 361 hostname, data->set.str[STRING_DOH], 362 data->multi, data->req.doh.headers); 363 if(result) 364 goto error; 365 data->req.doh.pending++; 366 } 367 368 if(conn->ip_version != CURL_IPRESOLVE_V4) { 369 /* create IPv6 DOH request */ 370 result = dohprobe(data, &data->req.doh.probe[1], DNS_TYPE_AAAA, 371 hostname, data->set.str[STRING_DOH], 372 data->multi, data->req.doh.headers); 373 if(result) 374 goto error; 375 data->req.doh.pending++; 376 } 377 return NULL; 378 379 error: 380 curl_slist_free_all(data->req.doh.headers); 381 data->req.doh.headers = NULL; 382 curl_easy_cleanup(data->req.doh.probe[0].easy); 383 data->req.doh.probe[0].easy = NULL; 384 curl_easy_cleanup(data->req.doh.probe[1].easy); 385 data->req.doh.probe[1].easy = NULL; 386 return NULL; 387 } 388 389 static DOHcode skipqname(unsigned char *doh, size_t dohlen, 390 unsigned int *indexp) 391 { 392 unsigned char length; 393 do { 394 if(dohlen < (*indexp + 1)) 395 return DOH_DNS_OUT_OF_RANGE; 396 length = doh[*indexp]; 397 if((length & 0xc0) == 0xc0) { 398 /* name pointer, advance over it and be done */ 399 if(dohlen < (*indexp + 2)) 400 return DOH_DNS_OUT_OF_RANGE; 401 *indexp += 2; 402 break; 403 } 404 if(length & 0xc0) 405 return DOH_DNS_BAD_LABEL; 406 if(dohlen < (*indexp + 1 + length)) 407 return DOH_DNS_OUT_OF_RANGE; 408 *indexp += 1 + length; 409 } while(length); 410 return DOH_OK; 411 } 412 413 static unsigned short get16bit(unsigned char *doh, int index) 414 { 415 return (unsigned short)((doh[index] << 8) | doh[index + 1]); 416 } 417 418 static unsigned int get32bit(unsigned char *doh, int index) 419 { 420 return (doh[index] << 24) | (doh[index + 1] << 16) | 421 (doh[index + 2] << 8) | doh[index + 3]; 422 } 423 424 static DOHcode store_a(unsigned char *doh, int index, struct dohentry *d) 425 { 426 /* silently ignore addresses over the limit */ 427 if(d->numaddr < DOH_MAX_ADDR) { 428 struct dohaddr *a = &d->addr[d->numaddr]; 429 a->type = DNS_TYPE_A; 430 memcpy(&a->ip.v4, &doh[index], 4); 431 d->numaddr++; 432 } 433 return DOH_OK; 434 } 435 436 static DOHcode store_aaaa(unsigned char *doh, int index, struct dohentry *d) 437 { 438 /* silently ignore addresses over the limit */ 439 if(d->numaddr < DOH_MAX_ADDR) { 440 struct dohaddr *a = &d->addr[d->numaddr]; 441 a->type = DNS_TYPE_AAAA; 442 memcpy(&a->ip.v6, &doh[index], 16); 443 d->numaddr++; 444 } 445 return DOH_OK; 446 } 447 448 static DOHcode cnameappend(struct cnamestore *c, 449 unsigned char *src, 450 size_t len) 451 { 452 if(!c->alloc) { 453 c->allocsize = len + 1; 454 c->alloc = malloc(c->allocsize); 455 if(!c->alloc) 456 return DOH_OUT_OF_MEM; 457 } 458 else if(c->allocsize < (c->allocsize + len + 1)) { 459 char *ptr; 460 c->allocsize += len + 1; 461 ptr = realloc(c->alloc, c->allocsize); 462 if(!ptr) { 463 free(c->alloc); 464 return DOH_OUT_OF_MEM; 465 } 466 c->alloc = ptr; 467 } 468 memcpy(&c->alloc[c->len], src, len); 469 c->len += len; 470 c->alloc[c->len] = 0; /* keep it zero terminated */ 471 return DOH_OK; 472 } 473 474 static DOHcode store_cname(unsigned char *doh, 475 size_t dohlen, 476 unsigned int index, 477 struct dohentry *d) 478 { 479 struct cnamestore *c; 480 unsigned int loop = 128; /* a valid DNS name can never loop this much */ 481 unsigned char length; 482 483 if(d->numcname == DOH_MAX_CNAME) 484 return DOH_OK; /* skip! */ 485 486 c = &d->cname[d->numcname++]; 487 do { 488 if(index >= dohlen) 489 return DOH_DNS_OUT_OF_RANGE; 490 length = doh[index]; 491 if((length & 0xc0) == 0xc0) { 492 int newpos; 493 /* name pointer, get the new offset (14 bits) */ 494 if((index + 1) >= dohlen) 495 return DOH_DNS_OUT_OF_RANGE; 496 497 /* move to the the new index */ 498 newpos = (length & 0x3f) << 8 | doh[index + 1]; 499 index = newpos; 500 continue; 501 } 502 else if(length & 0xc0) 503 return DOH_DNS_BAD_LABEL; /* bad input */ 504 else 505 index++; 506 507 if(length) { 508 DOHcode rc; 509 if(c->len) { 510 rc = cnameappend(c, (unsigned char *)".", 1); 511 if(rc) 512 return rc; 513 } 514 if((index + length) > dohlen) 515 return DOH_DNS_BAD_LABEL; 516 517 rc = cnameappend(c, &doh[index], length); 518 if(rc) 519 return rc; 520 index += length; 521 } 522 } while(length && --loop); 523 524 if(!loop) 525 return DOH_DNS_LABEL_LOOP; 526 return DOH_OK; 527 } 528 529 static DOHcode rdata(unsigned char *doh, 530 size_t dohlen, 531 unsigned short rdlength, 532 unsigned short type, 533 int index, 534 struct dohentry *d) 535 { 536 /* RDATA 537 - A (TYPE 1): 4 bytes 538 - AAAA (TYPE 28): 16 bytes 539 - NS (TYPE 2): N bytes */ 540 DOHcode rc; 541 542 switch(type) { 543 case DNS_TYPE_A: 544 if(rdlength != 4) 545 return DOH_DNS_RDATA_LEN; 546 rc = store_a(doh, index, d); 547 if(rc) 548 return rc; 549 break; 550 case DNS_TYPE_AAAA: 551 if(rdlength != 16) 552 return DOH_DNS_RDATA_LEN; 553 rc = store_aaaa(doh, index, d); 554 if(rc) 555 return rc; 556 break; 557 case DNS_TYPE_CNAME: 558 rc = store_cname(doh, dohlen, index, d); 559 if(rc) 560 return rc; 561 break; 562 default: 563 /* unsupported type, just skip it */ 564 break; 565 } 566 return DOH_OK; 567 } 568 569 static void init_dohentry(struct dohentry *de) 570 { 571 memset(de, 0, sizeof(*de)); 572 de->ttl = INT_MAX; 573 } 574 575 576 UNITTEST DOHcode doh_decode(unsigned char *doh, 577 size_t dohlen, 578 DNStype dnstype, 579 struct dohentry *d) 580 { 581 unsigned char rcode; 582 unsigned short qdcount; 583 unsigned short ancount; 584 unsigned short type = 0; 585 unsigned short class; 586 unsigned short rdlength; 587 unsigned short nscount; 588 unsigned short arcount; 589 unsigned int index = 12; 590 DOHcode rc; 591 592 if(dohlen < 12) 593 return DOH_TOO_SMALL_BUFFER; /* too small */ 594 if(!doh || doh[0] || doh[1]) 595 return DOH_DNS_BAD_ID; /* bad ID */ 596 rcode = doh[3] & 0x0f; 597 if(rcode) 598 return DOH_DNS_BAD_RCODE; /* bad rcode */ 599 600 qdcount = get16bit(doh, 4); 601 while(qdcount) { 602 rc = skipqname(doh, dohlen, &index); 603 if(rc) 604 return rc; /* bad qname */ 605 if(dohlen < (index + 4)) 606 return DOH_DNS_OUT_OF_RANGE; 607 index += 4; /* skip question's type and class */ 608 qdcount--; 609 } 610 611 ancount = get16bit(doh, 6); 612 while(ancount) { 613 unsigned int ttl; 614 615 rc = skipqname(doh, dohlen, &index); 616 if(rc) 617 return rc; /* bad qname */ 618 619 if(dohlen < (index + 2)) 620 return DOH_DNS_OUT_OF_RANGE; 621 622 type = get16bit(doh, index); 623 if((type != DNS_TYPE_CNAME) && (type != dnstype)) 624 /* Not the same type as was asked for nor CNAME */ 625 return DOH_DNS_UNEXPECTED_TYPE; 626 index += 2; 627 628 if(dohlen < (index + 2)) 629 return DOH_DNS_OUT_OF_RANGE; 630 class = get16bit(doh, index); 631 if(DNS_CLASS_IN != class) 632 return DOH_DNS_UNEXPECTED_CLASS; /* unsupported */ 633 index += 2; 634 635 if(dohlen < (index + 4)) 636 return DOH_DNS_OUT_OF_RANGE; 637 638 ttl = get32bit(doh, index); 639 if(ttl < d->ttl) 640 d->ttl = ttl; 641 index += 4; 642 643 if(dohlen < (index + 2)) 644 return DOH_DNS_OUT_OF_RANGE; 645 646 rdlength = get16bit(doh, index); 647 index += 2; 648 if(dohlen < (index + rdlength)) 649 return DOH_DNS_OUT_OF_RANGE; 650 651 rc = rdata(doh, dohlen, rdlength, type, index, d); 652 if(rc) 653 return rc; /* bad rdata */ 654 index += rdlength; 655 ancount--; 656 } 657 658 nscount = get16bit(doh, 8); 659 while(nscount) { 660 rc = skipqname(doh, dohlen, &index); 661 if(rc) 662 return rc; /* bad qname */ 663 664 if(dohlen < (index + 8)) 665 return DOH_DNS_OUT_OF_RANGE; 666 667 index += 2 + 2 + 4; /* type, class and ttl */ 668 669 if(dohlen < (index + 2)) 670 return DOH_DNS_OUT_OF_RANGE; 671 672 rdlength = get16bit(doh, index); 673 index += 2; 674 if(dohlen < (index + rdlength)) 675 return DOH_DNS_OUT_OF_RANGE; 676 index += rdlength; 677 nscount--; 678 } 679 680 arcount = get16bit(doh, 10); 681 while(arcount) { 682 rc = skipqname(doh, dohlen, &index); 683 if(rc) 684 return rc; /* bad qname */ 685 686 if(dohlen < (index + 8)) 687 return DOH_DNS_OUT_OF_RANGE; 688 689 index += 2 + 2 + 4; /* type, class and ttl */ 690 691 if(dohlen < (index + 2)) 692 return DOH_DNS_OUT_OF_RANGE; 693 694 rdlength = get16bit(doh, index); 695 index += 2; 696 if(dohlen < (index + rdlength)) 697 return DOH_DNS_OUT_OF_RANGE; 698 index += rdlength; 699 arcount--; 700 } 701 702 if(index != dohlen) 703 return DOH_DNS_MALFORMAT; /* something is wrong */ 704 705 if((type != DNS_TYPE_NS) && !d->numcname && !d->numaddr) 706 /* nothing stored! */ 707 return DOH_NO_CONTENT; 708 709 return DOH_OK; /* ok */ 710 } 711 712 #ifndef CURL_DISABLE_VERBOSE_STRINGS 713 static void showdoh(struct Curl_easy *data, 714 struct dohentry *d) 715 { 716 int i; 717 infof(data, "TTL: %u seconds\n", d->ttl); 718 for(i = 0; i < d->numaddr; i++) { 719 struct dohaddr *a = &d->addr[i]; 720 if(a->type == DNS_TYPE_A) { 721 infof(data, "DOH A: %u.%u.%u.%u\n", 722 a->ip.v4[0], a->ip.v4[1], 723 a->ip.v4[2], a->ip.v4[3]); 724 } 725 else if(a->type == DNS_TYPE_AAAA) { 726 int j; 727 char buffer[128]; 728 char *ptr; 729 size_t len; 730 msnprintf(buffer, 128, "DOH AAAA: "); 731 ptr = &buffer[10]; 732 len = 118; 733 for(j = 0; j < 16; j += 2) { 734 size_t l; 735 msnprintf(ptr, len, "%s%02x%02x", j?":":"", d->addr[i].ip.v6[j], 736 d->addr[i].ip.v6[j + 1]); 737 l = strlen(ptr); 738 len -= l; 739 ptr += l; 740 } 741 infof(data, "%s\n", buffer); 742 } 743 } 744 for(i = 0; i < d->numcname; i++) { 745 infof(data, "CNAME: %s\n", d->cname[i].alloc); 746 } 747 } 748 #else 749 #define showdoh(x,y) 750 #endif 751 752 /* 753 * doh2ai() 754 * 755 * This function returns a pointer to the first element of a newly allocated 756 * Curl_addrinfo struct linked list filled with the data from a set of DOH 757 * lookups. Curl_addrinfo is meant to work like the addrinfo struct does for 758 * a IPv6 stack, but usable also for IPv4, all hosts and environments. 759 * 760 * The memory allocated by this function *MUST* be free'd later on calling 761 * Curl_freeaddrinfo(). For each successful call to this function there 762 * must be an associated call later to Curl_freeaddrinfo(). 763 */ 764 765 static Curl_addrinfo * 766 doh2ai(const struct dohentry *de, const char *hostname, int port) 767 { 768 Curl_addrinfo *ai; 769 Curl_addrinfo *prevai = NULL; 770 Curl_addrinfo *firstai = NULL; 771 struct sockaddr_in *addr; 772 #ifdef ENABLE_IPV6 773 struct sockaddr_in6 *addr6; 774 #endif 775 CURLcode result = CURLE_OK; 776 int i; 777 778 if(!de) 779 /* no input == no output! */ 780 return NULL; 781 782 for(i = 0; i < de->numaddr; i++) { 783 size_t ss_size; 784 CURL_SA_FAMILY_T addrtype; 785 if(de->addr[i].type == DNS_TYPE_AAAA) { 786 #ifndef ENABLE_IPV6 787 /* we can't handle IPv6 addresses */ 788 continue; 789 #else 790 ss_size = sizeof(struct sockaddr_in6); 791 addrtype = AF_INET6; 792 #endif 793 } 794 else { 795 ss_size = sizeof(struct sockaddr_in); 796 addrtype = AF_INET; 797 } 798 799 ai = calloc(1, sizeof(Curl_addrinfo)); 800 if(!ai) { 801 result = CURLE_OUT_OF_MEMORY; 802 break; 803 } 804 ai->ai_canonname = strdup(hostname); 805 if(!ai->ai_canonname) { 806 result = CURLE_OUT_OF_MEMORY; 807 free(ai); 808 break; 809 } 810 ai->ai_addr = calloc(1, ss_size); 811 if(!ai->ai_addr) { 812 result = CURLE_OUT_OF_MEMORY; 813 free(ai->ai_canonname); 814 free(ai); 815 break; 816 } 817 818 if(!firstai) 819 /* store the pointer we want to return from this function */ 820 firstai = ai; 821 822 if(prevai) 823 /* make the previous entry point to this */ 824 prevai->ai_next = ai; 825 826 ai->ai_family = addrtype; 827 828 /* we return all names as STREAM, so when using this address for TFTP 829 the type must be ignored and conn->socktype be used instead! */ 830 ai->ai_socktype = SOCK_STREAM; 831 832 ai->ai_addrlen = (curl_socklen_t)ss_size; 833 834 /* leave the rest of the struct filled with zero */ 835 836 switch(ai->ai_family) { 837 case AF_INET: 838 addr = (void *)ai->ai_addr; /* storage area for this info */ 839 DEBUGASSERT(sizeof(struct in_addr) == sizeof(de->addr[i].ip.v4)); 840 memcpy(&addr->sin_addr, &de->addr[i].ip.v4, sizeof(struct in_addr)); 841 addr->sin_family = (CURL_SA_FAMILY_T)addrtype; 842 addr->sin_port = htons((unsigned short)port); 843 break; 844 845 #ifdef ENABLE_IPV6 846 case AF_INET6: 847 addr6 = (void *)ai->ai_addr; /* storage area for this info */ 848 DEBUGASSERT(sizeof(struct in6_addr) == sizeof(de->addr[i].ip.v6)); 849 memcpy(&addr6->sin6_addr, &de->addr[i].ip.v6, sizeof(struct in6_addr)); 850 addr6->sin6_family = (CURL_SA_FAMILY_T)addrtype; 851 addr6->sin6_port = htons((unsigned short)port); 852 break; 853 #endif 854 } 855 856 prevai = ai; 857 } 858 859 if(result) { 860 Curl_freeaddrinfo(firstai); 861 firstai = NULL; 862 } 863 864 return firstai; 865 } 866 867 #ifndef CURL_DISABLE_VERBOSE_STRINGS 868 static const char *type2name(DNStype dnstype) 869 { 870 return (dnstype == DNS_TYPE_A)?"A":"AAAA"; 871 } 872 #endif 873 874 UNITTEST void de_cleanup(struct dohentry *d) 875 { 876 int i = 0; 877 for(i = 0; i < d->numcname; i++) { 878 free(d->cname[i].alloc); 879 } 880 } 881 882 CURLcode Curl_doh_is_resolved(struct connectdata *conn, 883 struct Curl_dns_entry **dnsp) 884 { 885 struct Curl_easy *data = conn->data; 886 *dnsp = NULL; /* defaults to no response */ 887 888 if(!data->req.doh.probe[0].easy && !data->req.doh.probe[1].easy) { 889 failf(data, "Could not DOH-resolve: %s", conn->async.hostname); 890 return conn->bits.proxy?CURLE_COULDNT_RESOLVE_PROXY: 891 CURLE_COULDNT_RESOLVE_HOST; 892 } 893 else if(!data->req.doh.pending) { 894 DOHcode rc; 895 DOHcode rc2; 896 struct dohentry de; 897 struct Curl_dns_entry *dns; 898 struct Curl_addrinfo *ai; 899 /* remove DOH handles from multi handle and close them */ 900 curl_multi_remove_handle(data->multi, data->req.doh.probe[0].easy); 901 Curl_close(data->req.doh.probe[0].easy); 902 curl_multi_remove_handle(data->multi, data->req.doh.probe[1].easy); 903 Curl_close(data->req.doh.probe[1].easy); 904 905 /* parse the responses, create the struct and return it! */ 906 init_dohentry(&de); 907 rc = doh_decode(data->req.doh.probe[0].serverdoh.memory, 908 data->req.doh.probe[0].serverdoh.size, 909 data->req.doh.probe[0].dnstype, 910 &de); 911 free(data->req.doh.probe[0].serverdoh.memory); 912 if(rc) { 913 infof(data, "DOH: %s type %s for %s\n", doh_strerror(rc), 914 type2name(data->req.doh.probe[0].dnstype), 915 data->req.doh.host); 916 } 917 rc2 = doh_decode(data->req.doh.probe[1].serverdoh.memory, 918 data->req.doh.probe[1].serverdoh.size, 919 data->req.doh.probe[1].dnstype, 920 &de); 921 free(data->req.doh.probe[1].serverdoh.memory); 922 if(rc2) { 923 infof(data, "DOH: %s type %s for %s\n", doh_strerror(rc2), 924 type2name(data->req.doh.probe[1].dnstype), 925 data->req.doh.host); 926 } 927 if(!rc || !rc2) { 928 infof(data, "DOH Host name: %s\n", data->req.doh.host); 929 showdoh(data, &de); 930 931 ai = doh2ai(&de, data->req.doh.host, data->req.doh.port); 932 if(!ai) { 933 de_cleanup(&de); 934 return CURLE_OUT_OF_MEMORY; 935 } 936 937 if(data->share) 938 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); 939 940 /* we got a response, store it in the cache */ 941 dns = Curl_cache_addr(data, ai, data->req.doh.host, data->req.doh.port); 942 943 if(data->share) 944 Curl_share_unlock(data, CURL_LOCK_DATA_DNS); 945 946 de_cleanup(&de); 947 if(!dns) 948 /* returned failure, bail out nicely */ 949 Curl_freeaddrinfo(ai); 950 else { 951 conn->async.dns = dns; 952 *dnsp = dns; 953 return CURLE_OK; 954 } 955 } 956 de_cleanup(&de); 957 958 return CURLE_COULDNT_RESOLVE_HOST; 959 } 960 961 return CURLE_OK; 962 } 963