Browse Source

Support SSHFP records

Benjamin Sago 4 years ago
parent
commit
e7a7b77ce4
7 changed files with 133 additions and 1 deletions
  1. 6 0
      dns/src/record/mod.rs
  2. 0 1
      dns/src/record/others.rs
  3. 107 0
      dns/src/record/sshfp.rs
  4. 2 0
      dns/src/wire.rs
  5. 2 0
      src/colours.rs
  6. 15 0
      src/output.rs
  7. 1 0
      src/table.rs

+ 6 - 0
dns/src/record/mod.rs

@@ -28,6 +28,9 @@ pub use self::opt::OPT;
 mod ptr;
 pub use self::ptr::PTR;
 
+mod sshfp;
+pub use self::sshfp::SSHFP;
+
 mod soa;
 pub use self::soa::SOA;
 
@@ -72,6 +75,9 @@ pub enum Record {
     /// A **PTR** record.
     PTR(PTR),
 
+    /// A **SSHFP** record.
+    SSHFP(SSHFP),
+
     /// A **SOA** record.
     SOA(SOA),
 

+ 0 - 1
dns/src/record/others.rs

@@ -66,7 +66,6 @@ static TYPES: &[(&str, u16)] = &[
     ("RP",         17),
     ("SIG",        24),
     ("SMIMEA",     53),
-    ("SSHFP",      44),
     ("TA",      32768),
     ("TKEY",      249),
     ("TLSA",       52),

+ 107 - 0
dns/src/record/sshfp.rs

@@ -0,0 +1,107 @@
+use log::*;
+
+use crate::wire::*;
+
+
+/// A **SSHFP** _(secure shell fingerprint)_ record, which contains the
+/// fingerprint of an SSH public key.
+///
+/// # References
+///
+/// - [RFC 4255](https://tools.ietf.org/html/rfc4255) — Using DNS to Securely Publish Secure Shell (SSH) Key Fingerprints (January 2006)
+#[derive(PartialEq, Debug)]
+pub struct SSHFP {
+
+    /// The algorithm of the public key. This is a number with several defined
+    /// mappings.
+    pub algorithm: u8,
+
+    /// The type of the fingerprint, which specifies the hashing algorithm
+    /// used to derive the fingerprint. This is a number with several defined
+    /// mappings.
+    pub fingerprint_type: u8,
+
+    /// The fingerprint of the public key.
+    pub fingerprint: Vec<u8>,
+}
+
+impl Wire for SSHFP {
+    const NAME: &'static str = "SSHFP";
+    const RR_TYPE: u16 = 44;
+
+    #[cfg_attr(all(test, feature = "with_mutagen"), ::mutagen::mutate)]
+    fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireError> {
+        let algorithm = c.read_u8()?;
+        trace!("Parsed algorithm -> {:?}", algorithm);
+
+        let fingerprint_type = c.read_u8()?;
+        trace!("Parsed fingerprint type -> {:?}", fingerprint_type);
+
+        if stated_length <= 2 {
+            panic!("Length too short");
+        }
+
+        let fingerprint_length = stated_length - 1 - 1;
+        let mut fingerprint = Vec::new();
+        for _ in 0 .. fingerprint_length {
+            fingerprint.push(c.read_u8()?);
+        }
+
+        Ok(Self { algorithm, fingerprint_type, fingerprint })
+    }
+}
+
+impl SSHFP {
+
+    /// Returns the hexadecimal representation of the fingerprint.
+    pub fn hex_fingerprint(&self) -> String {
+        self.fingerprint.iter()
+            .map(|byte| format!("{:02x}", byte))
+            .collect()
+    }
+}
+
+
+#[cfg(test)]
+mod test {
+    use super::*;
+
+    #[test]
+    fn parses() {
+        let buf = &[
+            0x01,  // algorithm
+            0x01,  // fingerprint type
+            0x21, 0x22, 0x23, 0x24,  // an extremely short fingerprint
+        ];
+
+        assert_eq!(SSHFP::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(),
+                   SSHFP {
+                       algorithm: 1,
+                       fingerprint_type: 1,
+                       fingerprint: vec![ 0x21, 0x22, 0x23, 0x24 ],
+                   });
+    }
+
+    #[test]
+    fn buffer_ends_abruptly() {
+        let buf = &[
+            0x01,  // algorithm
+        ];
+
+        assert_eq!(SSHFP::read(6, &mut Cursor::new(buf)),
+                   Err(WireError::IO));
+    }
+
+    #[test]
+    fn hex_rep() {
+        let sshfp = SSHFP {
+            algorithm: 1,
+            fingerprint_type: 1,
+            fingerprint: vec![ 0xf3, 0x48, 0xcd, 0xc9 ],
+        };
+
+        assert_eq!(sshfp.hex_fingerprint(),
+                   String::from("f348cdc9"));
+    }
+
+}

+ 2 - 0
dns/src/wire.rs

@@ -180,6 +180,7 @@ impl Record {
         try_record!(NS);
         // OPT is handled separately
         try_record!(PTR);
+        try_record!(SSHFP);
         try_record!(SOA);
         try_record!(SRV);
         try_record!(TXT);
@@ -239,6 +240,7 @@ pub fn find_qtype_number(record_type: &str) -> Option<TypeInt> {
     try_record!(NS);
     // OPT is elsewhere
     try_record!(PTR);
+    try_record!(SSHFP);
     try_record!(SOA);
     try_record!(SRV);
     try_record!(TXT);

+ 2 - 0
src/colours.rs

@@ -22,6 +22,7 @@ pub struct Colours {
     pub ns: Style,
     pub opt: Style,
     pub ptr: Style,
+    pub sshfp: Style,
     pub soa: Style,
     pub srv: Style,
     pub txt: Style,
@@ -49,6 +50,7 @@ impl Colours {
             ns: Red.normal(),
             opt: Purple.normal(),
             ptr: Red.normal(),
+            sshfp: Cyan.normal(),
             soa: Purple.normal(),
             srv: Cyan.normal(),
             txt: Yellow.normal(),

+ 15 - 0
src/output.rs

@@ -206,6 +206,13 @@ impl TextFormat {
             Record::PTR(ref ptr) => {
                 format!("{:?}", ptr.cname.to_string())
             }
+            Record::SSHFP(ref sshfp) => {
+                format!("{} {} {}",
+                    sshfp.algorithm,
+                    sshfp.fingerprint_type,
+                    sshfp.hex_fingerprint(),
+                )
+            }
             Record::SOA(ref soa) => {
                 format!("{:?} {:?} {} {} {} {} {}",
                     soa.mname.to_string(),
@@ -374,6 +381,14 @@ fn json_record(record: &Record) -> JsonValue {
                 "cname": rec.cname.to_string(),
             })
         }
+        Record::SSHFP(rec) => {
+            json!({
+                "type": "SSHFP",
+                "algorithm": rec.algorithm,
+                "fingerprint_type": rec.fingerprint_type,
+                "fingerprint": rec.hex_fingerprint(),
+            })
+        }
         Record::SOA(rec) => {
             json!({
                 "type": "SOA",

+ 1 - 0
src/table.rs

@@ -122,6 +122,7 @@ impl Table {
             Record::MX(_)     => self.colours.mx.paint("MX"),
             Record::NS(_)     => self.colours.ns.paint("NS"),
             Record::PTR(_)    => self.colours.ptr.paint("PTR"),
+            Record::SSHFP(_)  => self.colours.sshfp.paint("SSHFP"),
             Record::SOA(_)    => self.colours.soa.paint("SOA"),
             Record::SRV(_)    => self.colours.srv.paint("SRV"),
             Record::TXT(_)    => self.colours.txt.paint("TXT"),