-/// A valid domain name.
-///
-/// It must end with a ".", be no longer than 255 bytes, consist of only printable ASCII
-/// characters and each label may be no longer than 63 bytes.
-#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
-pub struct Name(String);
-impl core::ops::Deref for Name {
- type Target = str;
- fn deref(&self) -> &str { &self.0 }
-}
-impl TryFrom<String> for Name {
- type Error = ();
- fn try_from(s: String) -> Result<Name, ()> {
- if s.is_empty() { return Err(()); }
- if *s.as_bytes().last().unwrap_or(&0) != b"."[0] { return Err(()); }
- if s.len() > 255 { return Err(()); }
- if s.chars().any(|c| !c.is_ascii_graphic() && c != '.' && c != '-') { return Err(()); }
- for label in s.split(".") {
- if label.len() > 63 { return Err(()); }
- }
-
- Ok(Name(s))
- }
-}
-impl TryFrom<&str> for Name {
- type Error = ();
- fn try_from(s: &str) -> Result<Name, ()> {
- Self::try_from(s.to_owned())
- }
-}
-
-#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
-/// A supported Resource Record
-///
-/// Note that we only currently support a handful of RR types as needed to generate and validate
-/// TXT or TLSA record proofs.
-pub enum RR {
- /// A text resource record
- Txt(Txt),
- /// A TLS Certificate Association resource record
- TLSA(TLSA),
- /// A Canonical Name record
- CName(CName),
- /// A DNS (Public) Key resource record
- DnsKey(DnsKey),
- /// A Delegated Signer resource record
- DS(DS),
- /// A Resource Record Signature record
- RRSig(RRSig),
-}
-impl RR {
- /// Gets the name this record refers to.
- pub fn name(&self) -> &Name {
- match self {
- RR::Txt(rr) => &rr.name,
- RR::CName(rr) => &rr.name,
- RR::TLSA(rr) => &rr.name,
- RR::DnsKey(rr) => &rr.name,
- RR::DS(rr) => &rr.name,
- RR::RRSig(rr) => &rr.name,
- }
- }
- fn ty(&self) -> u16 {
- match self {
- RR::Txt(_) => Txt::TYPE,
- RR::CName(_) => CName::TYPE,
- RR::TLSA(_) => TLSA::TYPE,
- RR::DnsKey(_) => DnsKey::TYPE,
- RR::DS(_) => DS::TYPE,
- RR::RRSig(_) => RRSig::TYPE,
- }
- }
- fn write_u16_len_prefixed_data(&self, out: &mut Vec<u8>) {
- match self {
- RR::Txt(rr) => StaticRecord::write_u16_len_prefixed_data(rr, out),
- RR::CName(rr) => StaticRecord::write_u16_len_prefixed_data(rr, out),
- RR::TLSA(rr) => StaticRecord::write_u16_len_prefixed_data(rr, out),
- RR::DnsKey(rr) => StaticRecord::write_u16_len_prefixed_data(rr, out),
- RR::DS(rr) => StaticRecord::write_u16_len_prefixed_data(rr, out),
- RR::RRSig(rr) => StaticRecord::write_u16_len_prefixed_data(rr, out),
- }
- }
-}
-impl From<Txt> for RR { fn from(txt: Txt) -> RR { RR::Txt(txt) } }
-impl From<CName> for RR { fn from(cname: CName) -> RR { RR::CName(cname) } }
-impl From<TLSA> for RR { fn from(tlsa: TLSA) -> RR { RR::TLSA(tlsa) } }
-impl From<DnsKey> for RR { fn from(dnskey: DnsKey) -> RR { RR::DnsKey(dnskey) } }
-impl From<DS> for RR { fn from(ds: DS) -> RR { RR::DS(ds) } }
-impl From<RRSig> for RR { fn from(rrsig: RRSig) -> RR { RR::RRSig(rrsig) } }
-
-trait StaticRecord : Ord {
- // http://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-4
- const TYPE: u16;
- fn name(&self) -> &Name;
- fn write_u16_len_prefixed_data(&self, out: &mut Vec<u8>);
-}
-/// A trait describing a resource record (including the [`RR`] enum).
-pub trait Record : Ord + {
- /// The resource record type, as maintained by IANA.
- ///
- /// Current assignments can be found at
- /// <http://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-4>
- fn ty(&self) -> u16;
- /// The name this record is at.
- fn name(&self) -> &Name;
- /// Writes the data of this record, prefixed by a u16 length, to the given `Vec`.
- fn write_u16_len_prefixed_data(&self, out: &mut Vec<u8>);
-}
-impl<RR: StaticRecord> Record for RR {
- fn ty(&self) -> u16 { RR::TYPE }
- fn name(&self) -> &Name { RR::name(self) }
- fn write_u16_len_prefixed_data(&self, out: &mut Vec<u8>) {
- RR::write_u16_len_prefixed_data(self, out)
- }
-}
-impl Record for RR {
- fn ty(&self) -> u16 { self.ty() }
- fn name(&self) -> &Name { self.name() }
- fn write_u16_len_prefixed_data(&self, out: &mut Vec<u8>) {
- self.write_u16_len_prefixed_data(out)
- }
-}
-
-fn read_u8(inp: &mut &[u8]) -> Result<u8, ()> {
- let res = *inp.get(0).ok_or(())?;
- *inp = &inp[1..];
- Ok(res)
-}
-fn read_u16(inp: &mut &[u8]) -> Result<u16, ()> {
- if inp.len() < 2 { return Err(()); }
- let mut bytes = [0; 2];
- bytes.copy_from_slice(&inp[..2]);
- *inp = &inp[2..];
- Ok(u16::from_be_bytes(bytes))
-}
-fn read_u32(inp: &mut &[u8]) -> Result<u32, ()> {
- if inp.len() < 4 { return Err(()); }
- let mut bytes = [0; 4];
- bytes.copy_from_slice(&inp[..4]);
- *inp = &inp[4..];
- Ok(u32::from_be_bytes(bytes))
-}
-
-fn read_name(inp: &mut &[u8]) -> Result<Name, ()> {
- let mut name = String::with_capacity(1024);
- loop {
- let len = read_u8(inp)? as usize;
- if len == 0 {
- if name.is_empty() { name += "."; }
- break;
- }
- if inp.len() <= len { return Err(()); }
- name += core::str::from_utf8(&inp[..len]).map_err(|_| ())?;
- name += ".";
- *inp = &inp[len..];
- if name.len() > 1024 { return Err(()); }
- }
- Ok(name.try_into()?)
-}
-
-trait Writer { fn write(&mut self, buf: &[u8]); }
-impl Writer for Vec<u8> { fn write(&mut self, buf: &[u8]) { self.extend_from_slice(buf); } }
-impl Writer for ring::digest::Context { fn write(&mut self, buf: &[u8]) { self.update(buf); } }
-fn write_name<W: Writer>(out: &mut W, name: &str) {
- let canonical_name = name.to_ascii_lowercase();
- if canonical_name == "." {
- out.write(&[0]);
- } else {
- for label in canonical_name.split(".") {
- out.write(&(label.len() as u8).to_be_bytes());
- out.write(label.as_bytes());
- }
- }
-}
-fn name_len(name: &Name) -> u16 {
- if name.0 == "." {
- 1
- } else {
- let mut res = 0;
- for label in name.split(".") {
- res += 1 + label.len();
- }
- res as u16
- }
-}
-
-fn parse_rr(inp: &mut &[u8]) -> Result<RR, ()> {
- let name = read_name(inp)?;
- let ty = read_u16(inp)?;
- let class = read_u16(inp)?;
- if class != 1 { return Err(()); } // We only support the INternet
- let _ttl = read_u32(inp)?;
- let data_len = read_u16(inp)? as usize;
- if inp.len() < data_len { return Err(()); }
- let mut data = &inp[..data_len];
- *inp = &inp[data_len..];
-
- match ty {
- Txt::TYPE => {
- let mut parsed_data = Vec::with_capacity(data_len - 1);
- while !data.is_empty() {
- let len = read_u8(&mut data)? as usize;
- if data.len() < len { return Err(()); }
- parsed_data.extend_from_slice(&data[..len]);
- data = &data[len..];
- }
- Ok(RR::Txt(Txt { name, data: parsed_data }))
- }
- CName::TYPE => {
- Ok(RR::CName(CName { name, canonical_name: read_name(&mut data)? }))
- }
- TLSA::TYPE => {
- if data_len <= 3 { return Err(()); }
- Ok(RR::TLSA(TLSA {
- name, cert_usage: read_u8(&mut data)?, selector: read_u8(&mut data)?,
- data_ty: read_u8(&mut data)?, data: data.to_vec(),
- }))
- },
- DnsKey::TYPE => {
- Ok(RR::DnsKey(DnsKey {
- name, flags: read_u16(&mut data)?, protocol: read_u8(&mut data)?,
- alg: read_u8(&mut data)?, pubkey: data.to_vec(),
- }))
- },
- DS::TYPE => {
- Ok(RR::DS(DS {
- name, key_tag: read_u16(&mut data)?, alg: read_u8(&mut data)?,
- digest_type: read_u8(&mut data)?, digest: data.to_vec(),
- }))
- },
- RRSig::TYPE => {
- Ok(RR::RRSig(RRSig {
- name, ty: read_u16(&mut data)?, alg: read_u8(&mut data)?,
- labels: read_u8(&mut data)?, orig_ttl: read_u32(&mut data)?,
- expiration: read_u32(&mut data)?, inception: read_u32(&mut data)?,
- key_tag: read_u16(&mut data)?, key_name: read_name(&mut data)?,
- signature: data.to_vec(),
- }))
- },
- _ => Err(()),
- }
-}