1 //! This module exposes utilities for building DNSSEC proofs by directly querying a recursive
5 use std::net::{SocketAddr, TcpStream};
6 use std::io::{Read, Write, Error, ErrorKind};
8 #[cfg(feature = "tokio")]
9 use tokio_crate::net::TcpStream as TokioTcpStream;
10 #[cfg(feature = "tokio")]
11 use tokio_crate::io::{AsyncReadExt, AsyncWriteExt};
16 // We don't care about transaction IDs as we're only going to accept signed data. Thus, we use
17 // this constant instead of a random value.
18 const TXID: u16 = 0x4242;
20 fn emap<V>(v: Result<V, ()>) -> Result<V, Error> {
21 v.map_err(|_| Error::new(ErrorKind::Other, "Bad Response"))
24 fn build_query(domain: &Name, ty: u16) -> Vec<u8> {
25 // TODO: Move to not allocating for the query
26 let mut query = Vec::with_capacity(1024);
27 let query_msg_len: u16 = 2 + 2 + 8 + 2 + 2 + name_len(domain) + 11;
28 query.extend_from_slice(&query_msg_len.to_be_bytes());
29 query.extend_from_slice(&TXID.to_be_bytes());
30 query.extend_from_slice(&[0x01, 0x20]); // Flags: Recursive, Authenticated Data
31 query.extend_from_slice(&[0, 1, 0, 0, 0, 0, 0, 1]); // One question, One additional
32 write_name(&mut query, domain);
33 query.extend_from_slice(&ty.to_be_bytes());
34 query.extend_from_slice(&1u16.to_be_bytes()); // INternet class
35 query.extend_from_slice(&[0, 0, 0x29]); // . OPT
36 query.extend_from_slice(&0u16.to_be_bytes()); // 0 UDP payload size
37 query.extend_from_slice(&[0, 0]); // EDNS version 0
38 query.extend_from_slice(&0x8000u16.to_be_bytes()); // Accept DNSSEC RRs
39 query.extend_from_slice(&0u16.to_be_bytes()); // No additional data
43 fn send_query(stream: &mut TcpStream, domain: &Name, ty: u16) -> Result<(), Error> {
44 let query = build_query(domain, ty);
45 stream.write_all(&query)?;
49 #[cfg(feature = "tokio")]
50 async fn send_query_async(stream: &mut TokioTcpStream, domain: &Name, ty: u16) -> Result<(), Error> {
51 let query = build_query(domain, ty);
52 stream.write_all(&query).await?;
56 fn handle_response(resp: &[u8], proof: &mut Vec<u8>, rrsig_key_names: &mut Vec<Name>) -> Result<u32, Error> {
57 let mut read: &[u8] = resp;
58 if emap(read_u16(&mut read))? != TXID { return Err(Error::new(ErrorKind::Other, "bad txid")); }
59 // 2 byte transaction ID
60 let flags = emap(read_u16(&mut read))?;
61 if flags & 0b1000_0000_0000_0000 == 0 {
62 return Err(Error::new(ErrorKind::Other, "Missing response flag"));
64 if flags & 0b0111_1010_0000_0111 != 0 {
65 return Err(Error::new(ErrorKind::Other, "Server indicated error or provided bunk flags"));
67 if flags & 0b10_0000 == 0 {
68 return Err(Error::new(ErrorKind::Other, "Server indicated data could not be authenticated"));
70 let questions = emap(read_u16(&mut read))?;
71 if questions != 1 { return Err(Error::new(ErrorKind::Other, "server responded to multiple Qs")); }
72 let answers = emap(read_u16(&mut read))?;
73 if answers == 0 { return Err(Error::new(ErrorKind::Other, "No answers")); }
74 let _authorities = emap(read_u16(&mut read))?;
75 let _additional = emap(read_u16(&mut read))?;
77 for _ in 0..questions {
78 emap(read_wire_packet_name(&mut read, resp))?;
79 emap(read_u16(&mut read))?; // type
80 emap(read_u16(&mut read))?; // class
83 // Only read the answers (skip authorities and additional) as that's all we care about.
84 let mut min_ttl = u32::MAX;
86 let (rr, ttl) = emap(parse_wire_packet_rr(&mut read, &resp))?;
87 write_rr(&rr, ttl, proof);
88 min_ttl = cmp::min(min_ttl, ttl);
89 if let RR::RRSig(rrsig) = rr { rrsig_key_names.push(rrsig.key_name); }
94 fn read_response(stream: &mut TcpStream, proof: &mut Vec<u8>, rrsig_key_names: &mut Vec<Name>) -> Result<u32, Error> {
96 stream.read_exact(&mut len)?;
97 let mut resp = vec![0; u16::from_be_bytes(len) as usize];
98 stream.read_exact(&mut resp)?;
99 handle_response(&resp, proof, rrsig_key_names)
102 #[cfg(feature = "tokio")]
103 async fn read_response_async(stream: &mut TokioTcpStream, proof: &mut Vec<u8>, rrsig_key_names: &mut Vec<Name>) -> Result<u32, Error> {
104 let mut len = [0; 2];
105 stream.read_exact(&mut len).await?;
106 let mut resp = vec![0; u16::from_be_bytes(len) as usize];
107 stream.read_exact(&mut resp).await?;
108 handle_response(&resp, proof, rrsig_key_names)
111 macro_rules! build_proof_impl {
112 ($stream: ident, $send_query: ident, $read_response: ident $(, $async_ok: tt)?) => { {
113 // We require the initial query to have already gone out, and assume our resolver will
114 // return any CNAMEs all the way to the final record in the response. From there, we just
115 // have to take any RRSIGs in the response and walk them up to the root. We do so
116 // iteratively, sending DNSKEY and DS lookups after every response, deduplicating requests
117 // using `dnskeys_requested`.
118 let mut res = Vec::new(); // The actual proof stream
119 let mut min_ttl = u32::MAX; // Min TTL of any answer record
120 const MAX_REQUESTS: usize = 20;
121 let mut rrsig_key_names = Vec::with_capacity(4); // Last response's RRSIG key_names
122 let mut dnskeys_requested = Vec::with_capacity(MAX_REQUESTS);
123 let mut pending_queries = 1;
124 let mut queries_made = 1;
125 while pending_queries != 0 && queries_made <= MAX_REQUESTS {
126 let response_min_ttl = $read_response(&mut $stream, &mut res, &mut rrsig_key_names)
127 $(.await?; $async_ok)??; // Either await?; Ok(())?, or just ?
128 pending_queries -= 1;
129 min_ttl = cmp::min(min_ttl, response_min_ttl);
130 rrsig_key_names.sort_unstable();
131 rrsig_key_names.dedup();
132 for key_name in rrsig_key_names.drain(..) {
133 if !dnskeys_requested.contains(&key_name) {
134 $send_query(&mut $stream, &key_name, DnsKey::TYPE)
135 $(.await?; $async_ok)??; // Either await?; Ok(())?, or just ?
136 pending_queries += 1;
138 dnskeys_requested.push(key_name.clone());
140 if key_name.as_str() != "." {
141 $send_query(&mut $stream, &key_name, DS::TYPE)
142 $(.await?; $async_ok)??; // Either await?; Ok(())?, or just ?
143 pending_queries += 1;
150 if queries_made > MAX_REQUESTS {
151 Err(Error::new(ErrorKind::Other, "Too many requests required"))
158 fn build_proof(resolver: SocketAddr, domain: &Name, ty: u16) -> Result<(Vec<u8>, u32), Error> {
159 let mut stream = TcpStream::connect(resolver)?;
160 send_query(&mut stream, domain, ty)?;
161 build_proof_impl!(stream, send_query, read_response)
164 #[cfg(feature = "tokio")]
165 async fn build_proof_async(resolver: SocketAddr, domain: &Name, ty: u16) -> Result<(Vec<u8>, u32), Error> {
166 let mut stream = TokioTcpStream::connect(resolver).await?;
167 send_query_async(&mut stream, domain, ty).await?;
168 build_proof_impl!(stream, send_query_async, read_response_async, { Ok::<(), Error>(()) })
171 /// Builds a DNSSEC proof for an A record by querying a recursive resolver, returning the proof as
172 /// well as the TTL for the proof provided by the recursive resolver.
174 /// Note that this proof is NOT verified in any way, you need to use the [`crate::validation`]
175 /// module to validate the records contained.
176 pub fn build_a_proof(resolver: SocketAddr, domain: &Name) -> Result<(Vec<u8>, u32), Error> {
177 build_proof(resolver, domain, A::TYPE)
180 /// Builds a DNSSEC proof for an AAAA record by querying a recursive resolver, returning the proof
181 /// as well as the TTL for the proof provided by the recursive resolver.
183 /// Note that this proof is NOT verified in any way, you need to use the [`crate::validation`]
184 /// module to validate the records contained.
185 pub fn build_aaaa_proof(resolver: SocketAddr, domain: &Name) -> Result<(Vec<u8>, u32), Error> {
186 build_proof(resolver, domain, AAAA::TYPE)
189 /// Builds a DNSSEC proof for an TXT record by querying a recursive resolver, returning the proof
190 /// as well as the TTL for the proof provided by the recursive resolver.
192 /// Note that this proof is NOT verified in any way, you need to use the [`crate::validation`]
193 /// module to validate the records contained.
194 pub fn build_txt_proof(resolver: SocketAddr, domain: &Name) -> Result<(Vec<u8>, u32), Error> {
195 build_proof(resolver, domain, Txt::TYPE)
198 /// Builds a DNSSEC proof for an TLSA record by querying a recursive resolver, returning the proof
199 /// as well as the TTL for the proof provided by the recursive resolver.
201 /// Note that this proof is NOT verified in any way, you need to use the [`crate::validation`]
202 /// module to validate the records contained.
203 pub fn build_tlsa_proof(resolver: SocketAddr, domain: &Name) -> Result<(Vec<u8>, u32), Error> {
204 build_proof(resolver, domain, TLSA::TYPE)
208 /// Builds a DNSSEC proof for an A record by querying a recursive resolver, returning the proof as
209 /// well as the TTL for the proof provided by the recursive resolver.
211 /// Note that this proof is NOT verified in any way, you need to use the [`crate::validation`]
212 /// module to validate the records contained.
213 #[cfg(feature = "tokio")]
214 pub async fn build_a_proof_async(resolver: SocketAddr, domain: &Name) -> Result<(Vec<u8>, u32), Error> {
215 build_proof_async(resolver, domain, A::TYPE).await
218 /// Builds a DNSSEC proof for an AAAA record by querying a recursive resolver, returning the proof
219 /// as well as the TTL for the proof provided by the recursive resolver.
221 /// Note that this proof is NOT verified in any way, you need to use the [`crate::validation`]
222 /// module to validate the records contained.
223 #[cfg(feature = "tokio")]
224 pub async fn build_aaaa_proof_async(resolver: SocketAddr, domain: &Name) -> Result<(Vec<u8>, u32), Error> {
225 build_proof_async(resolver, domain, AAAA::TYPE).await
228 /// Builds a DNSSEC proof for an TXT record by querying a recursive resolver, returning the proof
229 /// as well as the TTL for the proof provided by the recursive resolver.
231 /// Note that this proof is NOT verified in any way, you need to use the [`crate::validation`]
232 /// module to validate the records contained.
233 #[cfg(feature = "tokio")]
234 pub async fn build_txt_proof_async(resolver: SocketAddr, domain: &Name) -> Result<(Vec<u8>, u32), Error> {
235 build_proof_async(resolver, domain, Txt::TYPE).await
238 /// Builds a DNSSEC proof for an TLSA record by querying a recursive resolver, returning the proof
239 /// as well as the TTL for the proof provided by the recursive resolver.
241 /// Note that this proof is NOT verified in any way, you need to use the [`crate::validation`]
242 /// module to validate the records contained.
243 #[cfg(feature = "tokio")]
244 pub async fn build_tlsa_proof_async(resolver: SocketAddr, domain: &Name) -> Result<(Vec<u8>, u32), Error> {
245 build_proof_async(resolver, domain, TLSA::TYPE).await
248 #[cfg(all(feature = "validation", test))]
251 use crate::validation::*;
253 use rand::seq::SliceRandom;
255 use std::net::ToSocketAddrs;
256 use std::time::SystemTime;
260 fn test_cloudflare_txt_query() {
261 let sockaddr = "8.8.8.8:53".to_socket_addrs().unwrap().next().unwrap();
262 let query_name = "cloudflare.com.".try_into().unwrap();
263 let (proof, _) = build_txt_proof(sockaddr, &query_name).unwrap();
265 let mut rrs = parse_rr_stream(&proof).unwrap();
266 rrs.shuffle(&mut rand::rngs::OsRng);
267 let verified_rrs = verify_rr_stream(&rrs).unwrap();
268 assert!(verified_rrs.verified_rrs.len() > 1);
270 let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs();
271 assert!(verified_rrs.valid_from < now);
272 assert!(verified_rrs.expires > now);
276 fn test_sha1_query() {
277 let sockaddr = "8.8.8.8:53".to_socket_addrs().unwrap().next().unwrap();
278 let query_name = "benthecarman.com.".try_into().unwrap();
279 let (proof, _) = build_a_proof(sockaddr, &query_name).unwrap();
281 let mut rrs = parse_rr_stream(&proof).unwrap();
282 rrs.shuffle(&mut rand::rngs::OsRng);
283 let verified_rrs = verify_rr_stream(&rrs).unwrap();
284 assert!(verified_rrs.verified_rrs.len() >= 1);
286 let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs();
287 assert!(verified_rrs.valid_from < now);
288 assert!(verified_rrs.expires > now);
292 fn test_txt_query() {
293 let sockaddr = "8.8.8.8:53".to_socket_addrs().unwrap().next().unwrap();
294 let query_name = "matt.user._bitcoin-payment.mattcorallo.com.".try_into().unwrap();
295 let (proof, _) = build_txt_proof(sockaddr, &query_name).unwrap();
297 let mut rrs = parse_rr_stream(&proof).unwrap();
298 rrs.shuffle(&mut rand::rngs::OsRng);
299 let verified_rrs = verify_rr_stream(&rrs).unwrap();
300 assert_eq!(verified_rrs.verified_rrs.len(), 1);
302 let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs();
303 assert!(verified_rrs.valid_from < now);
304 assert!(verified_rrs.expires > now);
308 fn test_cname_query() {
309 for resolver in ["1.1.1.1:53", "8.8.8.8:53", "9.9.9.9:53"] {
310 let sockaddr = resolver.to_socket_addrs().unwrap().next().unwrap();
311 let query_name = "cname_test.matcorallo.com.".try_into().unwrap();
312 let (proof, _) = build_txt_proof(sockaddr, &query_name).unwrap();
314 let mut rrs = parse_rr_stream(&proof).unwrap();
315 rrs.shuffle(&mut rand::rngs::OsRng);
316 let verified_rrs = verify_rr_stream(&rrs).unwrap();
317 assert_eq!(verified_rrs.verified_rrs.len(), 2);
319 let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs();
320 assert!(verified_rrs.valid_from < now);
321 assert!(verified_rrs.expires > now);
323 let resolved_rrs = verified_rrs.resolve_name(&query_name);
324 assert_eq!(resolved_rrs.len(), 1);
325 if let RR::Txt(txt) = &resolved_rrs[0] {
326 assert_eq!(txt.name.as_str(), "txt_test.matcorallo.com.");
327 assert_eq!(txt.data, b"dnssec_prover_test");
332 #[cfg(feature = "tokio")]
333 use tokio_crate as tokio;
335 #[cfg(feature = "tokio")]
337 async fn test_txt_query_async() {
338 let sockaddr = "8.8.8.8:53".to_socket_addrs().unwrap().next().unwrap();
339 let query_name = "matt.user._bitcoin-payment.mattcorallo.com.".try_into().unwrap();
340 let (proof, _) = build_txt_proof_async(sockaddr, &query_name).await.unwrap();
342 let mut rrs = parse_rr_stream(&proof).unwrap();
343 rrs.shuffle(&mut rand::rngs::OsRng);
344 let verified_rrs = verify_rr_stream(&rrs).unwrap();
345 assert_eq!(verified_rrs.verified_rrs.len(), 1);
347 let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs();
348 assert!(verified_rrs.valid_from < now);
349 assert!(verified_rrs.expires > now);
352 #[cfg(feature = "tokio")]
354 async fn test_cross_domain_cname_query_async() {
355 for resolver in ["1.1.1.1:53", "8.8.8.8:53", "9.9.9.9:53"] {
356 let sockaddr = resolver.to_socket_addrs().unwrap().next().unwrap();
357 let query_name = "wildcard.x_domain_cname_wild.matcorallo.com.".try_into().unwrap();
358 let (proof, _) = build_txt_proof_async(sockaddr, &query_name).await.unwrap();
360 let mut rrs = parse_rr_stream(&proof).unwrap();
361 rrs.shuffle(&mut rand::rngs::OsRng);
362 let verified_rrs = verify_rr_stream(&rrs).unwrap();
363 assert_eq!(verified_rrs.verified_rrs.len(), 2);
365 let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs();
366 assert!(verified_rrs.valid_from < now);
367 assert!(verified_rrs.expires > now);
369 let resolved_rrs = verified_rrs.resolve_name(&query_name);
370 assert_eq!(resolved_rrs.len(), 1);
371 if let RR::Txt(txt) = &resolved_rrs[0] {
372 assert_eq!(txt.name.as_str(), "matt.user._bitcoin-payment.mattcorallo.com.");
373 assert!(txt.data.starts_with(b"bitcoin:"));