1e02e2b1dfd14e742e9d470f8b67e388500b0feb
[dnssec-prover] / src / query.rs
1 //! This module exposes utilities for building DNSSEC proofs by directly querying a recursive
2 //! resolver.
3
4 use core::{cmp, ops};
5 use alloc::vec;
6 use alloc::vec::Vec;
7
8 #[cfg(feature = "std")]
9 use std::net::{SocketAddr, TcpStream};
10 #[cfg(feature = "std")]
11 use std::io::{Read, Write, Error, ErrorKind};
12
13 #[cfg(feature = "tokio")]
14 use tokio_crate::net::TcpStream as TokioTcpStream;
15 #[cfg(feature = "tokio")]
16 use tokio_crate::io::{AsyncReadExt, AsyncWriteExt};
17
18 use crate::rr::*;
19 use crate::ser::*;
20
21 // In testing use a rather small buffer to ensure we hit the allocation paths sometimes. In
22 // production, we should generally never actually need to go to heap as DNS messages are rarely
23 // larger than a KiB or two.
24 #[cfg(any(test, fuzzing))]
25 const STACK_BUF_LIMIT: u16 = 32;
26 #[cfg(not(any(test, fuzzing)))]
27 const STACK_BUF_LIMIT: u16 = 2048;
28
29 /// A buffer for storing queries and responses.
30 #[derive(Clone, PartialEq, Eq)]
31 pub struct QueryBuf {
32         buf: [u8; STACK_BUF_LIMIT as usize],
33         heap_buf: Vec<u8>,
34         len: u16,
35 }
36 impl QueryBuf {
37         /// Generates a new buffer of the given length, consisting of all zeros.
38         pub fn new_zeroed(len: u16) -> Self {
39                 let heap_buf = if len > STACK_BUF_LIMIT { vec![0; len as usize] } else { Vec::new() };
40                 Self {
41                         buf: [0; STACK_BUF_LIMIT as usize],
42                         heap_buf,
43                         len
44                 }
45         }
46         /// Extends the size of this buffer by appending the given slice.
47         ///
48         /// If the total length of this buffer exceeds [`u16::MAX`] after appending, the buffer's state
49         /// is undefined, however pushing data beyond [`u16::MAX`] will not panic.
50         pub fn extend_from_slice(&mut self, sl: &[u8]) {
51                 let new_len = self.len.saturating_add(sl.len() as u16);
52                 let was_heap = self.len > STACK_BUF_LIMIT;
53                 let is_heap = new_len > STACK_BUF_LIMIT;
54                 if was_heap != is_heap {
55                         self.heap_buf = vec![0; new_len as usize];
56                         self.heap_buf[..self.len as usize].copy_from_slice(&self.buf[..self.len as usize]);
57                 }
58                 let target = if is_heap {
59                         self.heap_buf.resize(new_len as usize, 0);
60                         &mut self.heap_buf[self.len as usize..]
61                 } else {
62                         &mut self.buf[self.len as usize..new_len as usize]
63                 };
64                 target.copy_from_slice(sl);
65                 self.len = new_len;
66         }
67         /// Converts this query into its bytes on the heap
68         pub fn into_vec(self) -> Vec<u8> {
69                 if self.len > STACK_BUF_LIMIT {
70                         self.heap_buf
71                 } else {
72                         self.buf[..self.len as usize].to_vec()
73                 }
74         }
75 }
76 impl ops::Deref for QueryBuf {
77         type Target = [u8];
78         fn deref(&self) -> &[u8] {
79                 if self.len > STACK_BUF_LIMIT {
80                         &self.heap_buf
81                 } else {
82                         &self.buf[..self.len as usize]
83                 }
84         }
85 }
86 impl ops::DerefMut for QueryBuf {
87         fn deref_mut(&mut self) -> &mut [u8] {
88                 if self.len > STACK_BUF_LIMIT {
89                         &mut self.heap_buf
90                 } else {
91                         &mut self.buf[..self.len as usize]
92                 }
93         }
94 }
95
96 // We don't care about transaction IDs as we're only going to accept signed data.
97 // Further, if we're querying over DoH, the RFC says we SHOULD use a transaction ID of 0 here.
98 const TXID: u16 = 0;
99
100 fn build_query(domain: &Name, ty: u16) -> QueryBuf {
101         let mut query = QueryBuf::new_zeroed(0);
102         query.extend_from_slice(&TXID.to_be_bytes());
103         query.extend_from_slice(&[0x01, 0x20]); // Flags: Recursive, Authenticated Data
104         query.extend_from_slice(&[0, 1, 0, 0, 0, 0, 0, 1]); // One question, One additional
105         write_name(&mut query, domain);
106         query.extend_from_slice(&ty.to_be_bytes());
107         query.extend_from_slice(&1u16.to_be_bytes()); // INternet class
108         query.extend_from_slice(&[0, 0, 0x29]); // . OPT
109         query.extend_from_slice(&0u16.to_be_bytes()); // 0 UDP payload size
110         query.extend_from_slice(&[0, 0]); // EDNS version 0
111         query.extend_from_slice(&0x8000u16.to_be_bytes()); // Accept DNSSEC RRs
112         query.extend_from_slice(&0u16.to_be_bytes()); // No additional data
113         query
114 }
115
116 #[cfg(fuzzing)]
117 /// Read some input and parse it as if it came from a server, for fuzzing.
118 pub fn fuzz_response(response: &[u8]) {
119         let (mut proof, mut names) = (Vec::new(), Vec::new());
120         let _ = handle_response(response, &mut proof, &mut names);
121 }
122
123 fn handle_response(resp: &[u8], proof: &mut Vec<u8>, rrsig_key_names: &mut Vec<Name>) -> Result<u32, ()> {
124         let mut read: &[u8] = resp;
125         if read_u16(&mut read)? != TXID { return Err(()); }
126         // 2 byte transaction ID
127         let flags = read_u16(&mut read)?;
128         if flags & 0b1000_0000_0000_0000 == 0 {
129                 return Err(());
130         }
131         if flags & 0b0111_1010_0000_0111 != 0 {
132                 return Err(());
133         }
134         if flags & 0b10_0000 == 0 {
135                 return Err(());
136         }
137         let questions = read_u16(&mut read)?;
138         if questions != 1 { return Err(()); }
139         let answers = read_u16(&mut read)?;
140         if answers == 0 { return Err(()); }
141         let authorities = read_u16(&mut read)?;
142         let _additional = read_u16(&mut read)?;
143
144         for _ in 0..questions {
145                 read_wire_packet_name(&mut read, resp)?;
146                 read_u16(&mut read)?; // type
147                 read_u16(&mut read)?; // class
148         }
149
150         // Only read the answers and NSEC records in authorities, skipping additional entirely.
151         let mut min_ttl = u32::MAX;
152         for _ in 0..answers {
153                 let (rr, ttl) = parse_wire_packet_rr(&mut read, &resp)?;
154                 write_rr(&rr, ttl, proof);
155                 min_ttl = cmp::min(min_ttl, ttl);
156                 if let RR::RRSig(rrsig) = rr { rrsig_key_names.push(rrsig.key_name); }
157         }
158
159         for _ in 0..authorities {
160                 // Only include records from the authority section if they are NSEC/3 (or signatures
161                 // thereover). We don't care about NS records here.
162                 let (rr, ttl) = parse_wire_packet_rr(&mut read, &resp)?;
163                 match &rr {
164                         RR::RRSig(rrsig) => {
165                                 if rrsig.ty != NSec::TYPE && rrsig.ty != NSec3::TYPE {
166                                         continue;
167                                 }
168                         },
169                         RR::NSec(_)|RR::NSec3(_) => {},
170                         _ => continue,
171                 }
172                 write_rr(&rr, ttl, proof);
173                 min_ttl = cmp::min(min_ttl, ttl);
174                 if let RR::RRSig(rrsig) = rr { rrsig_key_names.push(rrsig.key_name); }
175         }
176
177         Ok(min_ttl)
178 }
179
180 #[cfg(fuzzing)]
181 /// Read a stream of responses and handle them it as if they came from a server, for fuzzing.
182 pub fn fuzz_proof_builder(mut response_stream: &[u8]) {
183         let (mut builder, _) = ProofBuilder::new(&"example.com.".try_into().unwrap(), Txt::TYPE);
184         while builder.awaiting_responses() {
185                 let len = if let Ok(len) = read_u16(&mut response_stream) { len } else { return };
186                 let mut buf = QueryBuf::new_zeroed(len);
187                 if response_stream.len() < len as usize { return; }
188                 buf.copy_from_slice(&response_stream[..len as usize]);
189                 response_stream = &response_stream[len as usize..];
190                 let _ = builder.process_response(&buf);
191         }
192         let _ = builder.finish_proof();
193 }
194
195 const MAX_REQUESTS: usize = 10;
196 /// A simple state machine which will generate a series of queries and process the responses until
197 /// it has built a DNSSEC proof.
198 ///
199 /// A [`ProofBuilder`] driver starts with [`ProofBuilder::new`], fetching the state machine and
200 /// initial query. As long as [`ProofBuilder::awaiting_responses`] returns true, responses should
201 /// be read from the resolver. For each query response read from the DNS resolver,
202 /// [`ProofBuilder::process_response`] should be called, and each fresh query returned should be
203 /// sent to the resolver. Once [`ProofBuilder::awaiting_responses`] returns false,
204 /// [`ProofBuilder::finish_proof`] should be called to fetch the resulting proof.
205 ///
206 /// To build a DNSSEC proof using a DoH server, take each [`QueryBuf`], encode it as base64url, and
207 /// make a query to `https://doh-server/endpoint?dns=base64url_encoded_query` with an `Accept`
208 /// header of `application/dns-message`. Each response, in raw binary, can be fed directly into
209 /// [`ProofBuilder::process_response`].
210 pub struct ProofBuilder {
211         proof: Vec<u8>,
212         min_ttl: u32,
213         dnskeys_requested: Vec<Name>,
214         pending_queries: usize,
215         queries_made: usize,
216 }
217
218 impl ProofBuilder {
219         /// Constructs a new [`ProofBuilder`] and an initial query to send to the recursive resolver to
220         /// begin the proof building process.
221         ///
222         /// Given a correctly-functioning resolver the proof will ultimately be able to prove the
223         /// contents of any records with the given `ty`pe at the given `name` (as long as the given
224         /// `ty`pe is supported by this library).
225         ///
226         /// You can find constants for supported standard types in the [`crate::rr`] module.
227         pub fn new(name: &Name, ty: u16) -> (ProofBuilder, QueryBuf) {
228                 let initial_query = build_query(name, ty);
229                 (ProofBuilder {
230                         proof: Vec::new(),
231                         min_ttl: u32::MAX,
232                         dnskeys_requested: Vec::with_capacity(MAX_REQUESTS),
233                         pending_queries: 1,
234                         queries_made: 1,
235                 }, initial_query)
236         }
237
238         /// Returns true as long as further responses are expected from the resolver.
239         ///
240         /// As long as this returns true, responses should be read from the resolver and passed to
241         /// [`Self::process_response`]. Once this returns false, [`Self::finish_proof`] should be used
242         /// to (possibly) get the final proof.
243         pub fn awaiting_responses(&self) -> bool {
244                 self.pending_queries > 0 && self.queries_made <= MAX_REQUESTS
245         }
246
247         /// Processes a query response from the recursive resolver, returning a list of new queries to
248         /// send to the resolver.
249         pub fn process_response(&mut self, resp: &QueryBuf) -> Result<Vec<QueryBuf>, ()> {
250                 if self.pending_queries == 0 { return Err(()); }
251
252                 let mut rrsig_key_names = Vec::new();
253                 let min_ttl = handle_response(&resp, &mut self.proof, &mut rrsig_key_names)?;
254                 self.min_ttl = cmp::min(self.min_ttl, min_ttl);
255                 self.pending_queries -= 1;
256
257                 rrsig_key_names.sort_unstable();
258                 rrsig_key_names.dedup();
259
260                 let mut new_queries = Vec::with_capacity(2);
261                 for key_name in rrsig_key_names.drain(..) {
262                         if !self.dnskeys_requested.contains(&key_name) {
263                                 new_queries.push(build_query(&key_name, DnsKey::TYPE));
264                                 self.pending_queries += 1;
265                                 self.queries_made += 1;
266                                 self.dnskeys_requested.push(key_name.clone());
267
268                                 if key_name.as_str() != "." {
269                                         new_queries.push(build_query(&key_name, DS::TYPE));
270                                         self.pending_queries += 1;
271                                         self.queries_made += 1;
272                                 }
273                         }
274                 }
275                 if self.queries_made <= MAX_REQUESTS {
276                         Ok(new_queries)
277                 } else {
278                         Ok(Vec::new())
279                 }
280         }
281
282         /// Finalizes the proof, if one is available, and returns it as well as the TTL that should be
283         /// used to cache the proof (i.e. the lowest TTL of all records which were used to build the
284         /// proof).
285         pub fn finish_proof(self) -> Result<(Vec<u8>, u32), ()> {
286                 if self.pending_queries > 0 || self.queries_made > MAX_REQUESTS {
287                         Err(())
288                 } else {
289                         Ok((self.proof, self.min_ttl))
290                 }
291         }
292 }
293
294 #[cfg(feature = "std")]
295 fn send_query(stream: &mut TcpStream, query: &[u8]) -> Result<(), Error> {
296         stream.write_all(&(query.len() as u16).to_be_bytes())?;
297         stream.write_all(&query)?;
298         Ok(())
299 }
300
301 #[cfg(feature = "tokio")]
302 async fn send_query_async(stream: &mut TokioTcpStream, query: &[u8]) -> Result<(), Error> {
303         stream.write_all(&(query.len() as u16).to_be_bytes()).await?;
304         stream.write_all(&query).await?;
305         Ok(())
306 }
307
308 #[cfg(feature = "std")]
309 fn read_response(stream: &mut TcpStream) -> Result<QueryBuf, Error> {
310         let mut len_bytes = [0; 2];
311         stream.read_exact(&mut len_bytes)?;
312         let mut buf = QueryBuf::new_zeroed(u16::from_be_bytes(len_bytes));
313         stream.read_exact(&mut buf)?;
314         Ok(buf)
315 }
316
317 #[cfg(feature = "tokio")]
318 async fn read_response_async(stream: &mut TokioTcpStream) -> Result<QueryBuf, Error> {
319         let mut len_bytes = [0; 2];
320         stream.read_exact(&mut len_bytes).await?;
321         let mut buf = QueryBuf::new_zeroed(u16::from_be_bytes(len_bytes));
322         stream.read_exact(&mut buf).await?;
323         Ok(buf)
324 }
325
326 #[cfg(feature = "std")]
327 macro_rules! build_proof_impl {
328         ($stream: ident, $send_query: ident, $read_response: ident, $domain: expr, $ty: expr $(, $async_ok: tt)?) => { {
329                 // We require the initial query to have already gone out, and assume our resolver will
330                 // return any CNAMEs all the way to the final record in the response. From there, we just
331                 // have to take any RRSIGs in the response and walk them up to the root. We do so
332                 // iteratively, sending DNSKEY and DS lookups after every response, deduplicating requests
333                 // using `dnskeys_requested`.
334                 let (mut builder, initial_query) = ProofBuilder::new($domain, $ty);
335                 $send_query(&mut $stream, &initial_query)
336                         $(.await?; $async_ok)??; // Either await?; Ok(())?, or just ?
337                 while builder.awaiting_responses() {
338                         let response = $read_response(&mut $stream)
339                                 $(.await?; $async_ok)??; // Either await?; Ok(())?, or just ?
340                         let new_queries = builder.process_response(&response)
341                                 .map_err(|()| Error::new(ErrorKind::Other, "Bad response"))?;
342                         for query in new_queries {
343                                 $send_query(&mut $stream, &query)
344                                         $(.await?; $async_ok)??; // Either await?; Ok(())?, or just ?
345                         }
346                 }
347
348                 builder.finish_proof()
349                         .map_err(|()| Error::new(ErrorKind::Other, "Too many requests required"))
350         } }
351 }
352
353 #[cfg(feature = "std")]
354 fn build_proof(resolver: SocketAddr, domain: &Name, ty: u16) -> Result<(Vec<u8>, u32), Error> {
355         let mut stream = TcpStream::connect(resolver)?;
356         build_proof_impl!(stream, send_query, read_response, domain, ty)
357 }
358
359 #[cfg(feature = "tokio")]
360 async fn build_proof_async(resolver: SocketAddr, domain: &Name, ty: u16) -> Result<(Vec<u8>, u32), Error> {
361         let mut stream = TokioTcpStream::connect(resolver).await?;
362         build_proof_impl!(stream, send_query_async, read_response_async, domain, ty, { Ok::<(), Error>(()) })
363 }
364
365 /// Builds a DNSSEC proof for an A record by querying a recursive resolver, returning the proof as
366 /// well as the TTL for the proof provided by the recursive resolver.
367 ///
368 /// Note that this proof is NOT verified in any way, you need to use the [`crate::validation`]
369 /// module to validate the records contained.
370 #[cfg(feature = "std")]
371 pub fn build_a_proof(resolver: SocketAddr, domain: &Name) -> Result<(Vec<u8>, u32), Error> {
372         build_proof(resolver, domain, A::TYPE)
373 }
374
375 /// Builds a DNSSEC proof for an AAAA record by querying a recursive resolver, returning the proof
376 /// as well as the TTL for the proof provided by the recursive resolver.
377 ///
378 /// Note that this proof is NOT verified in any way, you need to use the [`crate::validation`]
379 /// module to validate the records contained.
380 #[cfg(feature = "std")]
381 pub fn build_aaaa_proof(resolver: SocketAddr, domain: &Name) -> Result<(Vec<u8>, u32), Error> {
382         build_proof(resolver, domain, AAAA::TYPE)
383 }
384
385 /// Builds a DNSSEC proof for an TXT record by querying a recursive resolver, returning the proof
386 /// as well as the TTL for the proof provided by the recursive resolver.
387 ///
388 /// Note that this proof is NOT verified in any way, you need to use the [`crate::validation`]
389 /// module to validate the records contained.
390 #[cfg(feature = "std")]
391 pub fn build_txt_proof(resolver: SocketAddr, domain: &Name) -> Result<(Vec<u8>, u32), Error> {
392         build_proof(resolver, domain, Txt::TYPE)
393 }
394
395 /// Builds a DNSSEC proof for an TLSA record by querying a recursive resolver, returning the proof
396 /// as well as the TTL for the proof provided by the recursive resolver.
397 ///
398 /// Note that this proof is NOT verified in any way, you need to use the [`crate::validation`]
399 /// module to validate the records contained.
400 #[cfg(feature = "std")]
401 pub fn build_tlsa_proof(resolver: SocketAddr, domain: &Name) -> Result<(Vec<u8>, u32), Error> {
402         build_proof(resolver, domain, TLSA::TYPE)
403 }
404
405
406 /// Builds a DNSSEC proof for an A record by querying a recursive resolver, returning the proof as
407 /// well as the TTL for the proof provided by the recursive resolver.
408 ///
409 /// Note that this proof is NOT verified in any way, you need to use the [`crate::validation`]
410 /// module to validate the records contained.
411 #[cfg(feature = "tokio")]
412 pub async fn build_a_proof_async(resolver: SocketAddr, domain: &Name) -> Result<(Vec<u8>, u32), Error> {
413         build_proof_async(resolver, domain, A::TYPE).await
414 }
415
416 /// Builds a DNSSEC proof for an AAAA record by querying a recursive resolver, returning the proof
417 /// as well as the TTL for the proof provided by the recursive resolver.
418 ///
419 /// Note that this proof is NOT verified in any way, you need to use the [`crate::validation`]
420 /// module to validate the records contained.
421 #[cfg(feature = "tokio")]
422 pub async fn build_aaaa_proof_async(resolver: SocketAddr, domain: &Name) -> Result<(Vec<u8>, u32), Error> {
423         build_proof_async(resolver, domain, AAAA::TYPE).await
424 }
425
426 /// Builds a DNSSEC proof for an TXT record by querying a recursive resolver, returning the proof
427 /// as well as the TTL for the proof provided by the recursive resolver.
428 ///
429 /// Note that this proof is NOT verified in any way, you need to use the [`crate::validation`]
430 /// module to validate the records contained.
431 #[cfg(feature = "tokio")]
432 pub async fn build_txt_proof_async(resolver: SocketAddr, domain: &Name) -> Result<(Vec<u8>, u32), Error> {
433         build_proof_async(resolver, domain, Txt::TYPE).await
434 }
435
436 /// Builds a DNSSEC proof for an TLSA record by querying a recursive resolver, returning the proof
437 /// as well as the TTL for the proof provided by the recursive resolver.
438 ///
439 /// Note that this proof is NOT verified in any way, you need to use the [`crate::validation`]
440 /// module to validate the records contained.
441 #[cfg(feature = "tokio")]
442 pub async fn build_tlsa_proof_async(resolver: SocketAddr, domain: &Name) -> Result<(Vec<u8>, u32), Error> {
443         build_proof_async(resolver, domain, TLSA::TYPE).await
444 }
445
446 #[cfg(all(feature = "validation", feature = "std", test))]
447 mod tests {
448         use super::*;
449         use crate::validation::*;
450
451         use rand::seq::SliceRandom;
452
453         use std::net::ToSocketAddrs;
454         use std::time::SystemTime;
455
456         #[test]
457         fn test_cloudflare_txt_query() {
458                 let sockaddr = "8.8.8.8:53".to_socket_addrs().unwrap().next().unwrap();
459                 let query_name = "cloudflare.com.".try_into().unwrap();
460                 let (proof, _) = build_txt_proof(sockaddr, &query_name).unwrap();
461
462                 let mut rrs = parse_rr_stream(&proof).unwrap();
463                 rrs.shuffle(&mut rand::rngs::OsRng);
464                 let verified_rrs = verify_rr_stream(&rrs).unwrap();
465                 assert!(verified_rrs.verified_rrs.len() > 1);
466
467                 let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs();
468                 assert!(verified_rrs.valid_from < now);
469                 assert!(verified_rrs.expires > now);
470         }
471
472         #[test]
473         fn test_sha1_query() {
474                 let sockaddr = "8.8.8.8:53".to_socket_addrs().unwrap().next().unwrap();
475                 let query_name = "benthecarman.com.".try_into().unwrap();
476                 let (proof, _) = build_a_proof(sockaddr, &query_name).unwrap();
477
478                 let mut rrs = parse_rr_stream(&proof).unwrap();
479                 rrs.shuffle(&mut rand::rngs::OsRng);
480                 let verified_rrs = verify_rr_stream(&rrs).unwrap();
481                 assert!(verified_rrs.verified_rrs.len() >= 1);
482
483                 let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs();
484                 assert!(verified_rrs.valid_from < now);
485                 assert!(verified_rrs.expires > now);
486         }
487
488         #[test]
489         fn test_txt_query() {
490                 let sockaddr = "8.8.8.8:53".to_socket_addrs().unwrap().next().unwrap();
491                 let query_name = "matt.user._bitcoin-payment.mattcorallo.com.".try_into().unwrap();
492                 let (proof, _) = build_txt_proof(sockaddr, &query_name).unwrap();
493
494                 let mut rrs = parse_rr_stream(&proof).unwrap();
495                 rrs.shuffle(&mut rand::rngs::OsRng);
496                 let verified_rrs = verify_rr_stream(&rrs).unwrap();
497                 assert_eq!(verified_rrs.verified_rrs.len(), 1);
498
499                 let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs();
500                 assert!(verified_rrs.valid_from < now);
501                 assert!(verified_rrs.expires > now);
502         }
503
504         #[test]
505         fn test_cname_query() {
506                 for resolver in ["1.1.1.1:53", "8.8.8.8:53", "9.9.9.9:53"] {
507                         let sockaddr = resolver.to_socket_addrs().unwrap().next().unwrap();
508                         let query_name = "cname_test.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap();
509                         let (proof, _) = build_txt_proof(sockaddr, &query_name).unwrap();
510
511                         let mut rrs = parse_rr_stream(&proof).unwrap();
512                         rrs.shuffle(&mut rand::rngs::OsRng);
513                         let verified_rrs = verify_rr_stream(&rrs).unwrap();
514                         assert_eq!(verified_rrs.verified_rrs.len(), 2);
515
516                         let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs();
517                         assert!(verified_rrs.valid_from < now);
518                         assert!(verified_rrs.expires > now);
519
520                         let resolved_rrs = verified_rrs.resolve_name(&query_name);
521                         assert_eq!(resolved_rrs.len(), 1);
522                         if let RR::Txt(txt) = &resolved_rrs[0] {
523                                 assert_eq!(txt.name.as_str(), "txt_test.dnssec_proof_tests.bitcoin.ninja.");
524                                 assert_eq!(txt.data, b"dnssec_prover_test");
525                         } else { panic!(); }
526                 }
527         }
528
529         #[cfg(feature = "tokio")]
530         use tokio_crate as tokio;
531
532         #[cfg(feature = "tokio")]
533         #[tokio::test]
534         async fn test_txt_query_async() {
535                 let sockaddr = "8.8.8.8:53".to_socket_addrs().unwrap().next().unwrap();
536                 let query_name = "matt.user._bitcoin-payment.mattcorallo.com.".try_into().unwrap();
537                 let (proof, _) = build_txt_proof_async(sockaddr, &query_name).await.unwrap();
538
539                 let mut rrs = parse_rr_stream(&proof).unwrap();
540                 rrs.shuffle(&mut rand::rngs::OsRng);
541                 let verified_rrs = verify_rr_stream(&rrs).unwrap();
542                 assert_eq!(verified_rrs.verified_rrs.len(), 1);
543
544                 let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs();
545                 assert!(verified_rrs.valid_from < now);
546                 assert!(verified_rrs.expires > now);
547         }
548
549         #[cfg(feature = "tokio")]
550         #[tokio::test]
551         async fn test_cross_domain_cname_query_async() {
552                 for resolver in ["1.1.1.1:53", "8.8.8.8:53", "9.9.9.9:53"] {
553                         let sockaddr = resolver.to_socket_addrs().unwrap().next().unwrap();
554                         let query_name = "wildcard.x_domain_cname_wild.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap();
555                         let (proof, _) = build_txt_proof_async(sockaddr, &query_name).await.unwrap();
556
557                         let mut rrs = parse_rr_stream(&proof).unwrap();
558                         rrs.shuffle(&mut rand::rngs::OsRng);
559                         let verified_rrs = verify_rr_stream(&rrs).unwrap();
560                         assert_eq!(verified_rrs.verified_rrs.len(), 2);
561
562                         let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs();
563                         assert!(verified_rrs.valid_from < now);
564                         assert!(verified_rrs.expires > now);
565
566                         let resolved_rrs = verified_rrs.resolve_name(&query_name);
567                         assert_eq!(resolved_rrs.len(), 1);
568                         if let RR::Txt(txt) = &resolved_rrs[0] {
569                                 assert_eq!(txt.name.as_str(), "matt.user._bitcoin-payment.mattcorallo.com.");
570                                 assert!(txt.data.starts_with(b"bitcoin:"));
571                         } else { panic!(); }
572                 }
573         }
574
575         #[cfg(feature = "tokio")]
576         #[tokio::test]
577         async fn test_dname_wildcard_query_async() {
578                 for resolver in ["1.1.1.1:53", "8.8.8.8:53", "9.9.9.9:53"] {
579                         let sockaddr = resolver.to_socket_addrs().unwrap().next().unwrap();
580                         let query_name = "wildcard_a.wildcard_b.dname_test.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap();
581                         let (proof, _) = build_txt_proof_async(sockaddr, &query_name).await.unwrap();
582
583                         let mut rrs = parse_rr_stream(&proof).unwrap();
584                         rrs.shuffle(&mut rand::rngs::OsRng);
585                         let verified_rrs = verify_rr_stream(&rrs).unwrap();
586                         assert_eq!(verified_rrs.verified_rrs.len(), 3);
587
588                         let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs();
589                         assert!(verified_rrs.valid_from < now);
590                         assert!(verified_rrs.expires > now);
591
592                         let resolved_rrs = verified_rrs.resolve_name(&query_name);
593                         assert_eq!(resolved_rrs.len(), 1);
594                         if let RR::Txt(txt) = &resolved_rrs[0] {
595                                 assert_eq!(txt.name.as_str(), "cname.wildcard_test.dnssec_proof_tests.bitcoin.ninja.");
596                                 assert_eq!(txt.data, b"wildcard_test");
597                         } else { panic!(); }
598                 }
599         }
600
601 }