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