xref: /curl/lib/doh.c (revision a362962b)
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at https://curl.se/docs/copyright.html.
13  *
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  * SPDX-License-Identifier: curl
22  *
23  ***************************************************************************/
24 
25 #include "curl_setup.h"
26 
27 #ifndef CURL_DISABLE_DOH
28 
29 #include "urldata.h"
30 #include "curl_addrinfo.h"
31 #include "doh.h"
32 
33 #include "sendf.h"
34 #include "multiif.h"
35 #include "url.h"
36 #include "share.h"
37 #include "curl_base64.h"
38 #include "connect.h"
39 #include "strdup.h"
40 #include "dynbuf.h"
41 /* The last 3 #include files should be in this order */
42 #include "curl_printf.h"
43 #include "curl_memory.h"
44 #include "memdebug.h"
45 #include "escape.h"
46 
47 #define DNS_CLASS_IN 0x01
48 
49 /* local_print_buf truncates if the hex string will be more than this */
50 #define LOCAL_PB_HEXMAX 400
51 
52 #ifndef CURL_DISABLE_VERBOSE_STRINGS
53 static const char * const errors[]={
54   "",
55   "Bad label",
56   "Out of range",
57   "Label loop",
58   "Too small",
59   "Out of memory",
60   "RDATA length",
61   "Malformat",
62   "Bad RCODE",
63   "Unexpected TYPE",
64   "Unexpected CLASS",
65   "No content",
66   "Bad ID",
67   "Name too long"
68 };
69 
doh_strerror(DOHcode code)70 static const char *doh_strerror(DOHcode code)
71 {
72   if((code >= DOH_OK) && (code <= DOH_DNS_NAME_TOO_LONG))
73     return errors[code];
74   return "bad error code";
75 }
76 
77 struct curl_trc_feat Curl_doh_trc = {
78   "DoH",
79   CURL_LOG_LVL_NONE,
80 };
81 #endif /* !CURL_DISABLE_VERBOSE_STRINGS */
82 
83 /* @unittest 1655
84  */
doh_encode(const char * host,DNStype dnstype,unsigned char * dnsp,size_t len,size_t * olen)85 UNITTEST DOHcode doh_encode(const char *host,
86                             DNStype dnstype,
87                             unsigned char *dnsp, /* buffer */
88                             size_t len,  /* buffer size */
89                             size_t *olen) /* output length */
90 {
91   const size_t hostlen = strlen(host);
92   unsigned char *orig = dnsp;
93   const char *hostp = host;
94 
95   /* The expected output length is 16 bytes more than the length of
96    * the QNAME-encoding of the host name.
97    *
98    * A valid DNS name may not contain a zero-length label, except at
99    * the end.  For this reason, a name beginning with a dot, or
100    * containing a sequence of two or more consecutive dots, is invalid
101    * and cannot be encoded as a QNAME.
102    *
103    * If the host name ends with a trailing dot, the corresponding
104    * QNAME-encoding is one byte longer than the host name. If (as is
105    * also valid) the hostname is shortened by the omission of the
106    * trailing dot, then its QNAME-encoding will be two bytes longer
107    * than the host name.
108    *
109    * Each [ label, dot ] pair is encoded as [ length, label ],
110    * preserving overall length.  A final [ label ] without a dot is
111    * also encoded as [ length, label ], increasing overall length
112    * by one. The encoding is completed by appending a zero byte,
113    * representing the zero-length root label, again increasing
114    * the overall length by one.
115    */
116 
117   size_t expected_len;
118   DEBUGASSERT(hostlen);
119   expected_len = 12 + 1 + hostlen + 4;
120   if(host[hostlen-1]!='.')
121     expected_len++;
122 
123   if(expected_len > (256 + 16)) /* RFCs 1034, 1035 */
124     return DOH_DNS_NAME_TOO_LONG;
125 
126   if(len < expected_len)
127     return DOH_TOO_SMALL_BUFFER;
128 
129   *dnsp++ = 0; /* 16 bit id */
130   *dnsp++ = 0;
131   *dnsp++ = 0x01; /* |QR|   Opcode  |AA|TC|RD| Set the RD bit */
132   *dnsp++ = '\0'; /* |RA|   Z    |   RCODE   |                */
133   *dnsp++ = '\0';
134   *dnsp++ = 1;    /* QDCOUNT (number of entries in the question section) */
135   *dnsp++ = '\0';
136   *dnsp++ = '\0'; /* ANCOUNT */
137   *dnsp++ = '\0';
138   *dnsp++ = '\0'; /* NSCOUNT */
139   *dnsp++ = '\0';
140   *dnsp++ = '\0'; /* ARCOUNT */
141 
142   /* encode each label and store it in the QNAME */
143   while(*hostp) {
144     size_t labellen;
145     char *dot = strchr(hostp, '.');
146     if(dot)
147       labellen = dot - hostp;
148     else
149       labellen = strlen(hostp);
150     if((labellen > 63) || (!labellen)) {
151       /* label is too long or too short, error out */
152       *olen = 0;
153       return DOH_DNS_BAD_LABEL;
154     }
155     /* label is non-empty, process it */
156     *dnsp++ = (unsigned char)labellen;
157     memcpy(dnsp, hostp, labellen);
158     dnsp += labellen;
159     hostp += labellen;
160     /* advance past dot, but only if there is one */
161     if(dot)
162       hostp++;
163   } /* next label */
164 
165   *dnsp++ = 0; /* append zero-length label for root */
166 
167   /* There are assigned TYPE codes beyond 255: use range [1..65535]  */
168   *dnsp++ = (unsigned char)(255 & (dnstype>>8)); /* upper 8 bit TYPE */
169   *dnsp++ = (unsigned char)(255 & dnstype);      /* lower 8 bit TYPE */
170 
171   *dnsp++ = '\0'; /* upper 8 bit CLASS */
172   *dnsp++ = DNS_CLASS_IN; /* IN - "the Internet" */
173 
174   *olen = dnsp - orig;
175 
176   /* verify that our estimation of length is valid, since
177    * this has led to buffer overflows in this function */
178   DEBUGASSERT(*olen == expected_len);
179   return DOH_OK;
180 }
181 
182 static size_t
doh_write_cb(const void * contents,size_t size,size_t nmemb,void * userp)183 doh_write_cb(const void *contents, size_t size, size_t nmemb, void *userp)
184 {
185   size_t realsize = size * nmemb;
186   struct dynbuf *mem = (struct dynbuf *)userp;
187 
188   if(Curl_dyn_addn(mem, contents, realsize))
189     return 0;
190 
191   return realsize;
192 }
193 
194 #if defined(USE_HTTPSRR) && defined(CURLDEBUG)
local_print_buf(struct Curl_easy * data,const char * prefix,unsigned char * buf,size_t len)195 static void local_print_buf(struct Curl_easy *data,
196                             const char *prefix,
197                             unsigned char *buf, size_t len)
198 {
199   unsigned char hexstr[LOCAL_PB_HEXMAX];
200   size_t hlen = LOCAL_PB_HEXMAX;
201   bool truncated = false;
202 
203   if(len > (LOCAL_PB_HEXMAX / 2))
204     truncated = true;
205   Curl_hexencode(buf, len, hexstr, hlen);
206   if(!truncated)
207     infof(data, "%s: len=%d, val=%s", prefix, (int)len, hexstr);
208   else
209     infof(data, "%s: len=%d (truncated)val=%s", prefix, (int)len, hexstr);
210   return;
211 }
212 #endif
213 
214 /* called from multi.c when this DoH transfer is complete */
doh_done(struct Curl_easy * doh,CURLcode result)215 static int doh_done(struct Curl_easy *doh, CURLcode result)
216 {
217   struct Curl_easy *data = doh->set.dohfor;
218   struct dohdata *dohp = data->req.doh;
219   /* so one of the DoH request done for the 'data' transfer is now complete! */
220   dohp->pending--;
221   infof(doh, "a DoH request is completed, %u to go", dohp->pending);
222   if(result)
223     infof(doh, "DoH request %s", curl_easy_strerror(result));
224 
225   if(!dohp->pending) {
226     /* DoH completed */
227     curl_slist_free_all(dohp->headers);
228     dohp->headers = NULL;
229     Curl_expire(data, 0, EXPIRE_RUN_NOW);
230   }
231   return 0;
232 }
233 
234 #define ERROR_CHECK_SETOPT(x,y) \
235 do {                                          \
236   result = curl_easy_setopt(doh, x, y);       \
237   if(result &&                                \
238      result != CURLE_NOT_BUILT_IN &&          \
239      result != CURLE_UNKNOWN_OPTION)          \
240     goto error;                               \
241 } while(0)
242 
dohprobe(struct Curl_easy * data,struct dnsprobe * p,DNStype dnstype,const char * host,const char * url,CURLM * multi,struct curl_slist * headers)243 static CURLcode dohprobe(struct Curl_easy *data,
244                          struct dnsprobe *p, DNStype dnstype,
245                          const char *host,
246                          const char *url, CURLM *multi,
247                          struct curl_slist *headers)
248 {
249   struct Curl_easy *doh = NULL;
250   CURLcode result = CURLE_OK;
251   timediff_t timeout_ms;
252   DOHcode d = doh_encode(host, dnstype, p->dohbuffer, sizeof(p->dohbuffer),
253                          &p->dohlen);
254   if(d) {
255     failf(data, "Failed to encode DoH packet [%d]", d);
256     return CURLE_OUT_OF_MEMORY;
257   }
258 
259   p->dnstype = dnstype;
260   Curl_dyn_init(&p->serverdoh, DYN_DOH_RESPONSE);
261 
262   timeout_ms = Curl_timeleft(data, NULL, TRUE);
263   if(timeout_ms <= 0) {
264     result = CURLE_OPERATION_TIMEDOUT;
265     goto error;
266   }
267   /* Curl_open() is the internal version of curl_easy_init() */
268   result = Curl_open(&doh);
269   if(!result) {
270     /* pass in the struct pointer via a local variable to please coverity and
271        the gcc typecheck helpers */
272     struct dynbuf *resp = &p->serverdoh;
273     doh->state.internal = true;
274 #ifndef CURL_DISABLE_VERBOSE_STRINGS
275     doh->state.feat = &Curl_doh_trc;
276 #endif
277     ERROR_CHECK_SETOPT(CURLOPT_URL, url);
278     ERROR_CHECK_SETOPT(CURLOPT_DEFAULT_PROTOCOL, "https");
279     ERROR_CHECK_SETOPT(CURLOPT_WRITEFUNCTION, doh_write_cb);
280     ERROR_CHECK_SETOPT(CURLOPT_WRITEDATA, resp);
281     ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDS, p->dohbuffer);
282     ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDSIZE, (long)p->dohlen);
283     ERROR_CHECK_SETOPT(CURLOPT_HTTPHEADER, headers);
284 #ifdef USE_HTTP2
285     ERROR_CHECK_SETOPT(CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS);
286     ERROR_CHECK_SETOPT(CURLOPT_PIPEWAIT, 1L);
287 #endif
288 #ifndef CURLDEBUG
289     /* enforce HTTPS if not debug */
290     ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTPS);
291 #else
292     /* in debug mode, also allow http */
293     ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTP|CURLPROTO_HTTPS);
294 #endif
295     ERROR_CHECK_SETOPT(CURLOPT_TIMEOUT_MS, (long)timeout_ms);
296     ERROR_CHECK_SETOPT(CURLOPT_SHARE, data->share);
297     if(data->set.err && data->set.err != stderr)
298       ERROR_CHECK_SETOPT(CURLOPT_STDERR, data->set.err);
299     if(Curl_trc_ft_is_verbose(data, &Curl_doh_trc))
300       ERROR_CHECK_SETOPT(CURLOPT_VERBOSE, 1L);
301     if(data->set.no_signal)
302       ERROR_CHECK_SETOPT(CURLOPT_NOSIGNAL, 1L);
303 
304     ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYHOST,
305       data->set.doh_verifyhost ? 2L : 0L);
306     ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYPEER,
307       data->set.doh_verifypeer ? 1L : 0L);
308     ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYSTATUS,
309       data->set.doh_verifystatus ? 1L : 0L);
310 
311     /* Inherit *some* SSL options from the user's transfer. This is a
312        best-guess as to which options are needed for compatibility. #3661
313 
314        Note DoH does not inherit the user's proxy server so proxy SSL settings
315        have no effect and are not inherited. If that changes then two new
316        options should be added to check doh proxy insecure separately,
317        CURLOPT_DOH_PROXY_SSL_VERIFYHOST and CURLOPT_DOH_PROXY_SSL_VERIFYPEER.
318        */
319     if(data->set.ssl.falsestart)
320       ERROR_CHECK_SETOPT(CURLOPT_SSL_FALSESTART, 1L);
321     if(data->set.str[STRING_SSL_CAFILE]) {
322       ERROR_CHECK_SETOPT(CURLOPT_CAINFO,
323                          data->set.str[STRING_SSL_CAFILE]);
324     }
325     if(data->set.blobs[BLOB_CAINFO]) {
326       ERROR_CHECK_SETOPT(CURLOPT_CAINFO_BLOB,
327                          data->set.blobs[BLOB_CAINFO]);
328     }
329     if(data->set.str[STRING_SSL_CAPATH]) {
330       ERROR_CHECK_SETOPT(CURLOPT_CAPATH,
331                          data->set.str[STRING_SSL_CAPATH]);
332     }
333     if(data->set.str[STRING_SSL_CRLFILE]) {
334       ERROR_CHECK_SETOPT(CURLOPT_CRLFILE,
335                          data->set.str[STRING_SSL_CRLFILE]);
336     }
337     if(data->set.ssl.certinfo)
338       ERROR_CHECK_SETOPT(CURLOPT_CERTINFO, 1L);
339     if(data->set.ssl.fsslctx)
340       ERROR_CHECK_SETOPT(CURLOPT_SSL_CTX_FUNCTION, data->set.ssl.fsslctx);
341     if(data->set.ssl.fsslctxp)
342       ERROR_CHECK_SETOPT(CURLOPT_SSL_CTX_DATA, data->set.ssl.fsslctxp);
343     if(data->set.fdebug)
344       ERROR_CHECK_SETOPT(CURLOPT_DEBUGFUNCTION, data->set.fdebug);
345     if(data->set.debugdata)
346       ERROR_CHECK_SETOPT(CURLOPT_DEBUGDATA, data->set.debugdata);
347     if(data->set.str[STRING_SSL_EC_CURVES]) {
348       ERROR_CHECK_SETOPT(CURLOPT_SSL_EC_CURVES,
349                          data->set.str[STRING_SSL_EC_CURVES]);
350     }
351 
352     {
353       long mask =
354         (data->set.ssl.enable_beast ?
355          CURLSSLOPT_ALLOW_BEAST : 0) |
356         (data->set.ssl.no_revoke ?
357          CURLSSLOPT_NO_REVOKE : 0) |
358         (data->set.ssl.no_partialchain ?
359          CURLSSLOPT_NO_PARTIALCHAIN : 0) |
360         (data->set.ssl.revoke_best_effort ?
361          CURLSSLOPT_REVOKE_BEST_EFFORT : 0) |
362         (data->set.ssl.native_ca_store ?
363          CURLSSLOPT_NATIVE_CA : 0) |
364         (data->set.ssl.auto_client_cert ?
365          CURLSSLOPT_AUTO_CLIENT_CERT : 0);
366 
367       (void)curl_easy_setopt(doh, CURLOPT_SSL_OPTIONS, mask);
368     }
369 
370     doh->set.fmultidone = doh_done;
371     doh->set.dohfor = data; /* identify for which transfer this is done */
372     p->easy = doh;
373 
374     /* DoH handles must not inherit private_data. The handles may be passed to
375        the user via callbacks and the user will be able to identify them as
376        internal handles because private data is not set. The user can then set
377        private_data via CURLOPT_PRIVATE if they so choose. */
378     DEBUGASSERT(!doh->set.private_data);
379 
380     if(curl_multi_add_handle(multi, doh))
381       goto error;
382   }
383   else
384     goto error;
385   return CURLE_OK;
386 
387 error:
388   Curl_close(&doh);
389   return result;
390 }
391 
392 /*
393  * Curl_doh() resolves a name using DoH. It resolves a name and returns a
394  * 'Curl_addrinfo *' with the address information.
395  */
396 
Curl_doh(struct Curl_easy * data,const char * hostname,int port,int * waitp)397 struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
398                                const char *hostname,
399                                int port,
400                                int *waitp)
401 {
402   CURLcode result = CURLE_OK;
403   int slot;
404   struct dohdata *dohp;
405   struct connectdata *conn = data->conn;
406 #ifdef USE_HTTPSRR
407   /* for now, this is only used when ECH is enabled */
408 # ifdef USE_ECH
409   char *qname = NULL;
410 # endif
411 #endif
412   *waitp = FALSE;
413   (void)hostname;
414   (void)port;
415 
416   DEBUGASSERT(!data->req.doh);
417   DEBUGASSERT(conn);
418 
419   /* start clean, consider allocating this struct on demand */
420   dohp = data->req.doh = calloc(1, sizeof(struct dohdata));
421   if(!dohp)
422     return NULL;
423 
424   conn->bits.doh = TRUE;
425   dohp->host = hostname;
426   dohp->port = port;
427   dohp->headers =
428     curl_slist_append(NULL,
429                       "Content-Type: application/dns-message");
430   if(!dohp->headers)
431     goto error;
432 
433   /* create IPv4 DoH request */
434   result = dohprobe(data, &dohp->probe[DOH_PROBE_SLOT_IPADDR_V4],
435                     DNS_TYPE_A, hostname, data->set.str[STRING_DOH],
436                     data->multi, dohp->headers);
437   if(result)
438     goto error;
439   dohp->pending++;
440 
441 #ifdef USE_IPV6
442   if((conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
443     /* create IPv6 DoH request */
444     result = dohprobe(data, &dohp->probe[DOH_PROBE_SLOT_IPADDR_V6],
445                       DNS_TYPE_AAAA, hostname, data->set.str[STRING_DOH],
446                       data->multi, dohp->headers);
447     if(result)
448       goto error;
449     dohp->pending++;
450   }
451 #endif
452 
453 #ifdef USE_HTTPSRR
454   /*
455    * TODO: Figure out the conditions under which we want to make
456    * a request for an HTTPS RR when we are not doing ECH. For now,
457    * making this request breaks a bunch of DoH tests, e.g. test2100,
458    * where the addiitonal request doesn't match the pre-cooked data
459    * files, so there's a bit of work attached to making the request
460    * in a non-ECH use-case. For the present, we'll only make the
461    * request when ECH is enabled in the build and is being used for
462    * the curl operation.
463    */
464 # ifdef USE_ECH
465   if(data->set.tls_ech & CURLECH_ENABLE
466      || data->set.tls_ech & CURLECH_HARD) {
467     if(port == 443)
468       qname = strdup(hostname);
469     else
470       qname = aprintf("_%d._https.%s", port, hostname);
471     if(!qname)
472       goto error;
473     result = dohprobe(data, &dohp->probe[DOH_PROBE_SLOT_HTTPS],
474                       DNS_TYPE_HTTPS, qname, data->set.str[STRING_DOH],
475                       data->multi, dohp->headers);
476     free(qname);
477     if(result)
478       goto error;
479     dohp->pending++;
480   }
481 # endif
482 #endif
483   *waitp = TRUE; /* this never returns synchronously */
484   return NULL;
485 
486 error:
487   curl_slist_free_all(dohp->headers);
488   data->req.doh->headers = NULL;
489   for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) {
490     (void)curl_multi_remove_handle(data->multi, dohp->probe[slot].easy);
491     Curl_close(&dohp->probe[slot].easy);
492   }
493   Curl_safefree(data->req.doh);
494   return NULL;
495 }
496 
skipqname(const unsigned char * doh,size_t dohlen,unsigned int * indexp)497 static DOHcode skipqname(const unsigned char *doh, size_t dohlen,
498                          unsigned int *indexp)
499 {
500   unsigned char length;
501   do {
502     if(dohlen < (*indexp + 1))
503       return DOH_DNS_OUT_OF_RANGE;
504     length = doh[*indexp];
505     if((length & 0xc0) == 0xc0) {
506       /* name pointer, advance over it and be done */
507       if(dohlen < (*indexp + 2))
508         return DOH_DNS_OUT_OF_RANGE;
509       *indexp += 2;
510       break;
511     }
512     if(length & 0xc0)
513       return DOH_DNS_BAD_LABEL;
514     if(dohlen < (*indexp + 1 + length))
515       return DOH_DNS_OUT_OF_RANGE;
516     *indexp += (unsigned int)(1 + length);
517   } while(length);
518   return DOH_OK;
519 }
520 
get16bit(const unsigned char * doh,int index)521 static unsigned short get16bit(const unsigned char *doh, int index)
522 {
523   return (unsigned short)((doh[index] << 8) | doh[index + 1]);
524 }
525 
get32bit(const unsigned char * doh,int index)526 static unsigned int get32bit(const unsigned char *doh, int index)
527 {
528   /* make clang and gcc optimize this to bswap by incrementing
529      the pointer first. */
530   doh += index;
531 
532   /* avoid undefined behavior by casting to unsigned before shifting
533      24 bits, possibly into the sign bit. codegen is same, but
534      ub sanitizer won't be upset */
535   return ((unsigned)doh[0] << 24) | ((unsigned)doh[1] << 16) |
536          ((unsigned)doh[2] << 8) | doh[3];
537 }
538 
store_a(const unsigned char * doh,int index,struct dohentry * d)539 static DOHcode store_a(const unsigned char *doh, int index, struct dohentry *d)
540 {
541   /* silently ignore addresses over the limit */
542   if(d->numaddr < DOH_MAX_ADDR) {
543     struct dohaddr *a = &d->addr[d->numaddr];
544     a->type = DNS_TYPE_A;
545     memcpy(&a->ip.v4, &doh[index], 4);
546     d->numaddr++;
547   }
548   return DOH_OK;
549 }
550 
store_aaaa(const unsigned char * doh,int index,struct dohentry * d)551 static DOHcode store_aaaa(const unsigned char *doh,
552                           int index,
553                           struct dohentry *d)
554 {
555   /* silently ignore addresses over the limit */
556   if(d->numaddr < DOH_MAX_ADDR) {
557     struct dohaddr *a = &d->addr[d->numaddr];
558     a->type = DNS_TYPE_AAAA;
559     memcpy(&a->ip.v6, &doh[index], 16);
560     d->numaddr++;
561   }
562   return DOH_OK;
563 }
564 
565 #ifdef USE_HTTPSRR
store_https(const unsigned char * doh,int index,struct dohentry * d,uint16_t len)566 static DOHcode store_https(const unsigned char *doh,
567                            int index,
568                            struct dohentry *d,
569                            uint16_t len)
570 {
571   /* silently ignore RRs over the limit */
572   if(d->numhttps_rrs < DOH_MAX_HTTPS) {
573     struct dohhttps_rr *h = &d->https_rrs[d->numhttps_rrs];
574     h->val = Curl_memdup(&doh[index], len);
575     if(!h->val)
576       return DOH_OUT_OF_MEM;
577     h->len = len;
578     d->numhttps_rrs++;
579   }
580   return DOH_OK;
581 }
582 #endif
583 
store_cname(const unsigned char * doh,size_t dohlen,unsigned int index,struct dohentry * d)584 static DOHcode store_cname(const unsigned char *doh,
585                            size_t dohlen,
586                            unsigned int index,
587                            struct dohentry *d)
588 {
589   struct dynbuf *c;
590   unsigned int loop = 128; /* a valid DNS name can never loop this much */
591   unsigned char length;
592 
593   if(d->numcname == DOH_MAX_CNAME)
594     return DOH_OK; /* skip! */
595 
596   c = &d->cname[d->numcname++];
597   do {
598     if(index >= dohlen)
599       return DOH_DNS_OUT_OF_RANGE;
600     length = doh[index];
601     if((length & 0xc0) == 0xc0) {
602       int newpos;
603       /* name pointer, get the new offset (14 bits) */
604       if((index + 1) >= dohlen)
605         return DOH_DNS_OUT_OF_RANGE;
606 
607       /* move to the new index */
608       newpos = (length & 0x3f) << 8 | doh[index + 1];
609       index = newpos;
610       continue;
611     }
612     else if(length & 0xc0)
613       return DOH_DNS_BAD_LABEL; /* bad input */
614     else
615       index++;
616 
617     if(length) {
618       if(Curl_dyn_len(c)) {
619         if(Curl_dyn_addn(c, STRCONST(".")))
620           return DOH_OUT_OF_MEM;
621       }
622       if((index + length) > dohlen)
623         return DOH_DNS_BAD_LABEL;
624 
625       if(Curl_dyn_addn(c, &doh[index], length))
626         return DOH_OUT_OF_MEM;
627       index += length;
628     }
629   } while(length && --loop);
630 
631   if(!loop)
632     return DOH_DNS_LABEL_LOOP;
633   return DOH_OK;
634 }
635 
rdata(const unsigned char * doh,size_t dohlen,unsigned short rdlength,unsigned short type,int index,struct dohentry * d)636 static DOHcode rdata(const unsigned char *doh,
637                      size_t dohlen,
638                      unsigned short rdlength,
639                      unsigned short type,
640                      int index,
641                      struct dohentry *d)
642 {
643   /* RDATA
644      - A (TYPE 1):  4 bytes
645      - AAAA (TYPE 28): 16 bytes
646      - NS (TYPE 2): N bytes
647      - HTTPS (TYPE 65): N bytes */
648   DOHcode rc;
649 
650   switch(type) {
651   case DNS_TYPE_A:
652     if(rdlength != 4)
653       return DOH_DNS_RDATA_LEN;
654     rc = store_a(doh, index, d);
655     if(rc)
656       return rc;
657     break;
658   case DNS_TYPE_AAAA:
659     if(rdlength != 16)
660       return DOH_DNS_RDATA_LEN;
661     rc = store_aaaa(doh, index, d);
662     if(rc)
663       return rc;
664     break;
665 #ifdef USE_HTTPSRR
666   case DNS_TYPE_HTTPS:
667     rc = store_https(doh, index, d, rdlength);
668     if(rc)
669       return rc;
670     break;
671 #endif
672   case DNS_TYPE_CNAME:
673     rc = store_cname(doh, dohlen, index, d);
674     if(rc)
675       return rc;
676     break;
677   case DNS_TYPE_DNAME:
678     /* explicit for clarity; just skip; rely on synthesized CNAME  */
679     break;
680   default:
681     /* unsupported type, just skip it */
682     break;
683   }
684   return DOH_OK;
685 }
686 
de_init(struct dohentry * de)687 UNITTEST void de_init(struct dohentry *de)
688 {
689   int i;
690   memset(de, 0, sizeof(*de));
691   de->ttl = INT_MAX;
692   for(i = 0; i < DOH_MAX_CNAME; i++)
693     Curl_dyn_init(&de->cname[i], DYN_DOH_CNAME);
694 }
695 
696 
doh_decode(const unsigned char * doh,size_t dohlen,DNStype dnstype,struct dohentry * d)697 UNITTEST DOHcode doh_decode(const unsigned char *doh,
698                             size_t dohlen,
699                             DNStype dnstype,
700                             struct dohentry *d)
701 {
702   unsigned char rcode;
703   unsigned short qdcount;
704   unsigned short ancount;
705   unsigned short type = 0;
706   unsigned short rdlength;
707   unsigned short nscount;
708   unsigned short arcount;
709   unsigned int index = 12;
710   DOHcode rc;
711 
712   if(dohlen < 12)
713     return DOH_TOO_SMALL_BUFFER; /* too small */
714   if(!doh || doh[0] || doh[1])
715     return DOH_DNS_BAD_ID; /* bad ID */
716   rcode = doh[3] & 0x0f;
717   if(rcode)
718     return DOH_DNS_BAD_RCODE; /* bad rcode */
719 
720   qdcount = get16bit(doh, 4);
721   while(qdcount) {
722     rc = skipqname(doh, dohlen, &index);
723     if(rc)
724       return rc; /* bad qname */
725     if(dohlen < (index + 4))
726       return DOH_DNS_OUT_OF_RANGE;
727     index += 4; /* skip question's type and class */
728     qdcount--;
729   }
730 
731   ancount = get16bit(doh, 6);
732   while(ancount) {
733     unsigned short class;
734     unsigned int ttl;
735 
736     rc = skipqname(doh, dohlen, &index);
737     if(rc)
738       return rc; /* bad qname */
739 
740     if(dohlen < (index + 2))
741       return DOH_DNS_OUT_OF_RANGE;
742 
743     type = get16bit(doh, index);
744     if((type != DNS_TYPE_CNAME)    /* may be synthesized from DNAME */
745        && (type != DNS_TYPE_DNAME) /* if present, accept and ignore */
746        && (type != dnstype))
747       /* Not the same type as was asked for nor CNAME nor DNAME */
748       return DOH_DNS_UNEXPECTED_TYPE;
749     index += 2;
750 
751     if(dohlen < (index + 2))
752       return DOH_DNS_OUT_OF_RANGE;
753     class = get16bit(doh, index);
754     if(DNS_CLASS_IN != class)
755       return DOH_DNS_UNEXPECTED_CLASS; /* unsupported */
756     index += 2;
757 
758     if(dohlen < (index + 4))
759       return DOH_DNS_OUT_OF_RANGE;
760 
761     ttl = get32bit(doh, index);
762     if(ttl < d->ttl)
763       d->ttl = ttl;
764     index += 4;
765 
766     if(dohlen < (index + 2))
767       return DOH_DNS_OUT_OF_RANGE;
768 
769     rdlength = get16bit(doh, index);
770     index += 2;
771     if(dohlen < (index + rdlength))
772       return DOH_DNS_OUT_OF_RANGE;
773 
774     rc = rdata(doh, dohlen, rdlength, type, index, d);
775     if(rc)
776       return rc; /* bad rdata */
777     index += rdlength;
778     ancount--;
779   }
780 
781   nscount = get16bit(doh, 8);
782   while(nscount) {
783     rc = skipqname(doh, dohlen, &index);
784     if(rc)
785       return rc; /* bad qname */
786 
787     if(dohlen < (index + 8))
788       return DOH_DNS_OUT_OF_RANGE;
789 
790     index += 2 + 2 + 4; /* type, class and ttl */
791 
792     if(dohlen < (index + 2))
793       return DOH_DNS_OUT_OF_RANGE;
794 
795     rdlength = get16bit(doh, index);
796     index += 2;
797     if(dohlen < (index + rdlength))
798       return DOH_DNS_OUT_OF_RANGE;
799     index += rdlength;
800     nscount--;
801   }
802 
803   arcount = get16bit(doh, 10);
804   while(arcount) {
805     rc = skipqname(doh, dohlen, &index);
806     if(rc)
807       return rc; /* bad qname */
808 
809     if(dohlen < (index + 8))
810       return DOH_DNS_OUT_OF_RANGE;
811 
812     index += 2 + 2 + 4; /* type, class and ttl */
813 
814     if(dohlen < (index + 2))
815       return DOH_DNS_OUT_OF_RANGE;
816 
817     rdlength = get16bit(doh, index);
818     index += 2;
819     if(dohlen < (index + rdlength))
820       return DOH_DNS_OUT_OF_RANGE;
821     index += rdlength;
822     arcount--;
823   }
824 
825   if(index != dohlen)
826     return DOH_DNS_MALFORMAT; /* something is wrong */
827 
828 #ifdef USE_HTTTPS
829   if((type != DNS_TYPE_NS) && !d->numcname && !d->numaddr && !d->numhttps_rrs)
830 #else
831   if((type != DNS_TYPE_NS) && !d->numcname && !d->numaddr)
832 #endif
833     /* nothing stored! */
834     return DOH_NO_CONTENT;
835 
836   return DOH_OK; /* ok */
837 }
838 
839 #ifndef CURL_DISABLE_VERBOSE_STRINGS
showdoh(struct Curl_easy * data,const struct dohentry * d)840 static void showdoh(struct Curl_easy *data,
841                     const struct dohentry *d)
842 {
843   int i;
844   infof(data, "[DoH] TTL: %u seconds", d->ttl);
845   for(i = 0; i < d->numaddr; i++) {
846     const struct dohaddr *a = &d->addr[i];
847     if(a->type == DNS_TYPE_A) {
848       infof(data, "[DoH] A: %u.%u.%u.%u",
849             a->ip.v4[0], a->ip.v4[1],
850             a->ip.v4[2], a->ip.v4[3]);
851     }
852     else if(a->type == DNS_TYPE_AAAA) {
853       int j;
854       char buffer[128];
855       char *ptr;
856       size_t len;
857       len = msnprintf(buffer, 128, "[DoH] AAAA: ");
858       ptr = &buffer[len];
859       len = sizeof(buffer) - len;
860       for(j = 0; j < 16; j += 2) {
861         size_t l;
862         msnprintf(ptr, len, "%s%02x%02x", j?":":"", d->addr[i].ip.v6[j],
863                   d->addr[i].ip.v6[j + 1]);
864         l = strlen(ptr);
865         len -= l;
866         ptr += l;
867       }
868       infof(data, "%s", buffer);
869     }
870   }
871 #ifdef USE_HTTPSRR
872   for(i = 0; i < d->numhttps_rrs; i++) {
873 # ifdef CURLDEBUG
874     local_print_buf(data, "DoH HTTPS",
875                     d->https_rrs[i].val, d->https_rrs[i].len);
876 # else
877     infof(data, "DoH HTTPS RR: length %d", d->https_rrs[i].len);
878 # endif
879   }
880 #endif
881   for(i = 0; i < d->numcname; i++) {
882     infof(data, "CNAME: %s", Curl_dyn_ptr(&d->cname[i]));
883   }
884 }
885 #else
886 #define showdoh(x,y)
887 #endif
888 
889 /*
890  * doh2ai()
891  *
892  * This function returns a pointer to the first element of a newly allocated
893  * Curl_addrinfo struct linked list filled with the data from a set of DoH
894  * lookups.  Curl_addrinfo is meant to work like the addrinfo struct does for
895  * a IPv6 stack, but usable also for IPv4, all hosts and environments.
896  *
897  * The memory allocated by this function *MUST* be free'd later on calling
898  * Curl_freeaddrinfo().  For each successful call to this function there
899  * must be an associated call later to Curl_freeaddrinfo().
900  */
901 
doh2ai(const struct dohentry * de,const char * hostname,int port,struct Curl_addrinfo ** aip)902 static CURLcode doh2ai(const struct dohentry *de, const char *hostname,
903                        int port, struct Curl_addrinfo **aip)
904 {
905   struct Curl_addrinfo *ai;
906   struct Curl_addrinfo *prevai = NULL;
907   struct Curl_addrinfo *firstai = NULL;
908   struct sockaddr_in *addr;
909 #ifdef USE_IPV6
910   struct sockaddr_in6 *addr6;
911 #endif
912   CURLcode result = CURLE_OK;
913   int i;
914   size_t hostlen = strlen(hostname) + 1; /* include null-terminator */
915 
916   DEBUGASSERT(de);
917 
918   if(!de->numaddr)
919     return CURLE_COULDNT_RESOLVE_HOST;
920 
921   for(i = 0; i < de->numaddr; i++) {
922     size_t ss_size;
923     CURL_SA_FAMILY_T addrtype;
924     if(de->addr[i].type == DNS_TYPE_AAAA) {
925 #ifndef USE_IPV6
926       /* we can't handle IPv6 addresses */
927       continue;
928 #else
929       ss_size = sizeof(struct sockaddr_in6);
930       addrtype = AF_INET6;
931 #endif
932     }
933     else {
934       ss_size = sizeof(struct sockaddr_in);
935       addrtype = AF_INET;
936     }
937 
938     ai = calloc(1, sizeof(struct Curl_addrinfo) + ss_size + hostlen);
939     if(!ai) {
940       result = CURLE_OUT_OF_MEMORY;
941       break;
942     }
943     ai->ai_addr = (void *)((char *)ai + sizeof(struct Curl_addrinfo));
944     ai->ai_canonname = (void *)((char *)ai->ai_addr + ss_size);
945     memcpy(ai->ai_canonname, hostname, hostlen);
946 
947     if(!firstai)
948       /* store the pointer we want to return from this function */
949       firstai = ai;
950 
951     if(prevai)
952       /* make the previous entry point to this */
953       prevai->ai_next = ai;
954 
955     ai->ai_family = addrtype;
956 
957     /* we return all names as STREAM, so when using this address for TFTP
958        the type must be ignored and conn->socktype be used instead! */
959     ai->ai_socktype = SOCK_STREAM;
960 
961     ai->ai_addrlen = (curl_socklen_t)ss_size;
962 
963     /* leave the rest of the struct filled with zero */
964 
965     switch(ai->ai_family) {
966     case AF_INET:
967       addr = (void *)ai->ai_addr; /* storage area for this info */
968       DEBUGASSERT(sizeof(struct in_addr) == sizeof(de->addr[i].ip.v4));
969       memcpy(&addr->sin_addr, &de->addr[i].ip.v4, sizeof(struct in_addr));
970       addr->sin_family = addrtype;
971       addr->sin_port = htons((unsigned short)port);
972       break;
973 
974 #ifdef USE_IPV6
975     case AF_INET6:
976       addr6 = (void *)ai->ai_addr; /* storage area for this info */
977       DEBUGASSERT(sizeof(struct in6_addr) == sizeof(de->addr[i].ip.v6));
978       memcpy(&addr6->sin6_addr, &de->addr[i].ip.v6, sizeof(struct in6_addr));
979       addr6->sin6_family = addrtype;
980       addr6->sin6_port = htons((unsigned short)port);
981       break;
982 #endif
983     }
984 
985     prevai = ai;
986   }
987 
988   if(result) {
989     Curl_freeaddrinfo(firstai);
990     firstai = NULL;
991   }
992   *aip = firstai;
993 
994   return result;
995 }
996 
997 #ifndef CURL_DISABLE_VERBOSE_STRINGS
type2name(DNStype dnstype)998 static const char *type2name(DNStype dnstype)
999 {
1000   switch(dnstype) {
1001     case DNS_TYPE_A:
1002       return "A";
1003     case DNS_TYPE_AAAA:
1004       return "AAAA";
1005 #ifdef USE_HTTPSRR
1006     case DNS_TYPE_HTTPS:
1007       return "HTTPS";
1008 #endif
1009     default:
1010        return "unknown";
1011   }
1012 }
1013 #endif
1014 
de_cleanup(struct dohentry * d)1015 UNITTEST void de_cleanup(struct dohentry *d)
1016 {
1017   int i = 0;
1018   for(i = 0; i < d->numcname; i++) {
1019     Curl_dyn_free(&d->cname[i]);
1020   }
1021 #ifdef USE_HTTPSRR
1022   for(i = 0; i < d->numhttps_rrs; i++)
1023     free(d->https_rrs[i].val);
1024 #endif
1025 }
1026 
1027 #ifdef USE_HTTPSRR
1028 
1029 /*
1030  * @brief decode the DNS name in a binary RRData
1031  * @param buf points to the buffer (in/out)
1032  * @param remaining points to the remaining buffer length (in/out)
1033  * @param dnsname returns the string form name on success
1034  * @return is 1 for success, error otherwise
1035  *
1036  * The encoding here is defined in
1037  * https://tools.ietf.org/html/rfc1035#section-3.1
1038  *
1039  * The input buffer pointer will be modified so it points to
1040  * just after the end of the DNS name encoding on output. (And
1041  * that's why it's an "unsigned char **" :-)
1042  */
local_decode_rdata_name(unsigned char ** buf,size_t * remaining,char ** dnsname)1043 static CURLcode local_decode_rdata_name(unsigned char **buf, size_t *remaining,
1044                                         char **dnsname)
1045 {
1046   unsigned char *cp = NULL;
1047   int rem = 0;
1048   char *thename = NULL, *tp = NULL;
1049   unsigned char clen = 0; /* chunk len */
1050 
1051   if(!buf || !remaining || !dnsname)
1052     return CURLE_OUT_OF_MEMORY;
1053   rem = (int)*remaining;
1054   thename = calloc(1, CURL_MAXLEN_host_name);
1055   if(!thename)
1056     return CURLE_OUT_OF_MEMORY;
1057   cp = *buf;
1058   tp = thename;
1059   clen = *cp++;
1060   if(clen == 0) {
1061     /* special case - return "." as name */
1062     thename[0] = '.';
1063     thename[1] = 0x00;
1064   }
1065   while(clen) {
1066     if(clen >= rem) {
1067       free(thename);
1068       return CURLE_OUT_OF_MEMORY;
1069     }
1070     if(((tp - thename) + clen) > CURL_MAXLEN_host_name) {
1071       free(thename);
1072       return CURLE_OUT_OF_MEMORY;
1073     }
1074     memcpy(tp, cp, clen);
1075     tp += clen;
1076     *tp++ = '.';
1077     cp += clen;
1078     rem -= (clen + 1);
1079     if(rem <= 0) {
1080       free(thename);
1081       return CURLE_OUT_OF_MEMORY;
1082     }
1083     clen = *cp++;
1084   }
1085   *buf = cp;
1086   if(rem <= 0) {
1087     free(thename);
1088     return CURLE_OUT_OF_MEMORY;
1089   }
1090   *remaining = rem - 1;
1091   *dnsname = thename;
1092   return CURLE_OK;
1093 }
1094 
local_decode_rdata_alpn(unsigned char * rrval,size_t len,char ** alpns)1095 static CURLcode local_decode_rdata_alpn(unsigned char *rrval, size_t len,
1096                                         char **alpns)
1097 {
1098   /*
1099    * spec here is as per draft-ietf-dnsop-svcb-https, section-7.1.1
1100    * encoding is catenated list of strings each preceded by a one
1101    * octet length
1102    * output is comma-sep list of the strings
1103    * implementations may or may not handle quoting of comma within
1104    * string values, so we might see a comma within the wire format
1105    * version of a string, in which case we'll precede that by a
1106    * backslash - same goes for a backslash character, and of course
1107    * we need to use two backslashes in strings when we mean one;-)
1108    */
1109   int remaining = (int) len;
1110   char *oval;
1111   size_t olen = 0, i;
1112   unsigned char *cp = rrval;
1113   struct dynbuf dval;
1114 
1115   if(!alpns)
1116     return CURLE_OUT_OF_MEMORY;
1117   Curl_dyn_init(&dval, DYN_DOH_RESPONSE);
1118   remaining = (int)len;
1119   cp = rrval;
1120   while(remaining > 0) {
1121     size_t tlen = (size_t) *cp++;
1122 
1123     /* if not 1st time, add comma */
1124     if(remaining != (int)len && Curl_dyn_addn(&dval, ",", 1))
1125       goto err;
1126     remaining--;
1127     if(tlen > (size_t)remaining)
1128       goto err;
1129     /* add escape char if needed, clunky but easier to read */
1130     for(i = 0; i != tlen; i++) {
1131       if('\\' == *cp || ',' == *cp) {
1132         if(Curl_dyn_addn(&dval, "\\", 1))
1133           goto err;
1134       }
1135       if(Curl_dyn_addn(&dval, cp++, 1))
1136         goto err;
1137     }
1138     remaining -= (int)tlen;
1139   }
1140   olen = Curl_dyn_len(&dval);
1141   /* I think the + 1 here is ok but it could trigger a read error */
1142   oval = (char *)Curl_memdup(Curl_dyn_ptr(&dval), olen + 1);
1143   if(!oval)
1144     goto err;
1145   Curl_dyn_free(&dval);
1146   oval[olen]='\0';
1147   *alpns = oval;
1148   return CURLE_OK;
1149 err:
1150   Curl_dyn_free(&dval);
1151   return CURLE_BAD_CONTENT_ENCODING;
1152 }
1153 
1154 #ifdef CURLDEBUG
test_alpn_escapes(void)1155 static CURLcode test_alpn_escapes(void)
1156 {
1157   /* we'll use an example from draft-ietf-dnsop-svcb, figure 10 */
1158   static unsigned char example[] = {
1159     0x08,                                           /* length 8 */
1160     0x66, 0x5c, 0x6f, 0x6f, 0x2c, 0x62, 0x61, 0x72, /* value "f\\oo,bar" */
1161     0x02,                                           /* length 2 */
1162     0x68, 0x32                                      /* value "h2" */
1163   };
1164   size_t example_len = sizeof(example);
1165   char *aval = NULL;
1166   static const char *expected = "f\\\\oo\\,bar,h2";
1167 
1168   if(local_decode_rdata_alpn(example, example_len, &aval) != CURLE_OK)
1169     return CURLE_BAD_CONTENT_ENCODING;
1170   if(strlen(aval) != strlen(expected))
1171     return CURLE_BAD_CONTENT_ENCODING;
1172   if(memcmp(aval, expected, strlen(aval)))
1173     return CURLE_BAD_CONTENT_ENCODING;
1174   return CURLE_OK;
1175 }
1176 #endif
1177 
Curl_doh_decode_httpsrr(unsigned char * rrval,size_t len,struct Curl_https_rrinfo ** hrr)1178 static CURLcode Curl_doh_decode_httpsrr(unsigned char *rrval, size_t len,
1179                                         struct Curl_https_rrinfo **hrr)
1180 {
1181   size_t remaining = len;
1182   unsigned char *cp = rrval;
1183   uint16_t pcode = 0, plen = 0;
1184   struct Curl_https_rrinfo *lhrr = NULL;
1185   char *dnsname = NULL;
1186 
1187 #ifdef CURLDEBUG
1188   /* a few tests of escaping, shouldn't be here but ok for now */
1189   if(test_alpn_escapes() != CURLE_OK)
1190     return CURLE_OUT_OF_MEMORY;
1191 #endif
1192   lhrr = calloc(1, sizeof(struct Curl_https_rrinfo));
1193   if(!lhrr)
1194     return CURLE_OUT_OF_MEMORY;
1195   lhrr->val = calloc(1, len);
1196   if(!lhrr->val)
1197     goto err;
1198   lhrr->len = len;
1199   memcpy(lhrr->val, rrval, len);
1200   if(remaining <= 2)
1201     goto err;
1202   lhrr->priority = (uint16_t)((cp[0] << 8) + cp[1]);
1203   cp += 2;
1204   remaining -= (uint16_t)2;
1205   if(local_decode_rdata_name(&cp, &remaining, &dnsname) != CURLE_OK)
1206     goto err;
1207   lhrr->target = dnsname;
1208   while(remaining >= 4) {
1209     pcode = (uint16_t)((*cp << 8) + (*(cp + 1)));
1210     cp += 2;
1211     plen = (uint16_t)((*cp << 8) + (*(cp + 1)));
1212     cp += 2;
1213     remaining -= 4;
1214     if(pcode == HTTPS_RR_CODE_ALPN) {
1215       if(local_decode_rdata_alpn(cp, plen, &lhrr->alpns) != CURLE_OK)
1216         goto err;
1217     }
1218     if(pcode == HTTPS_RR_CODE_NO_DEF_ALPN)
1219       lhrr->no_def_alpn = TRUE;
1220     else if(pcode == HTTPS_RR_CODE_IPV4) {
1221       lhrr->ipv4hints = Curl_memdup(cp, plen);
1222       if(!lhrr->ipv4hints)
1223         goto err;
1224       lhrr->ipv4hints_len = (size_t)plen;
1225     }
1226     else if(pcode == HTTPS_RR_CODE_ECH) {
1227       lhrr->echconfiglist = Curl_memdup(cp, plen);
1228       if(!lhrr->echconfiglist)
1229         goto err;
1230       lhrr->echconfiglist_len = (size_t)plen;
1231     }
1232     else if(pcode == HTTPS_RR_CODE_IPV6) {
1233       lhrr->ipv6hints = Curl_memdup(cp, plen);
1234       if(!lhrr->ipv6hints)
1235         goto err;
1236       lhrr->ipv6hints_len = (size_t)plen;
1237     }
1238     if(plen > 0 && plen <= remaining) {
1239       cp += plen;
1240       remaining -= plen;
1241     }
1242   }
1243   DEBUGASSERT(!remaining);
1244   *hrr = lhrr;
1245   return CURLE_OK;
1246 err:
1247   if(lhrr) {
1248     if(lhrr->target)
1249       free(lhrr->target);
1250     if(lhrr->echconfiglist)
1251       free(lhrr->echconfiglist);
1252     if(lhrr->val)
1253       free(lhrr->val);
1254     free(lhrr);
1255   }
1256   return CURLE_OUT_OF_MEMORY;
1257 }
1258 
1259 # ifdef CURLDEBUG
local_print_httpsrr(struct Curl_easy * data,struct Curl_https_rrinfo * hrr)1260 static void local_print_httpsrr(struct Curl_easy *data,
1261                                 struct Curl_https_rrinfo *hrr)
1262 {
1263   DEBUGASSERT(hrr);
1264   infof(data, "HTTPS RR: priority %d, target: %s",
1265         hrr->priority, hrr->target);
1266   if(hrr->alpns)
1267     infof(data, "HTTPS RR: alpns %s", hrr->alpns);
1268   else
1269     infof(data, "HTTPS RR: no alpns");
1270   if(hrr->no_def_alpn)
1271     infof(data, "HTTPS RR: no_def_alpn set");
1272   else
1273     infof(data, "HTTPS RR: no_def_alpn not set");
1274   if(hrr->ipv4hints) {
1275     local_print_buf(data, "HTTPS RR: ipv4hints",
1276                     hrr->ipv4hints, hrr->ipv4hints_len);
1277   }
1278   else
1279     infof(data, "HTTPS RR: no ipv4hints");
1280   if(hrr->echconfiglist) {
1281     local_print_buf(data, "HTTPS RR: ECHConfigList",
1282                     hrr->echconfiglist, hrr->echconfiglist_len);
1283   }
1284   else
1285     infof(data, "HTTPS RR: no ECHConfigList");
1286   if(hrr->ipv6hints) {
1287     local_print_buf(data, "HTTPS RR: ipv6hint",
1288                     hrr->ipv6hints, hrr->ipv6hints_len);
1289   }
1290   else
1291     infof(data, "HTTPS RR: no ipv6hints");
1292   return;
1293 }
1294 # endif
1295 #endif
1296 
Curl_doh_is_resolved(struct Curl_easy * data,struct Curl_dns_entry ** dnsp)1297 CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
1298                               struct Curl_dns_entry **dnsp)
1299 {
1300   CURLcode result;
1301   struct dohdata *dohp = data->req.doh;
1302   *dnsp = NULL; /* defaults to no response */
1303   if(!dohp)
1304     return CURLE_OUT_OF_MEMORY;
1305 
1306   if(!dohp->probe[DOH_PROBE_SLOT_IPADDR_V4].easy &&
1307      !dohp->probe[DOH_PROBE_SLOT_IPADDR_V6].easy) {
1308     failf(data, "Could not DoH-resolve: %s", data->state.async.hostname);
1309     return CONN_IS_PROXIED(data->conn)?CURLE_COULDNT_RESOLVE_PROXY:
1310       CURLE_COULDNT_RESOLVE_HOST;
1311   }
1312   else if(!dohp->pending) {
1313 #ifndef USE_HTTPSRR
1314     DOHcode rc[DOH_PROBE_SLOTS] = {
1315       DOH_OK, DOH_OK
1316     };
1317 #else
1318     DOHcode rc[DOH_PROBE_SLOTS] = {
1319       DOH_OK, DOH_OK, DOH_OK
1320     };
1321 #endif
1322     struct dohentry de;
1323     int slot;
1324     /* remove DoH handles from multi handle and close them */
1325     for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) {
1326       curl_multi_remove_handle(data->multi, dohp->probe[slot].easy);
1327       Curl_close(&dohp->probe[slot].easy);
1328     }
1329     /* parse the responses, create the struct and return it! */
1330     de_init(&de);
1331     for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) {
1332       struct dnsprobe *p = &dohp->probe[slot];
1333       if(!p->dnstype)
1334         continue;
1335       rc[slot] = doh_decode(Curl_dyn_uptr(&p->serverdoh),
1336                             Curl_dyn_len(&p->serverdoh),
1337                             p->dnstype,
1338                             &de);
1339       Curl_dyn_free(&p->serverdoh);
1340 #ifndef CURL_DISABLE_VERBOSE_STRINGS
1341       if(rc[slot]) {
1342         infof(data, "DoH: %s type %s for %s", doh_strerror(rc[slot]),
1343               type2name(p->dnstype), dohp->host);
1344       }
1345 #endif
1346     } /* next slot */
1347 
1348     result = CURLE_COULDNT_RESOLVE_HOST; /* until we know better */
1349     if(!rc[DOH_PROBE_SLOT_IPADDR_V4] || !rc[DOH_PROBE_SLOT_IPADDR_V6]) {
1350       /* we have an address, of one kind or other */
1351       struct Curl_dns_entry *dns;
1352       struct Curl_addrinfo *ai;
1353 
1354 
1355       if(Curl_trc_ft_is_verbose(data, &Curl_doh_trc)) {
1356         infof(data, "[DoH] Host name: %s", dohp->host);
1357         showdoh(data, &de);
1358       }
1359 
1360       result = doh2ai(&de, dohp->host, dohp->port, &ai);
1361       if(result) {
1362         de_cleanup(&de);
1363         return result;
1364       }
1365 
1366       if(data->share)
1367         Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
1368 
1369       /* we got a response, store it in the cache */
1370       dns = Curl_cache_addr(data, ai, dohp->host, 0, dohp->port);
1371 
1372       if(data->share)
1373         Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
1374 
1375       if(!dns) {
1376         /* returned failure, bail out nicely */
1377         Curl_freeaddrinfo(ai);
1378       }
1379       else {
1380         data->state.async.dns = dns;
1381         *dnsp = dns;
1382         result = CURLE_OK;      /* address resolution OK */
1383       }
1384     } /* address processing done */
1385 
1386     /* Now process any build-specific attributes retrieved from DNS */
1387 #ifdef USE_HTTPSRR
1388     if(de.numhttps_rrs > 0 && result == CURLE_OK && *dnsp) {
1389       struct Curl_https_rrinfo *hrr = NULL;
1390       result = Curl_doh_decode_httpsrr(de.https_rrs->val, de.https_rrs->len,
1391                                        &hrr);
1392       if(result) {
1393         infof(data, "Failed to decode HTTPS RR");
1394         return result;
1395       }
1396       infof(data, "Some HTTPS RR to process");
1397 # ifdef CURLDEBUG
1398       local_print_httpsrr(data, hrr);
1399 # endif
1400       (*dnsp)->hinfo = hrr;
1401     }
1402 #endif
1403 
1404     /* All done */
1405     de_cleanup(&de);
1406     Curl_safefree(data->req.doh);
1407     return result;
1408 
1409   } /* !dohp->pending */
1410 
1411   /* else wait for pending DoH transactions to complete */
1412   return CURLE_OK;
1413 }
1414 
1415 #endif /* CURL_DISABLE_DOH */
1416