1use core::{cmp, ops};
5use alloc::vec;
6use alloc::vec::Vec;
7
8#[cfg(feature = "std")]
9use std::net::{SocketAddr, TcpStream};
10#[cfg(feature = "std")]
11use std::io::{Read, Write, Error, ErrorKind};
12
13#[cfg(feature = "tokio")]
14use tokio_crate::net::TcpStream as TokioTcpStream;
15#[cfg(feature = "tokio")]
16use tokio_crate::io::{AsyncReadExt, AsyncWriteExt};
17
18use crate::rr::*;
19use crate::ser::*;
20use crate::MAX_PROOF_STEPS;
21
22#[cfg(any(test, fuzzing))]
26const STACK_BUF_LIMIT: u16 = 32;
27#[cfg(not(any(test, fuzzing)))]
28const STACK_BUF_LIMIT: u16 = 2048;
29
30#[derive(Clone, PartialEq, Eq)]
32pub struct QueryBuf {
33 buf: [u8; STACK_BUF_LIMIT as usize],
34 heap_buf: Vec<u8>,
35 len: u16,
36}
37impl QueryBuf {
38 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 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 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}
77impl 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}
87impl 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
97const TXID: u16 = 0;
100
101fn 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]); query.extend_from_slice(&[0, 1, 0, 0, 0, 0, 0, 1]); write_name(&mut query, domain);
107 query.extend_from_slice(&ty.to_be_bytes());
108 query.extend_from_slice(&1u16.to_be_bytes()); query.extend_from_slice(&[0, 0, 0x29]); query.extend_from_slice(&0u16.to_be_bytes()); query.extend_from_slice(&[0, 0]); query.extend_from_slice(&0x8000u16.to_be_bytes()); query.extend_from_slice(&0u16.to_be_bytes()); query
115}
116
117#[derive(PartialEq, Eq)]
121pub enum ProofBuildingError {
122 InvalidResponse,
127 ServerFailure,
132 NoSuchName,
136 MissingRecord,
140 Unauthenticated,
146 NoResponseExpected,
151}
152
153impl core::fmt::Display for ProofBuildingError {
154 fn fmt(&self, fmt: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
155 match self {
156 ProofBuildingError::InvalidResponse =>
157 fmt.write_str("The server provided a response we could not understand"),
158 ProofBuildingError::ServerFailure =>
159 fmt.write_str("The server indicated it failed to talk to a required authorative DNS server"),
160 ProofBuildingError::NoSuchName =>
161 fmt.write_str("The server indicated the requested hostname does not exist"),
162 ProofBuildingError::MissingRecord =>
163 fmt.write_str("The server indicated one of the records we needed to build our proof did not exist"),
164 ProofBuildingError::Unauthenticated =>
165 fmt.write_str("The server indicated the records we needed were not DNSSEC-authenticated"),
166 ProofBuildingError::NoResponseExpected =>
167 fmt.write_str("Internal error in the proof building software"),
168 }
169 }
170}
171
172impl core::fmt::Debug for ProofBuildingError {
173 fn fmt(&self, fmt: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
174 core::fmt::Display::fmt(self, fmt)
175 }
176}
177
178#[cfg(feature = "std")]
179impl std::error::Error for ProofBuildingError {
180
181}
182
183#[cfg(dnssec_prover_fuzzing)]
184pub fn fuzz_response(response: &[u8]) {
186 let (mut proof, mut names) = (Vec::new(), Vec::new());
187 let _ = handle_response(response, &mut proof, &mut names);
188}
189
190fn handle_response(resp: &[u8], proof: &mut Vec<u8>, rrsig_key_names: &mut Vec<Name>) -> Result<u32, ProofBuildingError> {
195 let mut read: &[u8] = resp;
196 let resp_txid = read_u16(&mut read).map_err(|()| ProofBuildingError::InvalidResponse)?;
197 if resp_txid != TXID { return Err(ProofBuildingError::InvalidResponse); }
198 let flags = read_u16(&mut read).map_err(|()| ProofBuildingError::InvalidResponse)?;
200 if flags & 0b1000_0000_0000_0000 == 0 {
201 return Err(ProofBuildingError::InvalidResponse);
203 }
204 if flags & 0b1111 == 2 || flags & 0b1111 == 1 {
205 return Err(ProofBuildingError::ServerFailure);
206 }
207 if flags & 0b1111 == 3 {
208 return Err(ProofBuildingError::MissingRecord);
210 }
211 if flags & 0b0111_1010_0000_1111 != 0 {
213 return Err(ProofBuildingError::InvalidResponse);
214 }
215 if flags & 0b10_0000 == 0 {
216 return Err(ProofBuildingError::Unauthenticated);
218 }
219 let questions = read_u16(&mut read).map_err(|()| ProofBuildingError::InvalidResponse)?;
220 if questions != 1 { return Err(ProofBuildingError::InvalidResponse); }
221 let answers = read_u16(&mut read).map_err(|()| ProofBuildingError::InvalidResponse)?;
222 if answers == 0 { return Err(ProofBuildingError::InvalidResponse); }
223 let authorities = read_u16(&mut read).map_err(|()| ProofBuildingError::InvalidResponse)?;
224 let _additional = read_u16(&mut read).map_err(|()| ProofBuildingError::InvalidResponse)?;
225
226 for _ in 0..questions {
227 read_wire_packet_name(&mut read, resp).map_err(|()| ProofBuildingError::InvalidResponse)?;
228 read_u16(&mut read).map_err(|()| ProofBuildingError::InvalidResponse)?; read_u16(&mut read).map_err(|()| ProofBuildingError::InvalidResponse)?; }
231
232 let mut min_ttl = u32::MAX;
234 for _ in 0..answers {
235 let (rr, ttl) = parse_wire_packet_rr(&mut read, resp)
236 .map_err(|()| ProofBuildingError::InvalidResponse)?;
237 write_rr(&rr, ttl, proof);
238 min_ttl = cmp::min(min_ttl, ttl);
239 if let RR::RRSig(rrsig) = rr { rrsig_key_names.push(rrsig.key_name); }
240 }
241
242 for _ in 0..authorities {
243 let (rr, ttl) = parse_wire_packet_rr(&mut read, resp)
246 .map_err(|()| ProofBuildingError::InvalidResponse)?;
247 match &rr {
248 RR::RRSig(rrsig) => {
249 if rrsig.ty != NSec::TYPE && rrsig.ty != NSec3::TYPE {
250 continue;
251 }
252 },
253 RR::NSec(_)|RR::NSec3(_) => {},
254 _ => continue,
255 }
256 write_rr(&rr, ttl, proof);
257 min_ttl = cmp::min(min_ttl, ttl);
258 if let RR::RRSig(rrsig) = rr { rrsig_key_names.push(rrsig.key_name); }
259 }
260
261 Ok(min_ttl)
262}
263
264#[cfg(dnssec_prover_fuzzing)]
265pub fn fuzz_proof_builder(mut response_stream: &[u8]) {
267 let (mut builder, _) = ProofBuilder::new(&"example.com.".try_into().unwrap(), Txt::TYPE);
268 while builder.awaiting_responses() {
269 let len = if let Ok(len) = read_u16(&mut response_stream) { len } else { return };
270 let mut buf = QueryBuf::new_zeroed(len);
271 if response_stream.len() < len as usize { return; }
272 buf.copy_from_slice(&response_stream[..len as usize]);
273 response_stream = &response_stream[len as usize..];
274 let _ = builder.process_response(&buf);
275 }
276 let _ = builder.finish_proof();
277}
278
279#[derive(Clone)]
294pub struct ProofBuilder {
295 proof: Vec<u8>,
296 min_ttl: u32,
297 dnskeys_requested: Vec<Name>,
298 pending_queries: usize,
299 queries_made: usize,
300}
301
302impl ProofBuilder {
303 pub fn new(name: &Name, ty: u16) -> (ProofBuilder, QueryBuf) {
312 let initial_query = build_query(name, ty);
313 (ProofBuilder {
314 proof: Vec::new(),
315 min_ttl: u32::MAX,
316 dnskeys_requested: Vec::with_capacity(MAX_PROOF_STEPS),
317 pending_queries: 1,
318 queries_made: 1,
319 }, initial_query)
320 }
321
322 pub fn awaiting_responses(&self) -> bool {
328 self.pending_queries > 0 && self.queries_made <= MAX_PROOF_STEPS
329 }
330
331 pub fn process_response(&mut self, resp: &QueryBuf) -> Result<Vec<QueryBuf>, ProofBuildingError> {
334 if self.pending_queries == 0 { return Err(ProofBuildingError::NoResponseExpected); }
335
336 let mut rrsig_key_names = Vec::new();
337 let min_ttl = match handle_response(resp, &mut self.proof, &mut rrsig_key_names) {
338 Ok(min_ttl) => min_ttl,
339 Err(err) => {
340 if self.proof.is_empty() && err == ProofBuildingError::MissingRecord {
341 return Err(ProofBuildingError::NoSuchName);
342 } else {
343 return Err(err);
344 }
345 },
346 };
347 self.min_ttl = cmp::min(self.min_ttl, min_ttl);
348 self.pending_queries -= 1;
349
350 rrsig_key_names.sort_unstable();
351 rrsig_key_names.dedup();
352
353 let mut new_queries = Vec::with_capacity(2);
354 for key_name in rrsig_key_names.drain(..) {
355 if !self.dnskeys_requested.contains(&key_name) {
356 new_queries.push(build_query(&key_name, DnsKey::TYPE));
357 self.pending_queries += 1;
358 self.queries_made += 1;
359 self.dnskeys_requested.push(key_name.clone());
360
361 if key_name.as_str() != "." {
362 new_queries.push(build_query(&key_name, DS::TYPE));
363 self.pending_queries += 1;
364 self.queries_made += 1;
365 }
366 }
367 }
368 if self.queries_made <= MAX_PROOF_STEPS {
369 Ok(new_queries)
370 } else {
371 Ok(Vec::new())
372 }
373 }
374
375 pub fn finish_proof(self) -> Result<(Vec<u8>, u32), ()> {
381 if self.pending_queries > 0 || self.queries_made > MAX_PROOF_STEPS {
382 Err(())
383 } else {
384 Ok((self.proof, self.min_ttl))
385 }
386 }
387}
388
389#[cfg(feature = "std")]
390fn send_query(stream: &mut TcpStream, query: &[u8]) -> Result<(), Error> {
391 stream.write_all(&(query.len() as u16).to_be_bytes())?;
392 stream.write_all(&query)?;
393 Ok(())
394}
395
396#[cfg(feature = "tokio")]
397async fn send_query_async(stream: &mut TokioTcpStream, query: &[u8]) -> Result<(), Error> {
398 stream.write_all(&(query.len() as u16).to_be_bytes()).await?;
399 stream.write_all(&query).await?;
400 Ok(())
401}
402
403#[cfg(feature = "std")]
404fn read_response(stream: &mut TcpStream) -> Result<QueryBuf, Error> {
405 let mut len_bytes = [0; 2];
406 stream.read_exact(&mut len_bytes)?;
407 let mut buf = QueryBuf::new_zeroed(u16::from_be_bytes(len_bytes));
408 stream.read_exact(&mut buf)?;
409 Ok(buf)
410}
411
412#[cfg(feature = "tokio")]
413async fn read_response_async(stream: &mut TokioTcpStream) -> Result<QueryBuf, Error> {
414 let mut len_bytes = [0; 2];
415 stream.read_exact(&mut len_bytes).await?;
416 let mut buf = QueryBuf::new_zeroed(u16::from_be_bytes(len_bytes));
417 stream.read_exact(&mut buf).await?;
418 Ok(buf)
419}
420
421#[cfg(feature = "std")]
422macro_rules! build_proof_impl {
423 ($stream: ident, $send_query: ident, $read_response: ident, $domain: expr, $ty: expr $(, $async_ok: tt)?) => { {
424 let (mut builder, initial_query) = ProofBuilder::new($domain, $ty);
430 $send_query(&mut $stream, &initial_query)
431 $(.await?; $async_ok)??; while builder.awaiting_responses() {
433 let response = $read_response(&mut $stream)
434 $(.await?; $async_ok)??; let new_queries = builder.process_response(&response)
436 .map_err(|err| Error::new(ErrorKind::Other, err))?;
437 for query in new_queries {
438 $send_query(&mut $stream, &query)
439 $(.await?; $async_ok)??; }
441 }
442
443 builder.finish_proof()
444 .map_err(|()| Error::new(ErrorKind::Other, "Too many requests required"))
445 } }
446}
447
448#[cfg(feature = "std")]
449fn build_proof(resolver: SocketAddr, domain: &Name, ty: u16) -> Result<(Vec<u8>, u32), Error> {
450 let mut stream = TcpStream::connect(resolver)?;
451 build_proof_impl!(stream, send_query, read_response, domain, ty)
452}
453
454#[cfg(feature = "tokio")]
455async fn build_proof_async(resolver: SocketAddr, domain: &Name, ty: u16) -> Result<(Vec<u8>, u32), Error> {
456 let mut stream = TokioTcpStream::connect(resolver).await?;
457 build_proof_impl!(stream, send_query_async, read_response_async, domain, ty, { Ok::<(), Error>(()) })
458}
459
460#[cfg(feature = "std")]
466pub fn build_a_proof(resolver: SocketAddr, domain: &Name) -> Result<(Vec<u8>, u32), Error> {
467 build_proof(resolver, domain, A::TYPE)
468}
469
470#[cfg(feature = "std")]
476pub fn build_aaaa_proof(resolver: SocketAddr, domain: &Name) -> Result<(Vec<u8>, u32), Error> {
477 build_proof(resolver, domain, AAAA::TYPE)
478}
479
480#[cfg(feature = "std")]
486pub fn build_txt_proof(resolver: SocketAddr, domain: &Name) -> Result<(Vec<u8>, u32), Error> {
487 build_proof(resolver, domain, Txt::TYPE)
488}
489
490#[cfg(feature = "std")]
496pub fn build_tlsa_proof(resolver: SocketAddr, domain: &Name) -> Result<(Vec<u8>, u32), Error> {
497 build_proof(resolver, domain, TLSA::TYPE)
498}
499
500
501#[cfg(feature = "tokio")]
507pub async fn build_a_proof_async(resolver: SocketAddr, domain: &Name) -> Result<(Vec<u8>, u32), Error> {
508 build_proof_async(resolver, domain, A::TYPE).await
509}
510
511#[cfg(feature = "tokio")]
517pub async fn build_aaaa_proof_async(resolver: SocketAddr, domain: &Name) -> Result<(Vec<u8>, u32), Error> {
518 build_proof_async(resolver, domain, AAAA::TYPE).await
519}
520
521#[cfg(feature = "tokio")]
527pub async fn build_txt_proof_async(resolver: SocketAddr, domain: &Name) -> Result<(Vec<u8>, u32), Error> {
528 build_proof_async(resolver, domain, Txt::TYPE).await
529}
530
531#[cfg(feature = "tokio")]
537pub async fn build_tlsa_proof_async(resolver: SocketAddr, domain: &Name) -> Result<(Vec<u8>, u32), Error> {
538 build_proof_async(resolver, domain, TLSA::TYPE).await
539}
540
541#[cfg(all(feature = "validation", feature = "std", test))]
542mod tests {
543 use super::*;
544 use crate::validation::*;
545
546 use rand::seq::SliceRandom;
547
548 use std::net::ToSocketAddrs;
549 use std::time::SystemTime;
550
551 #[test]
552 fn test_cloudflare_txt_query() {
553 let sockaddr = "8.8.8.8:53".to_socket_addrs().unwrap().next().unwrap();
554 let query_name = "cloudflare.com.".try_into().unwrap();
555 let (proof, _) = build_txt_proof(sockaddr, &query_name).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!(verified_rrs.verified_rrs.len() > 1);
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
567 #[test]
568 fn test_sha1_query() {
569 let sockaddr = "8.8.8.8:53".to_socket_addrs().unwrap().next().unwrap();
570 let query_name = "benthecarman.com.".try_into().unwrap();
571 let (proof, _) = build_a_proof(sockaddr, &query_name).unwrap();
572
573 let mut rrs = parse_rr_stream(&proof).unwrap();
574 rrs.shuffle(&mut rand::rngs::OsRng);
575 let verified_rrs = verify_rr_stream(&rrs).unwrap();
576 assert!(verified_rrs.verified_rrs.len() >= 1);
577
578 let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs();
579 assert!(verified_rrs.valid_from < now);
580 assert!(verified_rrs.expires > now);
581 }
582
583 #[test]
584 fn test_txt_query() {
585 let sockaddr = "8.8.8.8:53".to_socket_addrs().unwrap().next().unwrap();
586 let query_name = "matt.user._bitcoin-payment.mattcorallo.com.".try_into().unwrap();
587 let (proof, _) = build_txt_proof(sockaddr, &query_name).unwrap();
588
589 let mut rrs = parse_rr_stream(&proof).unwrap();
590 rrs.shuffle(&mut rand::rngs::OsRng);
591 let verified_rrs = verify_rr_stream(&rrs).unwrap();
592 assert_eq!(verified_rrs.verified_rrs.len(), 1);
593
594 let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs();
595 assert!(verified_rrs.valid_from < now);
596 assert!(verified_rrs.expires > now);
597 }
598
599 #[test]
600 fn test_cname_query() {
601 for resolver in ["1.1.1.1:53", "8.8.8.8:53", "9.9.9.9:53"] {
602 let sockaddr = resolver.to_socket_addrs().unwrap().next().unwrap();
603 let query_name = "cname_test.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap();
604 let (proof, _) = build_txt_proof(sockaddr, &query_name).unwrap();
605
606 let mut rrs = parse_rr_stream(&proof).unwrap();
607 rrs.shuffle(&mut rand::rngs::OsRng);
608 let verified_rrs = verify_rr_stream(&rrs).unwrap();
609 assert_eq!(verified_rrs.verified_rrs.len(), 2);
610
611 let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs();
612 assert!(verified_rrs.valid_from < now);
613 assert!(verified_rrs.expires > now);
614
615 let resolved_rrs = verified_rrs.resolve_name(&query_name);
616 assert_eq!(resolved_rrs.len(), 1);
617 if let RR::Txt(txt) = &resolved_rrs[0] {
618 assert_eq!(txt.name.as_str(), "txt_test.dnssec_proof_tests.bitcoin.ninja.");
619 assert_eq!(txt.data.as_vec(), b"dnssec_prover_test");
620 } else { panic!(); }
621 }
622 }
623
624 #[cfg(feature = "tokio")]
625 use tokio_crate as tokio;
626
627 #[cfg(feature = "tokio")]
628 #[tokio::test]
629 async fn test_txt_query_async() {
630 let sockaddr = "8.8.8.8:53".to_socket_addrs().unwrap().next().unwrap();
631 let query_name = "matt.user._bitcoin-payment.mattcorallo.com.".try_into().unwrap();
632 let (proof, _) = build_txt_proof_async(sockaddr, &query_name).await.unwrap();
633
634 let mut rrs = parse_rr_stream(&proof).unwrap();
635 rrs.shuffle(&mut rand::rngs::OsRng);
636 let verified_rrs = verify_rr_stream(&rrs).unwrap();
637 assert_eq!(verified_rrs.verified_rrs.len(), 1);
638
639 let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs();
640 assert!(verified_rrs.valid_from < now);
641 assert!(verified_rrs.expires > now);
642 }
643
644 #[cfg(feature = "tokio")]
645 #[tokio::test]
646 async fn test_cross_domain_cname_query_async() {
647 for resolver in ["1.1.1.1:53", "8.8.8.8:53", "9.9.9.9:53"] {
648 let sockaddr = resolver.to_socket_addrs().unwrap().next().unwrap();
649 let query_name = "wildcard.x_domain_cname_wild.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap();
650 let (proof, _) = build_txt_proof_async(sockaddr, &query_name).await.unwrap();
651
652 let mut rrs = parse_rr_stream(&proof).unwrap();
653 rrs.shuffle(&mut rand::rngs::OsRng);
654 let verified_rrs = verify_rr_stream(&rrs).unwrap();
655 assert_eq!(verified_rrs.verified_rrs.len(), 2);
656
657 let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs();
658 assert!(verified_rrs.valid_from < now);
659 assert!(verified_rrs.expires > now);
660
661 let resolved_rrs = verified_rrs.resolve_name(&query_name);
662 assert_eq!(resolved_rrs.len(), 1);
663 if let RR::Txt(txt) = &resolved_rrs[0] {
664 assert_eq!(txt.name.as_str(), "matt.user._bitcoin-payment.mattcorallo.com.");
665 assert!(txt.data.as_vec().starts_with(b"bitcoin:"));
666 } else { panic!(); }
667 }
668 }
669
670 #[cfg(feature = "tokio")]
671 #[tokio::test]
672 async fn test_dname_wildcard_query_async() {
673 for resolver in ["1.1.1.1:53", "8.8.8.8:53", "9.9.9.9:53"] {
674 let sockaddr = resolver.to_socket_addrs().unwrap().next().unwrap();
675 let query_name = "wildcard_a.wildcard_b.dname_test.dnssec_proof_tests.bitcoin.ninja.".try_into().unwrap();
676 let (proof, _) = build_txt_proof_async(sockaddr, &query_name).await.unwrap();
677
678 let mut rrs = parse_rr_stream(&proof).unwrap();
679 rrs.shuffle(&mut rand::rngs::OsRng);
680 let verified_rrs = verify_rr_stream(&rrs).unwrap();
681 assert_eq!(verified_rrs.verified_rrs.len(), 3);
682
683 let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs();
684 assert!(verified_rrs.valid_from < now);
685 assert!(verified_rrs.expires > now);
686
687 let resolved_rrs = verified_rrs.resolve_name(&query_name);
688 assert_eq!(resolved_rrs.len(), 1);
689 if let RR::Txt(txt) = &resolved_rrs[0] {
690 assert_eq!(txt.name.as_str(), "cname.wildcard_test.dnssec_proof_tests.bitcoin.ninja.");
691 assert_eq!(txt.data.as_vec(), b"wildcard_test");
692 } else { panic!(); }
693 }
694 }
695
696 #[cfg(feature = "tokio")]
697 #[tokio::test]
698 async fn test_tbast_ovh_hosted() {
699 for resolver in ["1.1.1.1:53", "8.8.8.8:53", "9.9.9.9:53"] {
701 let sockaddr = resolver.to_socket_addrs().unwrap().next().unwrap();
702 let query_name = "me.user._bitcoin-payment.t-bast.xyz.".try_into().unwrap();
703 let (proof, _) = build_txt_proof_async(sockaddr, &query_name).await.unwrap();
704
705 let mut rrs = parse_rr_stream(&proof).unwrap();
706 rrs.shuffle(&mut rand::rngs::OsRng);
707 let verified_rrs = verify_rr_stream(&rrs).unwrap();
708 assert_eq!(verified_rrs.verified_rrs.len(), 1);
709
710 let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs();
711 assert!(verified_rrs.valid_from < now);
712 assert!(verified_rrs.expires > now);
713
714 let resolved_rrs = verified_rrs.resolve_name(&query_name);
715 assert_eq!(resolved_rrs.len(), 1);
716 if let RR::Txt(txt) = &resolved_rrs[0] {
717 assert_eq!(txt.name.as_str(), "me.user._bitcoin-payment.t-bast.xyz.");
718 assert!(txt.data.as_vec().starts_with(b"bitcoin:"));
719 } else { panic!(); }
720 }
721 }
722
723 #[cfg(feature = "tokio")]
724 #[tokio::test]
725 async fn test_no_dnssec() {
726 for resolver in ["1.1.1.1:53", "8.8.8.8:53", "9.9.9.9:53"] {
730 let sockaddr = resolver.to_socket_addrs().unwrap().next().unwrap();
731 let query_name = "google.com.".try_into().unwrap();
732 let err = build_a_proof_async(sockaddr, &query_name).await.unwrap_err();
733 assert_eq!(err.into_inner().unwrap().downcast().unwrap(), Box::new(ProofBuildingError::Unauthenticated));
734 }
735 }
736}