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