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