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