1 pub mod a; 2 pub mod aaaa; 3 pub mod cname; 4 pub mod mx; 5 pub mod ns; 6 pub mod opt; 7 pub mod ptr; 8 pub mod soa; 9 pub mod srv; 10 pub mod txt; 11 12 use super::name::*; 13 use super::packer::*; 14 use super::*; 15 use crate::error::*; 16 17 use a::*; 18 use aaaa::*; 19 use cname::*; 20 use mx::*; 21 use ns::*; 22 use opt::*; 23 use ptr::*; 24 use soa::*; 25 use srv::*; 26 use txt::*; 27 28 use std::collections::HashMap; 29 use std::fmt; 30 31 // EDNS(0) wire constants. 32 33 const EDNS0_VERSION: u32 = 0; 34 const EDNS0_DNSSEC_OK: u32 = 0x00008000; 35 const EDNS_VERSION_MASK: u32 = 0x00ff0000; 36 const EDNS0_DNSSEC_OK_MASK: u32 = 0x00ff8000; 37 38 // A Resource is a DNS resource record. 39 #[derive(Default, Debug)] 40 pub struct Resource { 41 pub header: ResourceHeader, 42 pub body: Option<Box<dyn ResourceBody>>, 43 } 44 45 impl fmt::Display for Resource { 46 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 47 write!( 48 f, 49 "dnsmessage.Resource{{Header: {}, Body: {}}}", 50 self.header, 51 if let Some(body) = &self.body { 52 body.to_string() 53 } else { 54 "None".to_owned() 55 } 56 ) 57 } 58 } 59 60 impl Resource { 61 // pack appends the wire format of the Resource to msg. 62 pub fn pack( 63 &mut self, 64 msg: Vec<u8>, 65 compression: &mut Option<HashMap<String, usize>>, 66 compression_off: usize, 67 ) -> Result<Vec<u8>> { 68 if let Some(body) = &self.body { 69 self.header.typ = body.real_type(); 70 } else { 71 return Err(Error::ErrNilResourceBody); 72 } 73 let (mut msg, len_off) = self.header.pack(msg, compression, compression_off)?; 74 let pre_len = msg.len(); 75 if let Some(body) = &self.body { 76 msg = body.pack(msg, compression, compression_off)?; 77 self.header.fix_len(&mut msg, len_off, pre_len)?; 78 } 79 Ok(msg) 80 } 81 82 pub fn unpack(&mut self, msg: &[u8], mut off: usize) -> Result<usize> { 83 off = self.header.unpack(msg, off, 0)?; 84 let (rb, off) = 85 unpack_resource_body(self.header.typ, msg, off, self.header.length as usize)?; 86 self.body = Some(rb); 87 Ok(off) 88 } 89 90 pub(crate) fn skip(msg: &[u8], off: usize) -> Result<usize> { 91 let mut new_off = Name::skip(msg, off)?; 92 new_off = DnsType::skip(msg, new_off)?; 93 new_off = DnsClass::skip(msg, new_off)?; 94 new_off = skip_uint32(msg, new_off)?; 95 let (length, mut new_off) = unpack_uint16(msg, new_off)?; 96 new_off += length as usize; 97 if new_off > msg.len() { 98 return Err(Error::ErrResourceLen); 99 } 100 Ok(new_off) 101 } 102 } 103 104 // A ResourceHeader is the header of a DNS resource record. There are 105 // many types of DNS resource records, but they all share the same header. 106 #[derive(Clone, Default, PartialEq, Eq, Debug)] 107 pub struct ResourceHeader { 108 // Name is the domain name for which this resource record pertains. 109 pub name: Name, 110 111 // Type is the type of DNS resource record. 112 // 113 // This field will be set automatically during packing. 114 pub typ: DnsType, 115 116 // Class is the class of network to which this DNS resource record 117 // pertains. 118 pub class: DnsClass, 119 120 // TTL is the length of time (measured in seconds) which this resource 121 // record is valid for (time to live). All Resources in a set should 122 // have the same TTL (RFC 2181 Section 5.2). 123 pub ttl: u32, 124 125 // Length is the length of data in the resource record after the header. 126 // 127 // This field will be set automatically during packing. 128 pub length: u16, 129 } 130 131 impl fmt::Display for ResourceHeader { 132 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 133 write!( 134 f, 135 "dnsmessage.ResourceHeader{{Name: {}, Type: {}, Class: {}, TTL: {}, Length: {}}}", 136 self.name, self.typ, self.class, self.ttl, self.length, 137 ) 138 } 139 } 140 141 impl ResourceHeader { 142 // pack appends the wire format of the ResourceHeader to oldMsg. 143 // 144 // lenOff is the offset in msg where the Length field was packed. 145 pub fn pack( 146 &self, 147 mut msg: Vec<u8>, 148 compression: &mut Option<HashMap<String, usize>>, 149 compression_off: usize, 150 ) -> Result<(Vec<u8>, usize)> { 151 msg = self.name.pack(msg, compression, compression_off)?; 152 msg = self.typ.pack(msg); 153 msg = self.class.pack(msg); 154 msg = pack_uint32(msg, self.ttl); 155 let len_off = msg.len(); 156 msg = pack_uint16(msg, self.length); 157 Ok((msg, len_off)) 158 } 159 160 pub fn unpack(&mut self, msg: &[u8], off: usize, _length: usize) -> Result<usize> { 161 let mut new_off = off; 162 new_off = self.name.unpack(msg, new_off)?; 163 new_off = self.typ.unpack(msg, new_off)?; 164 new_off = self.class.unpack(msg, new_off)?; 165 let (ttl, new_off) = unpack_uint32(msg, new_off)?; 166 self.ttl = ttl; 167 let (l, new_off) = unpack_uint16(msg, new_off)?; 168 self.length = l; 169 170 Ok(new_off) 171 } 172 173 // fixLen updates a packed ResourceHeader to include the length of the 174 // ResourceBody. 175 // 176 // lenOff is the offset of the ResourceHeader.Length field in msg. 177 // 178 // preLen is the length that msg was before the ResourceBody was packed. 179 pub fn fix_len(&mut self, msg: &mut [u8], len_off: usize, pre_len: usize) -> Result<()> { 180 if msg.len() < pre_len || msg.len() > pre_len + u16::MAX as usize { 181 return Err(Error::ErrResTooLong); 182 } 183 184 let con_len = msg.len() - pre_len; 185 186 // Fill in the length now that we know how long the content is. 187 msg[len_off] = ((con_len >> 8) & 0xFF) as u8; 188 msg[len_off + 1] = (con_len & 0xFF) as u8; 189 self.length = con_len as u16; 190 191 Ok(()) 192 } 193 194 // set_edns0 configures h for EDNS(0). 195 // 196 // The provided ext_rcode must be an extedned RCode. 197 pub fn set_edns0( 198 &mut self, 199 udp_payload_len: u16, 200 ext_rcode: u32, 201 dnssec_ok: bool, 202 ) -> Result<()> { 203 self.name = Name { 204 data: ".".to_owned(), 205 }; // RFC 6891 section 6.1.2 206 self.typ = DnsType::Opt; 207 self.class = DnsClass(udp_payload_len); 208 self.ttl = (ext_rcode >> 4) << 24; 209 if dnssec_ok { 210 self.ttl |= EDNS0_DNSSEC_OK; 211 } 212 Ok(()) 213 } 214 215 // dnssec_allowed reports whether the DNSSEC OK bit is set. 216 pub fn dnssec_allowed(&self) -> bool { 217 self.ttl & EDNS0_DNSSEC_OK_MASK == EDNS0_DNSSEC_OK // RFC 6891 section 6.1.3 218 } 219 220 // extended_rcode returns an extended RCode. 221 // 222 // The provided rcode must be the RCode in DNS message header. 223 pub fn extended_rcode(&self, rcode: RCode) -> RCode { 224 if self.ttl & EDNS_VERSION_MASK == EDNS0_VERSION { 225 // RFC 6891 section 6.1.3 226 let ttl = ((self.ttl >> 24) << 4) as u8 | rcode as u8; 227 return RCode::from(ttl); 228 } 229 rcode 230 } 231 } 232 233 // A ResourceBody is a DNS resource record minus the header. 234 pub trait ResourceBody: fmt::Display + fmt::Debug { 235 // real_type returns the actual type of the Resource. This is used to 236 // fill in the header Type field. 237 fn real_type(&self) -> DnsType; 238 239 // pack packs a Resource except for its header. 240 fn pack( 241 &self, 242 msg: Vec<u8>, 243 compression: &mut Option<HashMap<String, usize>>, 244 compression_off: usize, 245 ) -> Result<Vec<u8>>; 246 247 fn unpack(&mut self, msg: &[u8], off: usize, length: usize) -> Result<usize>; 248 } 249 250 pub fn unpack_resource_body( 251 typ: DnsType, 252 msg: &[u8], 253 mut off: usize, 254 length: usize, 255 ) -> Result<(Box<dyn ResourceBody>, usize)> { 256 let mut rb: Box<dyn ResourceBody> = match typ { 257 DnsType::A => Box::new(AResource::default()), 258 DnsType::Ns => Box::new(NsResource::default()), 259 DnsType::Cname => Box::new(CnameResource::default()), 260 DnsType::Soa => Box::new(SoaResource::default()), 261 DnsType::Ptr => Box::new(PtrResource::default()), 262 DnsType::Mx => Box::new(MxResource::default()), 263 DnsType::Txt => Box::new(TxtResource::default()), 264 DnsType::Aaaa => Box::new(AaaaResource::default()), 265 DnsType::Srv => Box::new(SrvResource::default()), 266 DnsType::Opt => Box::new(OptResource::default()), 267 _ => return Err(Error::ErrNilResourceBody), 268 }; 269 270 off = rb.unpack(msg, off, length)?; 271 272 Ok((rb, off)) 273 } 274