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