• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 /* doh_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_req_encode(const char * host,DNStype dnstype,unsigned char * dnsp,size_t len,size_t * olen)85 UNITTEST DOHcode doh_req_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 hostname.
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 hostname ends with a trailing dot, the corresponding
104    * QNAME-encoding is one byte longer than the hostname. 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 hostname.
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(char * contents,size_t size,size_t nmemb,void * userp)183 doh_write_cb(char *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(DEBUGBUILD)
doh_print_buf(struct Curl_easy * data,const char * prefix,unsigned char * buf,size_t len)195 static void doh_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; /* the transfer that asked for the DoH probe */
218 
219   data = Curl_multi_get_handle(doh->multi, doh->set.dohfor_mid);
220   if(!data) {
221     DEBUGF(infof(doh, "doh_done: xfer for mid=%" FMT_OFF_T
222                  " not found", doh->set.dohfor_mid));
223     DEBUGASSERT(0);
224   }
225   else {
226     struct doh_probes *dohp = data->req.doh;
227     /* one of the DoH request done for the 'data' transfer is now complete! */
228     dohp->pending--;
229     infof(doh, "a DoH request is completed, %u to go", dohp->pending);
230     if(result)
231       infof(doh, "DoH request %s", curl_easy_strerror(result));
232 
233     if(!dohp->pending) {
234       /* DoH completed, run the transfer picking up the results */
235       Curl_expire(data, 0, EXPIRE_RUN_NOW);
236     }
237   }
238   return 0;
239 }
240 
241 #define ERROR_CHECK_SETOPT(x,y)                         \
242   do {                                                  \
243     result = curl_easy_setopt((CURL *)doh, x, y);       \
244     if(result &&                                        \
245        result != CURLE_NOT_BUILT_IN &&                  \
246        result != CURLE_UNKNOWN_OPTION)                  \
247       goto error;                                       \
248   } while(0)
249 
doh_run_probe(struct Curl_easy * data,struct doh_probe * p,DNStype dnstype,const char * host,const char * url,CURLM * multi,struct curl_slist * headers)250 static CURLcode doh_run_probe(struct Curl_easy *data,
251                               struct doh_probe *p, DNStype dnstype,
252                               const char *host,
253                               const char *url, CURLM *multi,
254                               struct curl_slist *headers)
255 {
256   struct Curl_easy *doh = NULL;
257   CURLcode result = CURLE_OK;
258   timediff_t timeout_ms;
259   DOHcode d = doh_req_encode(host, dnstype, p->req_body, sizeof(p->req_body),
260                              &p->req_body_len);
261   if(d) {
262     failf(data, "Failed to encode DoH packet [%d]", d);
263     return CURLE_OUT_OF_MEMORY;
264   }
265 
266   p->dnstype = dnstype;
267   Curl_dyn_init(&p->resp_body, DYN_DOH_RESPONSE);
268 
269   timeout_ms = Curl_timeleft(data, NULL, TRUE);
270   if(timeout_ms <= 0) {
271     result = CURLE_OPERATION_TIMEDOUT;
272     goto error;
273   }
274   /* Curl_open() is the internal version of curl_easy_init() */
275   result = Curl_open(&doh);
276   if(result)
277     goto error;
278 
279   /* pass in the struct pointer via a local variable to please coverity and
280      the gcc typecheck helpers */
281   doh->state.internal = TRUE;
282 #ifndef CURL_DISABLE_VERBOSE_STRINGS
283   doh->state.feat = &Curl_doh_trc;
284 #endif
285   ERROR_CHECK_SETOPT(CURLOPT_URL, url);
286   ERROR_CHECK_SETOPT(CURLOPT_DEFAULT_PROTOCOL, "https");
287   ERROR_CHECK_SETOPT(CURLOPT_WRITEFUNCTION, doh_write_cb);
288   ERROR_CHECK_SETOPT(CURLOPT_WRITEDATA, &p->resp_body);
289   ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDS, p->req_body);
290   ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDSIZE, (long)p->req_body_len);
291   ERROR_CHECK_SETOPT(CURLOPT_HTTPHEADER, headers);
292 #ifdef USE_HTTP2
293   ERROR_CHECK_SETOPT(CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS);
294   ERROR_CHECK_SETOPT(CURLOPT_PIPEWAIT, 1L);
295 #endif
296 #ifndef DEBUGBUILD
297   /* enforce HTTPS if not debug */
298   ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTPS);
299 #else
300   /* in debug mode, also allow http */
301   ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTP|CURLPROTO_HTTPS);
302 #endif
303   ERROR_CHECK_SETOPT(CURLOPT_TIMEOUT_MS, (long)timeout_ms);
304   ERROR_CHECK_SETOPT(CURLOPT_SHARE, (CURLSH *)data->share);
305   if(data->set.err && data->set.err != stderr)
306     ERROR_CHECK_SETOPT(CURLOPT_STDERR, data->set.err);
307   if(Curl_trc_ft_is_verbose(data, &Curl_doh_trc))
308     ERROR_CHECK_SETOPT(CURLOPT_VERBOSE, 1L);
309   if(data->set.no_signal)
310     ERROR_CHECK_SETOPT(CURLOPT_NOSIGNAL, 1L);
311 
312   ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYHOST,
313     data->set.doh_verifyhost ? 2L : 0L);
314   ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYPEER,
315     data->set.doh_verifypeer ? 1L : 0L);
316   ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYSTATUS,
317     data->set.doh_verifystatus ? 1L : 0L);
318 
319   /* Inherit *some* SSL options from the user's transfer. This is a
320      best-guess as to which options are needed for compatibility. #3661
321 
322      Note DoH does not inherit the user's proxy server so proxy SSL settings
323      have no effect and are not inherited. If that changes then two new
324      options should be added to check doh proxy insecure separately,
325      CURLOPT_DOH_PROXY_SSL_VERIFYHOST and CURLOPT_DOH_PROXY_SSL_VERIFYPEER.
326      */
327   if(data->set.ssl.falsestart)
328     ERROR_CHECK_SETOPT(CURLOPT_SSL_FALSESTART, 1L);
329   if(data->set.str[STRING_SSL_CAFILE]) {
330     ERROR_CHECK_SETOPT(CURLOPT_CAINFO,
331                        data->set.str[STRING_SSL_CAFILE]);
332   }
333   if(data->set.blobs[BLOB_CAINFO]) {
334     ERROR_CHECK_SETOPT(CURLOPT_CAINFO_BLOB,
335                        data->set.blobs[BLOB_CAINFO]);
336   }
337   if(data->set.str[STRING_SSL_CAPATH]) {
338     ERROR_CHECK_SETOPT(CURLOPT_CAPATH,
339                        data->set.str[STRING_SSL_CAPATH]);
340   }
341   if(data->set.str[STRING_SSL_CRLFILE]) {
342     ERROR_CHECK_SETOPT(CURLOPT_CRLFILE,
343                        data->set.str[STRING_SSL_CRLFILE]);
344   }
345   if(data->set.ssl.certinfo)
346     ERROR_CHECK_SETOPT(CURLOPT_CERTINFO, 1L);
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   if(data->set.fdebug)
352     ERROR_CHECK_SETOPT(CURLOPT_DEBUGFUNCTION, data->set.fdebug);
353   if(data->set.debugdata)
354     ERROR_CHECK_SETOPT(CURLOPT_DEBUGDATA, data->set.debugdata);
355   if(data->set.str[STRING_SSL_EC_CURVES]) {
356     ERROR_CHECK_SETOPT(CURLOPT_SSL_EC_CURVES,
357                        data->set.str[STRING_SSL_EC_CURVES]);
358   }
359 
360   {
361     long mask =
362       (data->set.ssl.enable_beast ?
363        CURLSSLOPT_ALLOW_BEAST : 0) |
364       (data->set.ssl.no_revoke ?
365        CURLSSLOPT_NO_REVOKE : 0) |
366       (data->set.ssl.no_partialchain ?
367        CURLSSLOPT_NO_PARTIALCHAIN : 0) |
368       (data->set.ssl.revoke_best_effort ?
369        CURLSSLOPT_REVOKE_BEST_EFFORT : 0) |
370       (data->set.ssl.native_ca_store ?
371        CURLSSLOPT_NATIVE_CA : 0) |
372       (data->set.ssl.auto_client_cert ?
373        CURLSSLOPT_AUTO_CLIENT_CERT : 0);
374 
375     (void)curl_easy_setopt(doh, CURLOPT_SSL_OPTIONS, mask);
376   }
377 
378   doh->set.fmultidone = doh_done;
379   doh->set.dohfor_mid = data->mid; /* for which transfer this is done */
380 
381   /* DoH handles must not inherit private_data. The handles may be passed to
382      the user via callbacks and the user will be able to identify them as
383      internal handles because private data is not set. The user can then set
384      private_data via CURLOPT_PRIVATE if they so choose. */
385   DEBUGASSERT(!doh->set.private_data);
386 
387   if(curl_multi_add_handle(multi, doh))
388     goto error;
389 
390   p->easy_mid = doh->mid;
391   return CURLE_OK;
392 
393 error:
394   Curl_close(&doh);
395   p->easy_mid = -1;
396   return result;
397 }
398 
399 /*
400  * Curl_doh() resolves a name using DoH. It resolves a name and returns a
401  * 'Curl_addrinfo *' with the address information.
402  */
403 
Curl_doh(struct Curl_easy * data,const char * hostname,int port,int * waitp)404 struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
405                                const char *hostname,
406                                int port,
407                                int *waitp)
408 {
409   CURLcode result = CURLE_OK;
410   struct doh_probes *dohp;
411   struct connectdata *conn = data->conn;
412   size_t i;
413   *waitp = FALSE;
414   (void)hostname;
415   (void)port;
416 
417   DEBUGASSERT(!data->req.doh);
418   DEBUGASSERT(conn);
419 
420   /* start clean, consider allocating this struct on demand */
421   dohp = data->req.doh = calloc(1, sizeof(struct doh_probes));
422   if(!dohp)
423     return NULL;
424 
425   for(i = 0; i < DOH_SLOT_COUNT; ++i) {
426     dohp->probe[i].easy_mid = -1;
427   }
428 
429   conn->bits.doh = TRUE;
430   dohp->host = hostname;
431   dohp->port = port;
432   dohp->req_hds =
433     curl_slist_append(NULL,
434                       "Content-Type: application/dns-message");
435   if(!dohp->req_hds)
436     goto error;
437 
438   /* create IPv4 DoH request */
439   result = doh_run_probe(data, &dohp->probe[DOH_SLOT_IPV4],
440                          DNS_TYPE_A, hostname, data->set.str[STRING_DOH],
441                          data->multi, dohp->req_hds);
442   if(result)
443     goto error;
444   dohp->pending++;
445 
446 #ifdef USE_IPV6
447   if((conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
448     /* create IPv6 DoH request */
449     result = doh_run_probe(data, &dohp->probe[DOH_SLOT_IPV6],
450                            DNS_TYPE_AAAA, hostname, data->set.str[STRING_DOH],
451                            data->multi, dohp->req_hds);
452     if(result)
453       goto error;
454     dohp->pending++;
455   }
456 #endif
457 
458 #ifdef USE_HTTPSRR
459   if(conn->handler->protocol & PROTO_FAMILY_HTTP) {
460     /* Only use HTTPS RR for HTTP(S) transfers */
461     char *qname = NULL;
462     if(port != PORT_HTTPS) {
463       qname = aprintf("_%d._https.%s", port, hostname);
464       if(!qname)
465         goto error;
466     }
467     result = doh_run_probe(data, &dohp->probe[DOH_SLOT_HTTPS_RR],
468                            DNS_TYPE_HTTPS,
469                            qname ? qname : hostname, data->set.str[STRING_DOH],
470                            data->multi, dohp->req_hds);
471     free(qname);
472     if(result)
473       goto error;
474     dohp->pending++;
475   }
476 #endif
477   *waitp = TRUE; /* this never returns synchronously */
478   return NULL;
479 
480 error:
481   Curl_doh_cleanup(data);
482   return NULL;
483 }
484 
doh_skipqname(const unsigned char * doh,size_t dohlen,unsigned int * indexp)485 static DOHcode doh_skipqname(const unsigned char *doh, size_t dohlen,
486                              unsigned int *indexp)
487 {
488   unsigned char length;
489   do {
490     if(dohlen < (*indexp + 1))
491       return DOH_DNS_OUT_OF_RANGE;
492     length = doh[*indexp];
493     if((length & 0xc0) == 0xc0) {
494       /* name pointer, advance over it and be done */
495       if(dohlen < (*indexp + 2))
496         return DOH_DNS_OUT_OF_RANGE;
497       *indexp += 2;
498       break;
499     }
500     if(length & 0xc0)
501       return DOH_DNS_BAD_LABEL;
502     if(dohlen < (*indexp + 1 + length))
503       return DOH_DNS_OUT_OF_RANGE;
504     *indexp += (unsigned int)(1 + length);
505   } while(length);
506   return DOH_OK;
507 }
508 
doh_get16bit(const unsigned char * doh,unsigned int index)509 static unsigned short doh_get16bit(const unsigned char *doh,
510                                    unsigned int index)
511 {
512   return (unsigned short)((doh[index] << 8) | doh[index + 1]);
513 }
514 
doh_get32bit(const unsigned char * doh,unsigned int index)515 static unsigned int doh_get32bit(const unsigned char *doh, unsigned int index)
516 {
517   /* make clang and gcc optimize this to bswap by incrementing
518      the pointer first. */
519   doh += index;
520 
521   /* avoid undefined behavior by casting to unsigned before shifting
522      24 bits, possibly into the sign bit. codegen is same, but
523      ub sanitizer will not be upset */
524   return ((unsigned)doh[0] << 24) | ((unsigned)doh[1] << 16) |
525          ((unsigned)doh[2] << 8) | doh[3];
526 }
527 
doh_store_a(const unsigned char * doh,int index,struct dohentry * d)528 static void doh_store_a(const unsigned char *doh, int index,
529                         struct dohentry *d)
530 {
531   /* silently ignore addresses over the limit */
532   if(d->numaddr < DOH_MAX_ADDR) {
533     struct dohaddr *a = &d->addr[d->numaddr];
534     a->type = DNS_TYPE_A;
535     memcpy(&a->ip.v4, &doh[index], 4);
536     d->numaddr++;
537   }
538 }
539 
doh_store_aaaa(const unsigned char * doh,int index,struct dohentry * d)540 static void doh_store_aaaa(const unsigned char *doh, int index,
541                               struct dohentry *d)
542 {
543   /* silently ignore addresses over the limit */
544   if(d->numaddr < DOH_MAX_ADDR) {
545     struct dohaddr *a = &d->addr[d->numaddr];
546     a->type = DNS_TYPE_AAAA;
547     memcpy(&a->ip.v6, &doh[index], 16);
548     d->numaddr++;
549   }
550 }
551 
552 #ifdef USE_HTTPSRR
doh_store_https(const unsigned char * doh,int index,struct dohentry * d,uint16_t len)553 static DOHcode doh_store_https(const unsigned char *doh, int index,
554                                struct dohentry *d, uint16_t len)
555 {
556   /* silently ignore RRs over the limit */
557   if(d->numhttps_rrs < DOH_MAX_HTTPS) {
558     struct dohhttps_rr *h = &d->https_rrs[d->numhttps_rrs];
559     h->val = Curl_memdup(&doh[index], len);
560     if(!h->val)
561       return DOH_OUT_OF_MEM;
562     h->len = len;
563     d->numhttps_rrs++;
564   }
565   return DOH_OK;
566 }
567 #endif
568 
doh_store_cname(const unsigned char * doh,size_t dohlen,unsigned int index,struct dohentry * d)569 static DOHcode doh_store_cname(const unsigned char *doh, size_t dohlen,
570                                unsigned int index, struct dohentry *d)
571 {
572   struct dynbuf *c;
573   unsigned int loop = 128; /* a valid DNS name can never loop this much */
574   unsigned char length;
575 
576   if(d->numcname == DOH_MAX_CNAME)
577     return DOH_OK; /* skip! */
578 
579   c = &d->cname[d->numcname++];
580   do {
581     if(index >= dohlen)
582       return DOH_DNS_OUT_OF_RANGE;
583     length = doh[index];
584     if((length & 0xc0) == 0xc0) {
585       int newpos;
586       /* name pointer, get the new offset (14 bits) */
587       if((index + 1) >= dohlen)
588         return DOH_DNS_OUT_OF_RANGE;
589 
590       /* move to the new index */
591       newpos = (length & 0x3f) << 8 | doh[index + 1];
592       index = (unsigned int)newpos;
593       continue;
594     }
595     else if(length & 0xc0)
596       return DOH_DNS_BAD_LABEL; /* bad input */
597     else
598       index++;
599 
600     if(length) {
601       if(Curl_dyn_len(c)) {
602         if(Curl_dyn_addn(c, STRCONST(".")))
603           return DOH_OUT_OF_MEM;
604       }
605       if((index + length) > dohlen)
606         return DOH_DNS_BAD_LABEL;
607 
608       if(Curl_dyn_addn(c, &doh[index], length))
609         return DOH_OUT_OF_MEM;
610       index += length;
611     }
612   } while(length && --loop);
613 
614   if(!loop)
615     return DOH_DNS_LABEL_LOOP;
616   return DOH_OK;
617 }
618 
doh_rdata(const unsigned char * doh,size_t dohlen,unsigned short rdlength,unsigned short type,int index,struct dohentry * d)619 static DOHcode doh_rdata(const unsigned char *doh,
620                          size_t dohlen,
621                          unsigned short rdlength,
622                          unsigned short type,
623                          int index,
624                          struct dohentry *d)
625 {
626   /* RDATA
627      - A (TYPE 1):  4 bytes
628      - AAAA (TYPE 28): 16 bytes
629      - NS (TYPE 2): N bytes
630      - HTTPS (TYPE 65): N bytes */
631   DOHcode rc;
632 
633   switch(type) {
634   case DNS_TYPE_A:
635     if(rdlength != 4)
636       return DOH_DNS_RDATA_LEN;
637     doh_store_a(doh, index, d);
638     break;
639   case DNS_TYPE_AAAA:
640     if(rdlength != 16)
641       return DOH_DNS_RDATA_LEN;
642     doh_store_aaaa(doh, index, d);
643     break;
644 #ifdef USE_HTTPSRR
645   case DNS_TYPE_HTTPS:
646     rc = doh_store_https(doh, index, d, rdlength);
647     if(rc)
648       return rc;
649     break;
650 #endif
651   case DNS_TYPE_CNAME:
652     rc = doh_store_cname(doh, dohlen, (unsigned int)index, d);
653     if(rc)
654       return rc;
655     break;
656   case DNS_TYPE_DNAME:
657     /* explicit for clarity; just skip; rely on synthesized CNAME  */
658     break;
659   default:
660     /* unsupported type, just skip it */
661     break;
662   }
663   return DOH_OK;
664 }
665 
de_init(struct dohentry * de)666 UNITTEST void de_init(struct dohentry *de)
667 {
668   int i;
669   memset(de, 0, sizeof(*de));
670   de->ttl = INT_MAX;
671   for(i = 0; i < DOH_MAX_CNAME; i++)
672     Curl_dyn_init(&de->cname[i], DYN_DOH_CNAME);
673 }
674 
675 
doh_resp_decode(const unsigned char * doh,size_t dohlen,DNStype dnstype,struct dohentry * d)676 UNITTEST DOHcode doh_resp_decode(const unsigned char *doh,
677                                  size_t dohlen,
678                                  DNStype dnstype,
679                                  struct dohentry *d)
680 {
681   unsigned char rcode;
682   unsigned short qdcount;
683   unsigned short ancount;
684   unsigned short type = 0;
685   unsigned short rdlength;
686   unsigned short nscount;
687   unsigned short arcount;
688   unsigned int index = 12;
689   DOHcode rc;
690 
691   if(dohlen < 12)
692     return DOH_TOO_SMALL_BUFFER; /* too small */
693   if(!doh || doh[0] || doh[1])
694     return DOH_DNS_BAD_ID; /* bad ID */
695   rcode = doh[3] & 0x0f;
696   if(rcode)
697     return DOH_DNS_BAD_RCODE; /* bad rcode */
698 
699   qdcount = doh_get16bit(doh, 4);
700   while(qdcount) {
701     rc = doh_skipqname(doh, dohlen, &index);
702     if(rc)
703       return rc; /* bad qname */
704     if(dohlen < (index + 4))
705       return DOH_DNS_OUT_OF_RANGE;
706     index += 4; /* skip question's type and class */
707     qdcount--;
708   }
709 
710   ancount = doh_get16bit(doh, 6);
711   while(ancount) {
712     unsigned short class;
713     unsigned int ttl;
714 
715     rc = doh_skipqname(doh, dohlen, &index);
716     if(rc)
717       return rc; /* bad qname */
718 
719     if(dohlen < (index + 2))
720       return DOH_DNS_OUT_OF_RANGE;
721 
722     type = doh_get16bit(doh, index);
723     if((type != DNS_TYPE_CNAME)    /* may be synthesized from DNAME */
724        && (type != DNS_TYPE_DNAME) /* if present, accept and ignore */
725        && (type != dnstype))
726       /* Not the same type as was asked for nor CNAME nor DNAME */
727       return DOH_DNS_UNEXPECTED_TYPE;
728     index += 2;
729 
730     if(dohlen < (index + 2))
731       return DOH_DNS_OUT_OF_RANGE;
732     class = doh_get16bit(doh, index);
733     if(DNS_CLASS_IN != class)
734       return DOH_DNS_UNEXPECTED_CLASS; /* unsupported */
735     index += 2;
736 
737     if(dohlen < (index + 4))
738       return DOH_DNS_OUT_OF_RANGE;
739 
740     ttl = doh_get32bit(doh, index);
741     if(ttl < d->ttl)
742       d->ttl = ttl;
743     index += 4;
744 
745     if(dohlen < (index + 2))
746       return DOH_DNS_OUT_OF_RANGE;
747 
748     rdlength = doh_get16bit(doh, index);
749     index += 2;
750     if(dohlen < (index + rdlength))
751       return DOH_DNS_OUT_OF_RANGE;
752 
753     rc = doh_rdata(doh, dohlen, rdlength, type, (int)index, d);
754     if(rc)
755       return rc; /* bad doh_rdata */
756     index += rdlength;
757     ancount--;
758   }
759 
760   nscount = doh_get16bit(doh, 8);
761   while(nscount) {
762     rc = doh_skipqname(doh, dohlen, &index);
763     if(rc)
764       return rc; /* bad qname */
765 
766     if(dohlen < (index + 8))
767       return DOH_DNS_OUT_OF_RANGE;
768 
769     index += 2 + 2 + 4; /* type, class and ttl */
770 
771     if(dohlen < (index + 2))
772       return DOH_DNS_OUT_OF_RANGE;
773 
774     rdlength = doh_get16bit(doh, index);
775     index += 2;
776     if(dohlen < (index + rdlength))
777       return DOH_DNS_OUT_OF_RANGE;
778     index += rdlength;
779     nscount--;
780   }
781 
782   arcount = doh_get16bit(doh, 10);
783   while(arcount) {
784     rc = doh_skipqname(doh, dohlen, &index);
785     if(rc)
786       return rc; /* bad qname */
787 
788     if(dohlen < (index + 8))
789       return DOH_DNS_OUT_OF_RANGE;
790 
791     index += 2 + 2 + 4; /* type, class and ttl */
792 
793     if(dohlen < (index + 2))
794       return DOH_DNS_OUT_OF_RANGE;
795 
796     rdlength = doh_get16bit(doh, index);
797     index += 2;
798     if(dohlen < (index + rdlength))
799       return DOH_DNS_OUT_OF_RANGE;
800     index += rdlength;
801     arcount--;
802   }
803 
804   if(index != dohlen)
805     return DOH_DNS_MALFORMAT; /* something is wrong */
806 
807 #ifdef USE_HTTTPS
808   if((type != DNS_TYPE_NS) && !d->numcname && !d->numaddr && !d->numhttps_rrs)
809 #else
810   if((type != DNS_TYPE_NS) && !d->numcname && !d->numaddr)
811 #endif
812     /* nothing stored! */
813     return DOH_NO_CONTENT;
814 
815   return DOH_OK; /* ok */
816 }
817 
818 #ifndef CURL_DISABLE_VERBOSE_STRINGS
doh_show(struct Curl_easy * data,const struct dohentry * d)819 static void doh_show(struct Curl_easy *data,
820                      const struct dohentry *d)
821 {
822   int i;
823   infof(data, "[DoH] TTL: %u seconds", d->ttl);
824   for(i = 0; i < d->numaddr; i++) {
825     const struct dohaddr *a = &d->addr[i];
826     if(a->type == DNS_TYPE_A) {
827       infof(data, "[DoH] A: %u.%u.%u.%u",
828             a->ip.v4[0], a->ip.v4[1],
829             a->ip.v4[2], a->ip.v4[3]);
830     }
831     else if(a->type == DNS_TYPE_AAAA) {
832       int j;
833       char buffer[128] = "[DoH] AAAA: ";
834       size_t len = strlen(buffer);
835       char *ptr = &buffer[len];
836       len = sizeof(buffer) - len;
837       for(j = 0; j < 16; j += 2) {
838         size_t l;
839         msnprintf(ptr, len, "%s%02x%02x", j ? ":" : "", d->addr[i].ip.v6[j],
840                   d->addr[i].ip.v6[j + 1]);
841         l = strlen(ptr);
842         len -= l;
843         ptr += l;
844       }
845       infof(data, "%s", buffer);
846     }
847   }
848 #ifdef USE_HTTPSRR
849   for(i = 0; i < d->numhttps_rrs; i++) {
850 # ifdef DEBUGBUILD
851     doh_print_buf(data, "DoH HTTPS",
852                   d->https_rrs[i].val, d->https_rrs[i].len);
853 # else
854     infof(data, "DoH HTTPS RR: length %d", d->https_rrs[i].len);
855 # endif
856   }
857 #endif
858   for(i = 0; i < d->numcname; i++) {
859     infof(data, "CNAME: %s", Curl_dyn_ptr(&d->cname[i]));
860   }
861 }
862 #else
863 #define doh_show(x,y)
864 #endif
865 
866 /*
867  * doh2ai()
868  *
869  * This function returns a pointer to the first element of a newly allocated
870  * Curl_addrinfo struct linked list filled with the data from a set of DoH
871  * lookups. Curl_addrinfo is meant to work like the addrinfo struct does for
872  * a IPv6 stack, but usable also for IPv4, all hosts and environments.
873  *
874  * The memory allocated by this function *MUST* be free'd later on calling
875  * Curl_freeaddrinfo(). For each successful call to this function there
876  * must be an associated call later to Curl_freeaddrinfo().
877  */
878 
doh2ai(const struct dohentry * de,const char * hostname,int port,struct Curl_addrinfo ** aip)879 static CURLcode doh2ai(const struct dohentry *de, const char *hostname,
880                        int port, struct Curl_addrinfo **aip)
881 {
882   struct Curl_addrinfo *ai;
883   struct Curl_addrinfo *prevai = NULL;
884   struct Curl_addrinfo *firstai = NULL;
885   struct sockaddr_in *addr;
886 #ifdef USE_IPV6
887   struct sockaddr_in6 *addr6;
888 #endif
889   CURLcode result = CURLE_OK;
890   int i;
891   size_t hostlen = strlen(hostname) + 1; /* include null-terminator */
892 
893   DEBUGASSERT(de);
894 
895   if(!de->numaddr)
896     return CURLE_COULDNT_RESOLVE_HOST;
897 
898   for(i = 0; i < de->numaddr; i++) {
899     size_t ss_size;
900     CURL_SA_FAMILY_T addrtype;
901     if(de->addr[i].type == DNS_TYPE_AAAA) {
902 #ifndef USE_IPV6
903       /* we cannot handle IPv6 addresses */
904       continue;
905 #else
906       ss_size = sizeof(struct sockaddr_in6);
907       addrtype = AF_INET6;
908 #endif
909     }
910     else {
911       ss_size = sizeof(struct sockaddr_in);
912       addrtype = AF_INET;
913     }
914 
915     ai = calloc(1, sizeof(struct Curl_addrinfo) + ss_size + hostlen);
916     if(!ai) {
917       result = CURLE_OUT_OF_MEMORY;
918       break;
919     }
920     ai->ai_addr = (void *)((char *)ai + sizeof(struct Curl_addrinfo));
921     ai->ai_canonname = (void *)((char *)ai->ai_addr + ss_size);
922     memcpy(ai->ai_canonname, hostname, hostlen);
923 
924     if(!firstai)
925       /* store the pointer we want to return from this function */
926       firstai = ai;
927 
928     if(prevai)
929       /* make the previous entry point to this */
930       prevai->ai_next = ai;
931 
932     ai->ai_family = addrtype;
933 
934     /* we return all names as STREAM, so when using this address for TFTP
935        the type must be ignored and conn->socktype be used instead! */
936     ai->ai_socktype = SOCK_STREAM;
937 
938     ai->ai_addrlen = (curl_socklen_t)ss_size;
939 
940     /* leave the rest of the struct filled with zero */
941 
942     switch(ai->ai_family) {
943     case AF_INET:
944       addr = (void *)ai->ai_addr; /* storage area for this info */
945       DEBUGASSERT(sizeof(struct in_addr) == sizeof(de->addr[i].ip.v4));
946       memcpy(&addr->sin_addr, &de->addr[i].ip.v4, sizeof(struct in_addr));
947       addr->sin_family = (CURL_SA_FAMILY_T)addrtype;
948       addr->sin_port = htons((unsigned short)port);
949       break;
950 
951 #ifdef USE_IPV6
952     case AF_INET6:
953       addr6 = (void *)ai->ai_addr; /* storage area for this info */
954       DEBUGASSERT(sizeof(struct in6_addr) == sizeof(de->addr[i].ip.v6));
955       memcpy(&addr6->sin6_addr, &de->addr[i].ip.v6, sizeof(struct in6_addr));
956       addr6->sin6_family = (CURL_SA_FAMILY_T)addrtype;
957       addr6->sin6_port = htons((unsigned short)port);
958       break;
959 #endif
960     }
961 
962     prevai = ai;
963   }
964 
965   if(result) {
966     Curl_freeaddrinfo(firstai);
967     firstai = NULL;
968   }
969   *aip = firstai;
970 
971   return result;
972 }
973 
974 #ifndef CURL_DISABLE_VERBOSE_STRINGS
doh_type2name(DNStype dnstype)975 static const char *doh_type2name(DNStype dnstype)
976 {
977   switch(dnstype) {
978     case DNS_TYPE_A:
979       return "A";
980     case DNS_TYPE_AAAA:
981       return "AAAA";
982 #ifdef USE_HTTPSRR
983     case DNS_TYPE_HTTPS:
984       return "HTTPS";
985 #endif
986     default:
987        return "unknown";
988   }
989 }
990 #endif
991 
de_cleanup(struct dohentry * d)992 UNITTEST void de_cleanup(struct dohentry *d)
993 {
994   int i = 0;
995   for(i = 0; i < d->numcname; i++) {
996     Curl_dyn_free(&d->cname[i]);
997   }
998 #ifdef USE_HTTPSRR
999   for(i = 0; i < d->numhttps_rrs; i++)
1000     Curl_safefree(d->https_rrs[i].val);
1001 #endif
1002 }
1003 
1004 #ifdef USE_HTTPSRR
1005 
1006 /*
1007  * @brief decode the DNS name in a binary RRData
1008  * @param buf points to the buffer (in/out)
1009  * @param remaining points to the remaining buffer length (in/out)
1010  * @param dnsname returns the string form name on success
1011  * @return is 1 for success, error otherwise
1012  *
1013  * The encoding here is defined in
1014  * https://tools.ietf.org/html/rfc1035#section-3.1
1015  *
1016  * The input buffer pointer will be modified so it points to
1017  * just after the end of the DNS name encoding on output. (And
1018  * that is why it is an "unsigned char **" :-)
1019  */
doh_decode_rdata_name(unsigned char ** buf,size_t * remaining,char ** dnsname)1020 static CURLcode doh_decode_rdata_name(unsigned char **buf, size_t *remaining,
1021                                       char **dnsname)
1022 {
1023   unsigned char *cp = NULL;
1024   int rem = 0;
1025   unsigned char clen = 0; /* chunk len */
1026   struct dynbuf thename;
1027 
1028   DEBUGASSERT(buf && remaining && dnsname);
1029   if(!buf || !remaining || !dnsname)
1030     return CURLE_OUT_OF_MEMORY;
1031   rem = (int)*remaining;
1032   if(rem <= 0) {
1033     Curl_dyn_free(&thename);
1034     return CURLE_OUT_OF_MEMORY;
1035   }
1036   Curl_dyn_init(&thename, CURL_MAXLEN_host_name);
1037   cp = *buf;
1038   clen = *cp++;
1039   if(clen == 0) {
1040     /* special case - return "." as name */
1041     if(Curl_dyn_addn(&thename, ".", 1))
1042       return CURLE_OUT_OF_MEMORY;
1043   }
1044   while(clen) {
1045     if(clen >= rem) {
1046       Curl_dyn_free(&thename);
1047       return CURLE_OUT_OF_MEMORY;
1048     }
1049     if(Curl_dyn_addn(&thename, cp, clen) ||
1050        Curl_dyn_addn(&thename, ".", 1))
1051       return CURLE_TOO_LARGE;
1052 
1053     cp += clen;
1054     rem -= (clen + 1);
1055     if(rem <= 0) {
1056       Curl_dyn_free(&thename);
1057       return CURLE_OUT_OF_MEMORY;
1058     }
1059     clen = *cp++;
1060   }
1061   *buf = cp;
1062   *remaining = rem - 1;
1063   *dnsname = Curl_dyn_ptr(&thename);
1064   return CURLE_OK;
1065 }
1066 
1067 #ifdef DEBUGBUILD
doh_test_alpn_escapes(void)1068 static CURLcode doh_test_alpn_escapes(void)
1069 {
1070   /* we will use an example from draft-ietf-dnsop-svcb, figure 10 */
1071   static unsigned char example[] = {
1072     0x08,                                           /* length 8 */
1073     0x66, 0x5c, 0x6f, 0x6f, 0x2c, 0x62, 0x61, 0x72, /* value "f\\oo,bar" */
1074     0x02,                                           /* length 2 */
1075     0x68, 0x32                                      /* value "h2" */
1076   };
1077   size_t example_len = sizeof(example);
1078   unsigned char aval[MAX_HTTPSRR_ALPNS] = { 0 };
1079   static const char expected[2] = { ALPN_h2, ALPN_none };
1080 
1081   if(Curl_httpsrr_decode_alpn(example, example_len, aval) != CURLE_OK)
1082     return CURLE_BAD_CONTENT_ENCODING;
1083   if(memcmp(aval, expected, sizeof(expected)))
1084     return CURLE_BAD_CONTENT_ENCODING;
1085   return CURLE_OK;
1086 }
1087 #endif
1088 
doh_resp_decode_httpsrr(unsigned char * cp,size_t len,struct Curl_https_rrinfo ** hrr)1089 static CURLcode doh_resp_decode_httpsrr(unsigned char *cp, size_t len,
1090                                         struct Curl_https_rrinfo **hrr)
1091 {
1092   uint16_t pcode = 0, plen = 0;
1093   struct Curl_https_rrinfo *lhrr = NULL;
1094   char *dnsname = NULL;
1095 
1096 #ifdef DEBUGBUILD
1097   /* a few tests of escaping, should not be here but ok for now */
1098   if(doh_test_alpn_escapes() != CURLE_OK)
1099     return CURLE_OUT_OF_MEMORY;
1100 #endif
1101   if(len <= 2)
1102     return CURLE_BAD_FUNCTION_ARGUMENT;
1103   lhrr = calloc(1, sizeof(struct Curl_https_rrinfo));
1104   if(!lhrr)
1105     return CURLE_OUT_OF_MEMORY;
1106   lhrr->priority = doh_get16bit(cp, 0);
1107   cp += 2;
1108   len -= 2;
1109   if(doh_decode_rdata_name(&cp, &len, &dnsname) != CURLE_OK)
1110     goto err;
1111   lhrr->target = dnsname;
1112   lhrr->port = -1; /* until set */
1113   while(len >= 4) {
1114     pcode = doh_get16bit(cp, 0);
1115     plen = doh_get16bit(cp, 2);
1116     cp += 4;
1117     len -= 4;
1118     switch(pcode) {
1119     case HTTPS_RR_CODE_ALPN:
1120       if(Curl_httpsrr_decode_alpn(cp, plen, lhrr->alpns) != CURLE_OK)
1121         goto err;
1122       break;
1123     case HTTPS_RR_CODE_NO_DEF_ALPN:
1124       lhrr->no_def_alpn = TRUE;
1125       break;
1126     case HTTPS_RR_CODE_IPV4:
1127       if(!plen)
1128         goto err;
1129       lhrr->ipv4hints = Curl_memdup(cp, plen);
1130       if(!lhrr->ipv4hints)
1131         goto err;
1132       lhrr->ipv4hints_len = (size_t)plen;
1133       break;
1134     case HTTPS_RR_CODE_ECH:
1135       if(!plen)
1136         goto err;
1137       lhrr->echconfiglist = Curl_memdup(cp, plen);
1138       if(!lhrr->echconfiglist)
1139         goto err;
1140       lhrr->echconfiglist_len = (size_t)plen;
1141       break;
1142     case HTTPS_RR_CODE_IPV6:
1143       if(!plen)
1144         goto err;
1145       lhrr->ipv6hints = Curl_memdup(cp, plen);
1146       if(!lhrr->ipv6hints)
1147         goto err;
1148       lhrr->ipv6hints_len = (size_t)plen;
1149       break;
1150     case HTTPS_RR_CODE_PORT:
1151       lhrr->port = doh_get16bit(cp, 0);
1152       break;
1153     default:
1154       break;
1155     }
1156     if(plen > 0 && plen <= len) {
1157       cp += plen;
1158       len -= plen;
1159     }
1160   }
1161   DEBUGASSERT(!len);
1162   *hrr = lhrr;
1163   return CURLE_OK;
1164 err:
1165   Curl_safefree(lhrr->target);
1166   Curl_safefree(lhrr->echconfiglist);
1167   Curl_safefree(lhrr);
1168   return CURLE_OUT_OF_MEMORY;
1169 }
1170 
1171 # ifdef DEBUGBUILD
doh_print_httpsrr(struct Curl_easy * data,struct Curl_https_rrinfo * hrr)1172 static void doh_print_httpsrr(struct Curl_easy *data,
1173                               struct Curl_https_rrinfo *hrr)
1174 {
1175   DEBUGASSERT(hrr);
1176   infof(data, "HTTPS RR: priority %d, target: %s",
1177         hrr->priority, hrr->target);
1178   if(hrr->alpns[0] != ALPN_none)
1179     infof(data, "HTTPS RR: alpns %u %u %u %u",
1180           hrr->alpns[0], hrr->alpns[1], hrr->alpns[2], hrr->alpns[3]);
1181   else
1182     infof(data, "HTTPS RR: no alpns");
1183   if(hrr->no_def_alpn)
1184     infof(data, "HTTPS RR: no_def_alpn set");
1185   else
1186     infof(data, "HTTPS RR: no_def_alpn not set");
1187   if(hrr->ipv4hints) {
1188     doh_print_buf(data, "HTTPS RR: ipv4hints",
1189                   hrr->ipv4hints, hrr->ipv4hints_len);
1190   }
1191   else
1192     infof(data, "HTTPS RR: no ipv4hints");
1193   if(hrr->echconfiglist) {
1194     doh_print_buf(data, "HTTPS RR: ECHConfigList",
1195                   hrr->echconfiglist, hrr->echconfiglist_len);
1196   }
1197   else
1198     infof(data, "HTTPS RR: no ECHConfigList");
1199   if(hrr->ipv6hints) {
1200     doh_print_buf(data, "HTTPS RR: ipv6hint",
1201                   hrr->ipv6hints, hrr->ipv6hints_len);
1202   }
1203   else
1204     infof(data, "HTTPS RR: no ipv6hints");
1205   return;
1206 }
1207 # endif
1208 #endif
1209 
Curl_doh_is_resolved(struct Curl_easy * data,struct Curl_dns_entry ** dnsp)1210 CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
1211                               struct Curl_dns_entry **dnsp)
1212 {
1213   CURLcode result;
1214   struct doh_probes *dohp = data->req.doh;
1215   *dnsp = NULL; /* defaults to no response */
1216   if(!dohp)
1217     return CURLE_OUT_OF_MEMORY;
1218 
1219   if(dohp->probe[DOH_SLOT_IPV4].easy_mid < 0 &&
1220      dohp->probe[DOH_SLOT_IPV6].easy_mid < 0) {
1221     failf(data, "Could not DoH-resolve: %s", data->state.async.hostname);
1222     return CONN_IS_PROXIED(data->conn) ? CURLE_COULDNT_RESOLVE_PROXY :
1223       CURLE_COULDNT_RESOLVE_HOST;
1224   }
1225   else if(!dohp->pending) {
1226     DOHcode rc[DOH_SLOT_COUNT];
1227     struct dohentry de;
1228     int slot;
1229 
1230     memset(rc, 0, sizeof(rc));
1231     /* remove DoH handles from multi handle and close them */
1232     Curl_doh_close(data);
1233     /* parse the responses, create the struct and return it! */
1234     de_init(&de);
1235     for(slot = 0; slot < DOH_SLOT_COUNT; slot++) {
1236       struct doh_probe *p = &dohp->probe[slot];
1237       if(!p->dnstype)
1238         continue;
1239       rc[slot] = doh_resp_decode(Curl_dyn_uptr(&p->resp_body),
1240                                  Curl_dyn_len(&p->resp_body),
1241                                  p->dnstype, &de);
1242       Curl_dyn_free(&p->resp_body);
1243 #ifndef CURL_DISABLE_VERBOSE_STRINGS
1244       if(rc[slot]) {
1245         infof(data, "DoH: %s type %s for %s", doh_strerror(rc[slot]),
1246               doh_type2name(p->dnstype), dohp->host);
1247       }
1248 #endif
1249     } /* next slot */
1250 
1251     result = CURLE_COULDNT_RESOLVE_HOST; /* until we know better */
1252     if(!rc[DOH_SLOT_IPV4] || !rc[DOH_SLOT_IPV6]) {
1253       /* we have an address, of one kind or other */
1254       struct Curl_dns_entry *dns;
1255       struct Curl_addrinfo *ai;
1256 
1257 
1258       if(Curl_trc_ft_is_verbose(data, &Curl_doh_trc)) {
1259         infof(data, "[DoH] hostname: %s", dohp->host);
1260         doh_show(data, &de);
1261       }
1262 
1263       result = doh2ai(&de, dohp->host, dohp->port, &ai);
1264       if(result) {
1265         de_cleanup(&de);
1266         return result;
1267       }
1268 
1269       if(data->share)
1270         Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
1271 
1272       /* we got a response, store it in the cache */
1273       dns = Curl_cache_addr(data, ai, dohp->host, 0, dohp->port, FALSE);
1274 
1275       if(data->share)
1276         Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
1277 
1278       if(!dns) {
1279         /* returned failure, bail out nicely */
1280         Curl_freeaddrinfo(ai);
1281       }
1282       else {
1283         data->state.async.dns = dns;
1284         *dnsp = dns;
1285         result = CURLE_OK;      /* address resolution OK */
1286       }
1287     } /* address processing done */
1288 
1289     /* Now process any build-specific attributes retrieved from DNS */
1290 #ifdef USE_HTTPSRR
1291     if(de.numhttps_rrs > 0 && result == CURLE_OK && *dnsp) {
1292       struct Curl_https_rrinfo *hrr = NULL;
1293       result = doh_resp_decode_httpsrr(de.https_rrs->val, de.https_rrs->len,
1294                                        &hrr);
1295       if(result) {
1296         infof(data, "Failed to decode HTTPS RR");
1297         return result;
1298       }
1299       infof(data, "Some HTTPS RR to process");
1300 # ifdef DEBUGBUILD
1301       doh_print_httpsrr(data, hrr);
1302 # endif
1303       (*dnsp)->hinfo = hrr;
1304     }
1305 #endif
1306 
1307     /* All done */
1308     de_cleanup(&de);
1309     Curl_doh_cleanup(data);
1310     return result;
1311 
1312   } /* !dohp->pending */
1313 
1314   /* else wait for pending DoH transactions to complete */
1315   return CURLE_OK;
1316 }
1317 
Curl_doh_close(struct Curl_easy * data)1318 void Curl_doh_close(struct Curl_easy *data)
1319 {
1320   struct doh_probes *doh = data->req.doh;
1321   if(doh && data->multi) {
1322     struct Curl_easy *probe_data;
1323     curl_off_t mid;
1324     size_t slot;
1325     for(slot = 0; slot < DOH_SLOT_COUNT; slot++) {
1326       mid = doh->probe[slot].easy_mid;
1327       if(mid < 0)
1328         continue;
1329       doh->probe[slot].easy_mid = -1;
1330       /* should have been called before data is removed from multi handle */
1331       DEBUGASSERT(data->multi);
1332       probe_data = data->multi ? Curl_multi_get_handle(data->multi, mid) :
1333         NULL;
1334       if(!probe_data) {
1335         DEBUGF(infof(data, "Curl_doh_close: xfer for mid=%"
1336                      FMT_OFF_T " not found!",
1337                      doh->probe[slot].easy_mid));
1338         continue;
1339       }
1340       /* data->multi might already be reset at this time */
1341       curl_multi_remove_handle(data->multi, probe_data);
1342       Curl_close(&probe_data);
1343     }
1344   }
1345 }
1346 
Curl_doh_cleanup(struct Curl_easy * data)1347 void Curl_doh_cleanup(struct Curl_easy *data)
1348 {
1349   struct doh_probes *doh = data->req.doh;
1350   if(doh) {
1351     Curl_doh_close(data);
1352     curl_slist_free_all(doh->req_hds);
1353     data->req.doh->req_hds = NULL;
1354     Curl_safefree(data->req.doh);
1355   }
1356 }
1357 
1358 #endif /* CURL_DISABLE_DOH */
1359