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