//! //! Structs and information to create/parse the _fenrir DNSSEC record //! //! Encoding and decoding in base85, RFC1924 //! //! //! Basic encoding idea: //! * 1 byte: half-bytes //! * half: num of addresses //! * half: num of pubkeys //! [ # list of addresses //! * 1 byte: bitfield //! * 0..1 ipv4/ipv6 //! * 2..4 priority (for failover) //! * 5..7 weight between priority //! * 1 byte: public key id //! * 2 bytes: UDP port //! * X bytes: IP //! ] //! [ # list of pubkeys //! * 1 byte: pubkey type //! * 1 byte: pubkey id //! * Y bytes: pubkey //! ] use ::core::num::NonZeroU16; use ::num_traits::FromPrimitive; use ::std::{net::IpAddr, vec::Vec}; /* * Public key data */ /// Public Key ID #[derive(Debug, Copy, Clone)] pub struct PublicKeyId(u8); /// Public Key Type #[derive(::num_derive::FromPrimitive, Debug, Copy, Clone)] // public enum: use non_exhaustive to force users to add a default case // so in the future we can expand this easily #[non_exhaustive] #[repr(u8)] pub enum PublicKeyType { /// ed25519 asymmetric key Ed25519 = 0, } impl PublicKeyType { /// Get the size of a public key of this kind pub fn key_len(&self) -> usize { match &self { PublicKeyType::Ed25519 => 42, } } } /// Public Key, with its type and id #[derive(Debug, Clone)] pub struct PublicKey { /// public key raw data raw: Vec, /// type of public key kind: PublicKeyType, /// id of public key id: PublicKeyId, } impl PublicKey { fn raw_len(&self) -> usize { let size = 2; // Public Key Type + ID size + self.raw.len() } fn encode_into(&self, raw: &mut Vec) { raw.push(self.kind as u8); raw.push(self.id.0); raw.extend_from_slice(&self.raw); } fn decode_raw(raw: &[u8]) -> Result<(Self, usize), Error> { if raw.len() < 4 { return Err(Error::NotEnoughData(0)); } let kind = PublicKeyType::from_u8(raw[0]).unwrap(); let id = PublicKeyId(raw[1]); if raw.len() < 2 + kind.key_len() { return Err(Error::NotEnoughData(2)); } let mut raw_key = Vec::with_capacity(kind.key_len()); raw_key.extend_from_slice(&raw[2..(2 + kind.key_len())]); let total_length = 2 + kind.key_len(); Ok(( Self { raw: raw_key, kind, id, }, total_length, )) } } /* * Address data */ /// Priority of each group of addresses #[derive(::num_derive::FromPrimitive, Debug, Copy, Clone)] // public enum: use non_exhaustive to force users to add a default case // so in the future we can expand this easily #[non_exhaustive] #[repr(u8)] pub enum AddressPriority { /// Initially contact addresses in this priority P0 = 0, /// First failover P1, /// Second failover P2, /// Third failover P3, /// Fourth failover P4, /// Fifth failover P5, /// Sisth failover P6, /// Seventh failover P7, } /// Inside of each group, weight of the address /// This helps in distributing the traffic to multiple authentication servers: /// * client sums all weights in a group /// * generate a random number [0..sum_of_weights] /// * the number indicates which server will take the connection /// So to equally distribute all connections you just have to use the same /// weight in the same group #[derive(::num_derive::FromPrimitive, Debug, Copy, Clone)] // public enum: use non_exhaustive to force users to add a default case // so in the future we can expand this easily #[non_exhaustive] #[repr(u8)] pub enum AddressWeight { /// Minimum weigth: 1 W1 = 0, /// little weigth: 2 W2, /// little weigth: 3 W3, /// medium weigth: 4 W4, /// medium weigth: 5 W5, /// heavy weigth: 6 W6, /// heavy weigth: 7 W7, /// Maximum weigth: 8 W8, } /// Authentication server address information: /// * ip /// * udp port /// * priority /// * weight within priority #[derive(Debug, Clone, Copy)] pub struct Address { /// Ip address of server, v4 or v6 pub ip: IpAddr, /// udp port. None means that this address is reachable only /// with Fenrir over IP pub port: Option, /// Priority group of this address pub priority: AddressPriority, /// Weight of this address in the priority group pub weight: AddressWeight, /// Public key ID used by this address pub public_key_id: PublicKeyId, } impl Address { fn raw_len(&self) -> usize { let size = 4; // UDP port + Priority + Weight match self.ip { IpAddr::V4(_) => size + 4, IpAddr::V6(_) => size + 16, } } fn encode_into(&self, raw: &mut Vec) { let mut bitfield: u8 = match self.ip { IpAddr::V4(_) => 0, IpAddr::V6(_) => 1, }; bitfield <<= 3; bitfield |= self.priority as u8; bitfield <<= 3; bitfield |= self.weight as u8; raw.push(bitfield); raw.push(self.public_key_id.0); raw.extend_from_slice( &(match self.port { Some(port) => port.get().to_le_bytes(), None => [0, 0], // oh noez, which zero goes first? }), ); match self.ip { IpAddr::V4(ip) => { let raw_ip = ip.octets(); raw.extend_from_slice(&raw_ip); } IpAddr::V6(ip) => { let raw_ip = ip.octets(); raw.extend_from_slice(&raw_ip); } }; } fn decode_raw(raw: &[u8]) -> Result<(Self, usize), Error> { if raw.len() < 8 { return Err(Error::NotEnoughData(0)); } let ip_type = raw[0] >> 6; let is_ipv6: bool; let total_length: usize; match ip_type { 0 => { is_ipv6 = false; total_length = 8; } 1 => { total_length = 20; if raw.len() < total_length { return Err(Error::NotEnoughData(1)); } is_ipv6 = true } _ => return Err(Error::UnsupportedData(0)), } let raw_priority = (raw[0] << 2) >> 5; let raw_weight = (raw[0] << 5) >> 5; let priority = AddressPriority::from_u8(raw_priority).unwrap(); let weight = AddressWeight::from_u8(raw_weight).unwrap(); let public_key_id = PublicKeyId(raw[1]); let raw_port = u16::from_le_bytes(raw[2..3].try_into().unwrap()); let port = if raw_port == 0 { None } else { Some(NonZeroU16::new(raw_port).unwrap()) }; let ip = if is_ipv6 { let raw_ip: [u8; 16] = raw[4..20].try_into().unwrap(); IpAddr::from(raw_ip) } else { let raw_ip: [u8; 4] = raw[4..8].try_into().unwrap(); IpAddr::from(raw_ip) }; Ok(( Self { ip, port, priority, weight, public_key_id, }, total_length, )) } } /* * Actual record puuting it all toghether */ /// All informations found in the DNSSEC record #[derive(Debug, Clone)] pub struct Record { /// Public keys used by any authentication server public_keys: Vec, /// List of all authentication servers' addresses. /// Multiple ones can point to the same authentication server addresses: Vec
, } impl Record { /// Simply encode all the record in base85 pub fn encode(&self) -> Result { // check possible failure scenarios if self.public_keys.len() == 0 { return Err(Error::NoPublicKeyFound); } if self.public_keys.len() > 16 { return Err(Error::Max16PublicKeys); } if self.addresses.len() == 0 { return Err(Error::NoAddressFound); } if self.addresses.len() > 16 { return Err(Error::Max16Addresses); } // everything else is all good let total_size: usize = 1 + self.addresses.iter().map(|a| a.raw_len()).sum::() + self.public_keys.iter().map(|a| a.raw_len()).sum::(); let mut raw = Vec::with_capacity(total_size); // amount of data. addresses, then pubkeys. 4 bits each let len_combined: u8 = self.addresses.len() as u8; let len_combined = len_combined << 4; let len_combined = len_combined | self.public_keys.len() as u8; raw.push(len_combined); for address in self.addresses.iter() { address.encode_into(&mut raw); } for public_key in self.public_keys.iter() { public_key.encode_into(&mut raw); } Ok(::base85::encode(&raw)) } /// Decode from base85 to the actual object pub fn decode(raw: &[u8]) -> Result { // bare minimum for 1 address and key const MIN_RAW_LENGTH: usize = 1 + 8 + 8; if raw.len() <= MIN_RAW_LENGTH { return Err(Error::NotEnoughData(0)); } let mut num_addresses = (raw[0] >> 4) as usize; let mut num_public_keys = (raw[0] & 0x0F) as usize; let mut bytes_parsed = 1; let mut result = Self { addresses: Vec::with_capacity(num_addresses), public_keys: Vec::with_capacity(num_public_keys), }; while num_addresses > 0 { let (address, bytes) = match Address::decode_raw(&raw[bytes_parsed..]) { Ok(address) => address, Err(Error::UnsupportedData(b)) => { return Err(Error::UnsupportedData(bytes_parsed + b)) } Err(Error::NotEnoughData(b)) => { return Err(Error::NotEnoughData(bytes_parsed + b)) } Err(e) => return Err(e), }; bytes_parsed = bytes_parsed + bytes; result.addresses.push(address); num_addresses = num_addresses - 1; } while num_public_keys > 0 { let (public_key, bytes) = match PublicKey::decode_raw(&raw[bytes_parsed..]) { Ok(public_key) => public_key, Err(Error::UnsupportedData(b)) => { return Err(Error::UnsupportedData(bytes_parsed + b)) } Err(Error::NotEnoughData(b)) => { return Err(Error::NotEnoughData(bytes_parsed + b)) } Err(e) => return Err(e), }; bytes_parsed = bytes_parsed + bytes; result.public_keys.push(public_key); num_public_keys = num_public_keys - 1; } if bytes_parsed != raw.len() { Err(Error::UnknownData(bytes_parsed)) } else { Ok(result) } } } /// Possible errors in encoding or decoding the DNSSEC record #[derive(::thiserror::Error, Debug)] pub enum Error { /// General IO error #[error("IO error: {0:?}")] IO(#[from] ::std::io::Error), /// We need at least one address #[error("no addresses found")] NoAddressFound, /// Too many addresses (max 16) #[error("can't encode more than 16 addresses")] Max16Addresses, /// We need at least one public key #[error("no public keys found")] NoPublicKeyFound, /// Maximum 16 public keys supported #[error("can't encode more than 16 public keys")] Max16PublicKeys, /// Not enough data to decode something meaningful #[error("not enough data. Parsed {0} bytes")] NotEnoughData(usize), /// Unsupported Data: can't parse #[error("Unsupported data. Parsed {0} bytes")] UnsupportedData(usize), /// Unknown data at the end #[error("Unknown data after {0} bytes")] UnknownData(usize), }