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 additional 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 unsigned char clen = 0; /* chunk len */
1049 struct dynbuf thename;
1050
1051 DEBUGASSERT(buf && remaining && dnsname);
1052 if(!buf || !remaining || !dnsname)
1053 return CURLE_OUT_OF_MEMORY;
1054 rem = (int)*remaining;
1055 if(rem <= 0) {
1056 Curl_dyn_free(&thename);
1057 return CURLE_OUT_OF_MEMORY;
1058 }
1059 Curl_dyn_init(&thename, CURL_MAXLEN_host_name);
1060 cp = *buf;
1061 clen = *cp++;
1062 if(clen == 0) {
1063 /* special case - return "." as name */
1064 if(Curl_dyn_addn(&thename, ".", 1))
1065 return CURLE_OUT_OF_MEMORY;
1066 }
1067 while(clen) {
1068 if(clen >= rem) {
1069 Curl_dyn_free(&thename);
1070 return CURLE_OUT_OF_MEMORY;
1071 }
1072 if(Curl_dyn_addn(&thename, cp, clen) ||
1073 Curl_dyn_addn(&thename, ".", 1))
1074 return CURLE_TOO_LARGE;
1075
1076 cp += clen;
1077 rem -= (clen + 1);
1078 if(rem <= 0) {
1079 Curl_dyn_free(&thename);
1080 return CURLE_OUT_OF_MEMORY;
1081 }
1082 clen = *cp++;
1083 }
1084 *buf = cp;
1085 *remaining = rem - 1;
1086 *dnsname = Curl_dyn_ptr(&thename);
1087 return CURLE_OK;
1088 }
1089
local_decode_rdata_alpn(unsigned char * rrval,size_t len,char ** alpns)1090 static CURLcode local_decode_rdata_alpn(unsigned char *rrval, size_t len,
1091 char **alpns)
1092 {
1093 /*
1094 * spec here is as per draft-ietf-dnsop-svcb-https, section-7.1.1
1095 * encoding is catenated list of strings each preceded by a one
1096 * octet length
1097 * output is comma-sep list of the strings
1098 * implementations may or may not handle quoting of comma within
1099 * string values, so we might see a comma within the wire format
1100 * version of a string, in which case we'll precede that by a
1101 * backslash - same goes for a backslash character, and of course
1102 * we need to use two backslashes in strings when we mean one;-)
1103 */
1104 int remaining = (int) len;
1105 char *oval;
1106 size_t i;
1107 unsigned char *cp = rrval;
1108 struct dynbuf dval;
1109
1110 if(!alpns)
1111 return CURLE_OUT_OF_MEMORY;
1112 Curl_dyn_init(&dval, DYN_DOH_RESPONSE);
1113 remaining = (int)len;
1114 cp = rrval;
1115 while(remaining > 0) {
1116 size_t tlen = (size_t) *cp++;
1117
1118 /* if not 1st time, add comma */
1119 if(remaining != (int)len && Curl_dyn_addn(&dval, ",", 1))
1120 goto err;
1121 remaining--;
1122 if(tlen > (size_t)remaining)
1123 goto err;
1124 /* add escape char if needed, clunky but easier to read */
1125 for(i = 0; i != tlen; i++) {
1126 if('\\' == *cp || ',' == *cp) {
1127 if(Curl_dyn_addn(&dval, "\\", 1))
1128 goto err;
1129 }
1130 if(Curl_dyn_addn(&dval, cp++, 1))
1131 goto err;
1132 }
1133 remaining -= (int)tlen;
1134 }
1135 /* this string is always null terminated */
1136 oval = Curl_dyn_ptr(&dval);
1137 if(!oval)
1138 goto err;
1139 *alpns = oval;
1140 return CURLE_OK;
1141 err:
1142 Curl_dyn_free(&dval);
1143 return CURLE_BAD_CONTENT_ENCODING;
1144 }
1145
1146 #ifdef CURLDEBUG
test_alpn_escapes(void)1147 static CURLcode test_alpn_escapes(void)
1148 {
1149 /* we'll use an example from draft-ietf-dnsop-svcb, figure 10 */
1150 static unsigned char example[] = {
1151 0x08, /* length 8 */
1152 0x66, 0x5c, 0x6f, 0x6f, 0x2c, 0x62, 0x61, 0x72, /* value "f\\oo,bar" */
1153 0x02, /* length 2 */
1154 0x68, 0x32 /* value "h2" */
1155 };
1156 size_t example_len = sizeof(example);
1157 char *aval = NULL;
1158 static const char *expected = "f\\\\oo\\,bar,h2";
1159
1160 if(local_decode_rdata_alpn(example, example_len, &aval) != CURLE_OK)
1161 return CURLE_BAD_CONTENT_ENCODING;
1162 if(strlen(aval) != strlen(expected))
1163 return CURLE_BAD_CONTENT_ENCODING;
1164 if(memcmp(aval, expected, strlen(aval)))
1165 return CURLE_BAD_CONTENT_ENCODING;
1166 return CURLE_OK;
1167 }
1168 #endif
1169
Curl_doh_decode_httpsrr(unsigned char * rrval,size_t len,struct Curl_https_rrinfo ** hrr)1170 static CURLcode Curl_doh_decode_httpsrr(unsigned char *rrval, size_t len,
1171 struct Curl_https_rrinfo **hrr)
1172 {
1173 size_t remaining = len;
1174 unsigned char *cp = rrval;
1175 uint16_t pcode = 0, plen = 0;
1176 struct Curl_https_rrinfo *lhrr = NULL;
1177 char *dnsname = NULL;
1178
1179 #ifdef CURLDEBUG
1180 /* a few tests of escaping, shouldn't be here but ok for now */
1181 if(test_alpn_escapes() != CURLE_OK)
1182 return CURLE_OUT_OF_MEMORY;
1183 #endif
1184 lhrr = calloc(1, sizeof(struct Curl_https_rrinfo));
1185 if(!lhrr)
1186 return CURLE_OUT_OF_MEMORY;
1187 lhrr->val = Curl_memdup(rrval, len);
1188 if(!lhrr->val)
1189 goto err;
1190 lhrr->len = len;
1191 if(remaining <= 2)
1192 goto err;
1193 lhrr->priority = (uint16_t)((cp[0] << 8) + cp[1]);
1194 cp += 2;
1195 remaining -= (uint16_t)2;
1196 if(local_decode_rdata_name(&cp, &remaining, &dnsname) != CURLE_OK)
1197 goto err;
1198 lhrr->target = dnsname;
1199 while(remaining >= 4) {
1200 pcode = (uint16_t)((*cp << 8) + (*(cp + 1)));
1201 cp += 2;
1202 plen = (uint16_t)((*cp << 8) + (*(cp + 1)));
1203 cp += 2;
1204 remaining -= 4;
1205 if(pcode == HTTPS_RR_CODE_ALPN) {
1206 if(local_decode_rdata_alpn(cp, plen, &lhrr->alpns) != CURLE_OK)
1207 goto err;
1208 }
1209 if(pcode == HTTPS_RR_CODE_NO_DEF_ALPN)
1210 lhrr->no_def_alpn = TRUE;
1211 else if(pcode == HTTPS_RR_CODE_IPV4) {
1212 lhrr->ipv4hints = Curl_memdup(cp, plen);
1213 if(!lhrr->ipv4hints)
1214 goto err;
1215 lhrr->ipv4hints_len = (size_t)plen;
1216 }
1217 else if(pcode == HTTPS_RR_CODE_ECH) {
1218 lhrr->echconfiglist = Curl_memdup(cp, plen);
1219 if(!lhrr->echconfiglist)
1220 goto err;
1221 lhrr->echconfiglist_len = (size_t)plen;
1222 }
1223 else if(pcode == HTTPS_RR_CODE_IPV6) {
1224 lhrr->ipv6hints = Curl_memdup(cp, plen);
1225 if(!lhrr->ipv6hints)
1226 goto err;
1227 lhrr->ipv6hints_len = (size_t)plen;
1228 }
1229 if(plen > 0 && plen <= remaining) {
1230 cp += plen;
1231 remaining -= plen;
1232 }
1233 }
1234 DEBUGASSERT(!remaining);
1235 *hrr = lhrr;
1236 return CURLE_OK;
1237 err:
1238 if(lhrr) {
1239 free(lhrr->target);
1240 free(lhrr->echconfiglist);
1241 free(lhrr->val);
1242 free(lhrr);
1243 }
1244 return CURLE_OUT_OF_MEMORY;
1245 }
1246
1247 # ifdef CURLDEBUG
local_print_httpsrr(struct Curl_easy * data,struct Curl_https_rrinfo * hrr)1248 static void local_print_httpsrr(struct Curl_easy *data,
1249 struct Curl_https_rrinfo *hrr)
1250 {
1251 DEBUGASSERT(hrr);
1252 infof(data, "HTTPS RR: priority %d, target: %s",
1253 hrr->priority, hrr->target);
1254 if(hrr->alpns)
1255 infof(data, "HTTPS RR: alpns %s", hrr->alpns);
1256 else
1257 infof(data, "HTTPS RR: no alpns");
1258 if(hrr->no_def_alpn)
1259 infof(data, "HTTPS RR: no_def_alpn set");
1260 else
1261 infof(data, "HTTPS RR: no_def_alpn not set");
1262 if(hrr->ipv4hints) {
1263 local_print_buf(data, "HTTPS RR: ipv4hints",
1264 hrr->ipv4hints, hrr->ipv4hints_len);
1265 }
1266 else
1267 infof(data, "HTTPS RR: no ipv4hints");
1268 if(hrr->echconfiglist) {
1269 local_print_buf(data, "HTTPS RR: ECHConfigList",
1270 hrr->echconfiglist, hrr->echconfiglist_len);
1271 }
1272 else
1273 infof(data, "HTTPS RR: no ECHConfigList");
1274 if(hrr->ipv6hints) {
1275 local_print_buf(data, "HTTPS RR: ipv6hint",
1276 hrr->ipv6hints, hrr->ipv6hints_len);
1277 }
1278 else
1279 infof(data, "HTTPS RR: no ipv6hints");
1280 return;
1281 }
1282 # endif
1283 #endif
1284
Curl_doh_is_resolved(struct Curl_easy * data,struct Curl_dns_entry ** dnsp)1285 CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
1286 struct Curl_dns_entry **dnsp)
1287 {
1288 CURLcode result;
1289 struct dohdata *dohp = data->req.doh;
1290 *dnsp = NULL; /* defaults to no response */
1291 if(!dohp)
1292 return CURLE_OUT_OF_MEMORY;
1293
1294 if(!dohp->probe[DOH_PROBE_SLOT_IPADDR_V4].easy &&
1295 !dohp->probe[DOH_PROBE_SLOT_IPADDR_V6].easy) {
1296 failf(data, "Could not DoH-resolve: %s", data->state.async.hostname);
1297 return CONN_IS_PROXIED(data->conn)?CURLE_COULDNT_RESOLVE_PROXY:
1298 CURLE_COULDNT_RESOLVE_HOST;
1299 }
1300 else if(!dohp->pending) {
1301 #ifndef USE_HTTPSRR
1302 DOHcode rc[DOH_PROBE_SLOTS] = {
1303 DOH_OK, DOH_OK
1304 };
1305 #else
1306 DOHcode rc[DOH_PROBE_SLOTS] = {
1307 DOH_OK, DOH_OK, DOH_OK
1308 };
1309 #endif
1310 struct dohentry de;
1311 int slot;
1312 /* remove DoH handles from multi handle and close them */
1313 for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) {
1314 curl_multi_remove_handle(data->multi, dohp->probe[slot].easy);
1315 Curl_close(&dohp->probe[slot].easy);
1316 }
1317 /* parse the responses, create the struct and return it! */
1318 de_init(&de);
1319 for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) {
1320 struct dnsprobe *p = &dohp->probe[slot];
1321 if(!p->dnstype)
1322 continue;
1323 rc[slot] = doh_decode(Curl_dyn_uptr(&p->serverdoh),
1324 Curl_dyn_len(&p->serverdoh),
1325 p->dnstype,
1326 &de);
1327 Curl_dyn_free(&p->serverdoh);
1328 #ifndef CURL_DISABLE_VERBOSE_STRINGS
1329 if(rc[slot]) {
1330 infof(data, "DoH: %s type %s for %s", doh_strerror(rc[slot]),
1331 type2name(p->dnstype), dohp->host);
1332 }
1333 #endif
1334 } /* next slot */
1335
1336 result = CURLE_COULDNT_RESOLVE_HOST; /* until we know better */
1337 if(!rc[DOH_PROBE_SLOT_IPADDR_V4] || !rc[DOH_PROBE_SLOT_IPADDR_V6]) {
1338 /* we have an address, of one kind or other */
1339 struct Curl_dns_entry *dns;
1340 struct Curl_addrinfo *ai;
1341
1342
1343 if(Curl_trc_ft_is_verbose(data, &Curl_doh_trc)) {
1344 infof(data, "[DoH] Host name: %s", dohp->host);
1345 showdoh(data, &de);
1346 }
1347
1348 result = doh2ai(&de, dohp->host, dohp->port, &ai);
1349 if(result) {
1350 de_cleanup(&de);
1351 return result;
1352 }
1353
1354 if(data->share)
1355 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
1356
1357 /* we got a response, store it in the cache */
1358 dns = Curl_cache_addr(data, ai, dohp->host, 0, dohp->port);
1359
1360 if(data->share)
1361 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
1362
1363 if(!dns) {
1364 /* returned failure, bail out nicely */
1365 Curl_freeaddrinfo(ai);
1366 }
1367 else {
1368 data->state.async.dns = dns;
1369 *dnsp = dns;
1370 result = CURLE_OK; /* address resolution OK */
1371 }
1372 } /* address processing done */
1373
1374 /* Now process any build-specific attributes retrieved from DNS */
1375 #ifdef USE_HTTPSRR
1376 if(de.numhttps_rrs > 0 && result == CURLE_OK && *dnsp) {
1377 struct Curl_https_rrinfo *hrr = NULL;
1378 result = Curl_doh_decode_httpsrr(de.https_rrs->val, de.https_rrs->len,
1379 &hrr);
1380 if(result) {
1381 infof(data, "Failed to decode HTTPS RR");
1382 return result;
1383 }
1384 infof(data, "Some HTTPS RR to process");
1385 # ifdef CURLDEBUG
1386 local_print_httpsrr(data, hrr);
1387 # endif
1388 (*dnsp)->hinfo = hrr;
1389 }
1390 #endif
1391
1392 /* All done */
1393 de_cleanup(&de);
1394 Curl_safefree(data->req.doh);
1395 return result;
1396
1397 } /* !dohp->pending */
1398
1399 /* else wait for pending DoH transactions to complete */
1400 return CURLE_OK;
1401 }
1402
1403 #endif /* CURL_DISABLE_DOH */
1404