4
0
Benjamin Sago 4 жил өмнө
parent
commit
aeeaf2a339

+ 7 - 0
Cargo.lock

@@ -64,6 +64,12 @@ dependencies = [
  "rustc-demangle",
 ]
 
+[[package]]
+name = "base64"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
+
 [[package]]
 name = "bitflags"
 version = "1.2.1"
@@ -141,6 +147,7 @@ checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
 name = "dns"
 version = "0.1.0"
 dependencies = [
+ "base64",
  "byteorder",
  "log",
  "mutagen",

+ 3 - 0
dns/Cargo.toml

@@ -13,6 +13,9 @@ log = "0.4"
 # protocol parsing
 byteorder = "1.3"
 
+# packet printing
+base64 = "0.13"
+
 # testing
 mutagen = { git = "https://github.com/llogiq/mutagen", optional = true }
 

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

@@ -28,6 +28,9 @@ pub use self::naptr::NAPTR;
 mod ns;
 pub use self::ns::NS;
 
+mod openpgpkey;
+pub use self::openpgpkey::OPENPGPKEY;
+
 mod opt;
 pub use self::opt::OPT;
 
@@ -88,6 +91,9 @@ pub enum Record {
     /// A **NS** record.
     NS(NS),
 
+    /// An **OPENPGPKEY** record.
+    OPENPGPKEY(OPENPGPKEY),
+
     // OPT is not included here.
 
     /// A **PTR** record.

+ 87 - 0
dns/src/record/openpgpkey.rs

@@ -0,0 +1,87 @@
+use crate::wire::*;
+
+
+/// A **OPENPGPKEY** record, which holds a PGP key.
+///
+/// # References
+///
+/// - [RFC 1035 §3.3.14](https://tools.ietf.org/html/rfc7929) — DNS-Based
+///   Authentication of Named Entities Bindings for OpenPGP (August 2016)
+#[derive(PartialEq, Debug)]
+pub struct OPENPGPKEY {
+
+    /// The PGP key, as unencoded bytes.
+    pub key: Vec<u8>,
+}
+
+impl Wire for OPENPGPKEY {
+    const NAME: &'static str = "OPENPGPKEY";
+    const RR_TYPE: u16 = 61;
+
+    #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)]
+    fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireError> {
+        if stated_length == 0 {
+            let mandated_length = MandatedLength::AtLeast(1);
+            return Err(WireError::WrongRecordLength { stated_length, mandated_length });
+        }
+
+        let mut key = vec![0_u8; usize::from(stated_length)];
+        c.read_exact(&mut key)?;
+        Ok(Self { key })
+    }
+}
+
+impl OPENPGPKEY {
+
+    /// The base64-encoded PGP key.
+    pub fn base64_key(&self) -> String {
+        base64::encode(&self.key)
+    }
+}
+
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use pretty_assertions::assert_eq;
+
+    #[test]
+    fn parses() {
+        let buf = &[
+            0x12, 0x34, 0x56, 0x78,  // key
+        ];
+
+        assert_eq!(OPENPGPKEY::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(),
+                   OPENPGPKEY {
+                       key: vec![ 0x12, 0x34, 0x56, 0x78 ],
+                   });
+    }
+
+    #[test]
+    fn one_byte_of_uri() {
+        let buf = &[
+            0x2b,  // one byte of key
+        ];
+
+        assert_eq!(OPENPGPKEY::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(),
+                   OPENPGPKEY {
+                       key: vec![ 0x2b ],
+                   });
+    }
+
+    #[test]
+    fn record_empty() {
+        assert_eq!(OPENPGPKEY::read(0, &mut Cursor::new(&[])),
+                   Err(WireError::WrongRecordLength { stated_length: 0, mandated_length: MandatedLength::AtLeast(1) }));
+    }
+
+    #[test]
+    fn buffer_ends_abruptly() {
+        let buf = &[
+            0x12, 0x34,  // the beginning of a key
+        ];
+
+        assert_eq!(OPENPGPKEY::read(23, &mut Cursor::new(buf)),
+                   Err(WireError::IO));
+    }
+}

+ 3 - 1
dns/src/wire.rs

@@ -1,6 +1,6 @@
 //! Parsing the DNS wire protocol.
 
-pub(crate) use std::io::Cursor;
+pub(crate) use std::io::{Cursor, Read};
 pub(crate) use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
 
 use std::io;
@@ -184,6 +184,7 @@ impl Record {
         try_record!(MX);
         try_record!(NAPTR);
         try_record!(NS);
+        try_record!(OPENPGPKEY);
         // OPT is handled separately
         try_record!(PTR);
         try_record!(SSHFP);
@@ -248,6 +249,7 @@ pub fn find_qtype_number(record_type: &str) -> Option<TypeInt> {
     try_record!(MX);
     try_record!(NAPTR);
     try_record!(NS);
+    try_record!(OPENPGPKEY);
     // OPT is elsewhere
     try_record!(PTR);
     try_record!(SSHFP);

+ 2 - 0
src/colours.rs

@@ -22,6 +22,7 @@ pub struct Colours {
     pub mx: Style,
     pub ns: Style,
     pub naptr: Style,
+    pub openpgpkey: Style,
     pub opt: Style,
     pub ptr: Style,
     pub sshfp: Style,
@@ -54,6 +55,7 @@ impl Colours {
             mx: Cyan.normal(),
             naptr: Green.normal(),
             ns: Red.normal(),
+            openpgpkey: Cyan.normal(),
             opt: Purple.normal(),
             ptr: Red.normal(),
             sshfp: Cyan.normal(),

+ 9 - 0
src/output.rs

@@ -223,6 +223,9 @@ impl TextFormat {
             Record::NS(ref ns) => {
                 format!("{:?}", ns.nameserver.to_string())
             }
+            Record::OPENPGPKEY(ref opgp) => {
+                format!("{:?}", opgp.base64_key())
+            }
             Record::PTR(ref ptr) => {
                 format!("{:?}", ptr.cname.to_string())
             }
@@ -432,6 +435,12 @@ fn json_record(record: &Record) -> JsonValue {
                 "nameserver": rec.nameserver.to_string(),
             })
         }
+        Record::OPENPGPKEY(rec) => {
+            json!({
+                "type": "OPENPGPKEY",
+                "key": rec.base64_key(),
+            })
+        }
         Record::PTR(rec) => {
             json!({
                 "type": "PTR",

+ 17 - 16
src/table.rs

@@ -114,22 +114,23 @@ impl Table {
 
     fn coloured_record_type(&self, record: &Record) -> ANSIString<'static> {
         match *record {
-            Record::A(_)      => self.colours.a.paint("A"),
-            Record::AAAA(_)   => self.colours.aaaa.paint("AAAA"),
-            Record::CAA(_)    => self.colours.caa.paint("CAA"),
-            Record::CNAME(_)  => self.colours.cname.paint("CNAME"),
-            Record::HINFO(_)  => self.colours.hinfo.paint("HINFO"),
-            Record::LOC(_)    => self.colours.loc.paint("LOC"),
-            Record::MX(_)     => self.colours.mx.paint("MX"),
-            Record::NAPTR(_)  => self.colours.ns.paint("NAPTR"),
-            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::TLSA(_)   => self.colours.tlsa.paint("TLSA"),
-            Record::TXT(_)    => self.colours.txt.paint("TXT"),
-            Record::URI(_)    => self.colours.uri.paint("URI"),
+            Record::A(_)           => self.colours.a.paint("A"),
+            Record::AAAA(_)        => self.colours.aaaa.paint("AAAA"),
+            Record::CAA(_)         => self.colours.caa.paint("CAA"),
+            Record::CNAME(_)       => self.colours.cname.paint("CNAME"),
+            Record::HINFO(_)       => self.colours.hinfo.paint("HINFO"),
+            Record::LOC(_)         => self.colours.loc.paint("LOC"),
+            Record::MX(_)          => self.colours.mx.paint("MX"),
+            Record::NAPTR(_)       => self.colours.ns.paint("NAPTR"),
+            Record::NS(_)          => self.colours.ns.paint("NS"),
+            Record::OPENPGPKEY(_)  => self.colours.openpgpkey.paint("OPENPGPKEY"),
+            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::TLSA(_)        => self.colours.tlsa.paint("TLSA"),
+            Record::TXT(_)         => self.colours.txt.paint("TXT"),
+            Record::URI(_)         => self.colours.uri.paint("URI"),
 
             Record::Other { ref type_number, .. } => self.colours.unknown.paint(type_number.to_string()),
         }