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