1 //! This module exposes utilities for building DNSSEC proofs by directly querying a recursive
4 use std::net::{SocketAddr, TcpStream};
5 use std::io::{Read, Write, Error, ErrorKind};
7 #[cfg(feature = "tokio")]
8 use tokio_crate::net::TcpStream as TokioTcpStream;
9 #[cfg(feature = "tokio")]
10 use tokio_crate::io::{AsyncReadExt, AsyncWriteExt};
15 // We don't care about transaction IDs as we're only going to accept signed data. Thus, we use
16 // this constant instead of a random value.
17 const TXID: u16 = 0x4242;
19 fn emap<V>(v: Result<V, ()>) -> Result<V, Error> {
20 v.map_err(|_| Error::new(ErrorKind::Other, "Bad Response"))
23 fn build_query(domain: Name, ty: u16) -> Vec<u8> {
24 // TODO: Move to not allocating for the query
25 let mut query = Vec::with_capacity(1024);
26 let query_msg_len: u16 = 2 + 2 + 8 + 2 + 2 + name_len(&domain) + 11;
27 query.extend_from_slice(&query_msg_len.to_be_bytes());
28 query.extend_from_slice(&TXID.to_be_bytes());
29 query.extend_from_slice(&[0x01, 0x20]); // Flags: Recursive, Authenticated Data
30 query.extend_from_slice(&[0, 1, 0, 0, 0, 0, 0, 1]); // One question, One additional
31 write_name(&mut query, &domain);
32 query.extend_from_slice(&ty.to_be_bytes());
33 query.extend_from_slice(&1u16.to_be_bytes()); // INternet class
34 query.extend_from_slice(&[0, 0, 0x29]); // . OPT
35 query.extend_from_slice(&0u16.to_be_bytes()); // 0 UDP payload size
36 query.extend_from_slice(&[0, 0]); // EDNS version 0
37 query.extend_from_slice(&0x8000u16.to_be_bytes()); // Accept DNSSEC RRs
38 query.extend_from_slice(&0u16.to_be_bytes()); // No additional data
42 fn send_query(stream: &mut TcpStream, domain: Name, ty: u16) -> Result<(), Error> {
43 let query = build_query(domain, ty);
44 stream.write_all(&query)?;
48 #[cfg(feature = "tokio")]
49 async fn send_query_async(stream: &mut TokioTcpStream, domain: Name, ty: u16) -> Result<(), Error> {
50 let query = build_query(domain, ty);
51 stream.write_all(&query).await?;
55 fn handle_response(resp: &[u8], proof: &mut Vec<u8>) -> Result<Option<RRSig>, Error> {
56 let mut read: &[u8] = resp;
57 if emap(read_u16(&mut read))? != TXID { return Err(Error::new(ErrorKind::Other, "bad txid")); }
58 // 2 byte transaction ID
59 let flags = emap(read_u16(&mut read))?;
60 if flags & 0b1000_0000_0000_0000 == 0 {
61 return Err(Error::new(ErrorKind::Other, "Missing response flag"));
63 if flags & 0b0111_1010_0000_0111 != 0 {
64 return Err(Error::new(ErrorKind::Other, "Server indicated error or provided bunk flags"));
66 if flags & 0b10_0000 == 0 {
67 return Err(Error::new(ErrorKind::Other, "Server indicated data could not be authenticated"));
69 let questions = emap(read_u16(&mut read))?;
70 if questions != 1 { return Err(Error::new(ErrorKind::Other, "server responded to multiple Qs")); }
71 let answers = emap(read_u16(&mut read))?;
72 let _authorities = emap(read_u16(&mut read))?;
73 let _additional = emap(read_u16(&mut read))?;
75 for _ in 0..questions {
76 emap(read_wire_packet_name(&mut read, resp))?;
77 emap(read_u16(&mut read))?; // type
78 emap(read_u16(&mut read))?; // class
81 // Only read the answers (skip authorities and additional) as that's all we care about.
82 let mut rrsig_opt = None;
84 let (rr, ttl) = emap(parse_wire_packet_rr(&mut read, &resp))?;
85 write_rr(&rr, ttl, proof);
86 if let RR::RRSig(rrsig) = rr { rrsig_opt = Some(rrsig); }
91 fn read_response(stream: &mut TcpStream, proof: &mut Vec<u8>) -> Result<Option<RRSig>, Error> {
93 stream.read_exact(&mut len)?;
94 let mut resp = vec![0; u16::from_be_bytes(len) as usize];
95 stream.read_exact(&mut resp)?;
96 handle_response(&resp, proof)
99 #[cfg(feature = "tokio")]
100 async fn read_response_async(stream: &mut TokioTcpStream, proof: &mut Vec<u8>) -> Result<Option<RRSig>, Error> {
101 let mut len = [0; 2];
102 stream.read_exact(&mut len).await?;
103 let mut resp = vec![0; u16::from_be_bytes(len) as usize];
104 stream.read_exact(&mut resp).await?;
105 handle_response(&resp, proof)
108 macro_rules! build_proof_impl {
109 ($stream: ident, $send_query: ident, $read_response: ident $(, $async_ok: tt)?) => { {
110 let mut res = Vec::new();
111 let mut reached_root = false;
113 let rrsig_opt = $read_response(&mut $stream, &mut res)
114 $(.await?; $async_ok)??; // Either await?; Ok(())?, or just ?
115 if let Some(rrsig) = rrsig_opt {
116 if rrsig.name.as_str() == "." {
119 if i != 0 && rrsig.name == rrsig.key_name {
120 $send_query(&mut $stream, rrsig.key_name, DS::TYPE)
122 $send_query(&mut $stream, rrsig.key_name, DnsKey::TYPE)
123 }$(.await?; $async_ok)??; // Either await?; Ok(())?, or just ?
126 if reached_root { break; }
129 if !reached_root { Err(Error::new(ErrorKind::Other, "Too many requests required")) }
134 fn build_proof(resolver: SocketAddr, domain: Name, ty: u16) -> Result<Vec<u8>, Error> {
135 let mut stream = TcpStream::connect(resolver)?;
136 send_query(&mut stream, domain, ty)?;
137 build_proof_impl!(stream, send_query, read_response)
140 #[cfg(feature = "tokio")]
141 async fn build_proof_async(resolver: SocketAddr, domain: Name, ty: u16) -> Result<Vec<u8>, Error> {
142 let mut stream = TokioTcpStream::connect(resolver).await?;
143 send_query_async(&mut stream, domain, ty).await?;
144 build_proof_impl!(stream, send_query_async, read_response_async, { Ok::<(), Error>(()) })
147 /// Builds a DNSSEC proof for an A record by querying a recursive resolver
148 pub fn build_a_proof(resolver: SocketAddr, domain: Name) -> Result<Vec<u8>, Error> {
149 build_proof(resolver, domain, A::TYPE)
152 /// Builds a DNSSEC proof for an AAAA record by querying a recursive resolver
153 pub fn build_aaaa_proof(resolver: SocketAddr, domain: Name) -> Result<Vec<u8>, Error> {
154 build_proof(resolver, domain, AAAA::TYPE)
157 /// Builds a DNSSEC proof for a TXT record by querying a recursive resolver
158 pub fn build_txt_proof(resolver: SocketAddr, domain: Name) -> Result<Vec<u8>, Error> {
159 build_proof(resolver, domain, Txt::TYPE)
162 /// Builds a DNSSEC proof for a TLSA record by querying a recursive resolver
163 pub fn build_tlsa_proof(resolver: SocketAddr, domain: Name) -> Result<Vec<u8>, Error> {
164 build_proof(resolver, domain, TLSA::TYPE)
168 /// Builds a DNSSEC proof for an A record by querying a recursive resolver
169 #[cfg(feature = "tokio")]
170 pub async fn build_a_proof_async(resolver: SocketAddr, domain: Name) -> Result<Vec<u8>, Error> {
171 build_proof_async(resolver, domain, A::TYPE).await
174 /// Builds a DNSSEC proof for an AAAA record by querying a recursive resolver
175 #[cfg(feature = "tokio")]
176 pub async fn build_aaaa_proof_async(resolver: SocketAddr, domain: Name) -> Result<Vec<u8>, Error> {
177 build_proof_async(resolver, domain, AAAA::TYPE).await
180 /// Builds a DNSSEC proof for a TXT record by querying a recursive resolver
181 #[cfg(feature = "tokio")]
182 pub async fn build_txt_proof_async(resolver: SocketAddr, domain: Name) -> Result<Vec<u8>, Error> {
183 build_proof_async(resolver, domain, Txt::TYPE).await
186 /// Builds a DNSSEC proof for a TLSA record by querying a recursive resolver
187 #[cfg(feature = "tokio")]
188 pub async fn build_tlsa_proof_async(resolver: SocketAddr, domain: Name) -> Result<Vec<u8>, Error> {
189 build_proof_async(resolver, domain, TLSA::TYPE).await
192 #[cfg(all(feature = "validation", test))]
195 use crate::validation::*;
197 use rand::seq::SliceRandom;
199 use std::net::ToSocketAddrs;
200 use std::time::SystemTime;
204 fn test_cloudflare_txt_query() {
205 let sockaddr = "8.8.8.8:53".to_socket_addrs().unwrap().next().unwrap();
206 let query_name = "cloudflare.com.".try_into().unwrap();
207 let proof = build_txt_proof(sockaddr, query_name).unwrap();
209 let mut rrs = parse_rr_stream(&proof).unwrap();
210 rrs.shuffle(&mut rand::rngs::OsRng);
211 let verified_rrs = verify_rr_stream(&rrs).unwrap();
212 assert!(verified_rrs.verified_rrs.len() > 1);
214 let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs();
215 assert!(verified_rrs.valid_from < now);
216 assert!(verified_rrs.expires > now);
220 fn test_txt_query() {
221 let sockaddr = "8.8.8.8:53".to_socket_addrs().unwrap().next().unwrap();
222 let query_name = "matt.user._bitcoin-payment.mattcorallo.com.".try_into().unwrap();
223 let proof = build_txt_proof(sockaddr, query_name).unwrap();
225 let mut rrs = parse_rr_stream(&proof).unwrap();
226 rrs.shuffle(&mut rand::rngs::OsRng);
227 let verified_rrs = verify_rr_stream(&rrs).unwrap();
228 assert_eq!(verified_rrs.verified_rrs.len(), 1);
230 let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs();
231 assert!(verified_rrs.valid_from < now);
232 assert!(verified_rrs.expires > now);
235 #[cfg(feature = "tokio")]
236 use tokio_crate as tokio;
238 #[cfg(feature = "tokio")]
240 async fn test_txt_query_async() {
241 let sockaddr = "8.8.8.8:53".to_socket_addrs().unwrap().next().unwrap();
242 let query_name = "matt.user._bitcoin-payment.mattcorallo.com.".try_into().unwrap();
243 let proof = build_txt_proof_async(sockaddr, query_name).await.unwrap();
245 let mut rrs = parse_rr_stream(&proof).unwrap();
246 rrs.shuffle(&mut rand::rngs::OsRng);
247 let verified_rrs = verify_rr_stream(&rrs).unwrap();
248 assert_eq!(verified_rrs.verified_rrs.len(), 1);
250 let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs();
251 assert!(verified_rrs.valid_from < now);
252 assert!(verified_rrs.expires > now);