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

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

@@ -49,6 +49,9 @@ pub use self::tlsa::TLSA;
 mod txt;
 pub use self::txt::TXT;
 
+mod uri;
+pub use self::uri::URI;
+
 
 mod others;
 pub use self::others::{UnknownQtype, find_other_qtype_number};
@@ -105,6 +108,9 @@ pub enum Record {
     /// A **TXT** record.
     TXT(TXT),
 
+    /// A **URI** record.
+    URI(URI),
+
     /// A record with a type that we don’t recognise.
     Other {
 

+ 111 - 0
dns/src/record/uri.rs

@@ -0,0 +1,111 @@
+use log::*;
+
+use crate::wire::*;
+
+
+/// A **URI** record, which holds a URI along with weight and priority values
+/// to balance between several records.
+///
+/// # References
+///
+/// - [RFC 7553](https://tools.ietf.org/html/rfc7553) — The Uniform Resource
+///   Identifier (URI) DNS Resource Record (June 2015)
+/// - [RFC 3986](https://tools.ietf.org/html/rfc3986) — Uniform Resource
+///   Identifier (URI): Generic Syntax (January 2005)
+#[derive(PartialEq, Debug)]
+pub struct URI {
+
+    /// The priority of the URI. Clients are supposed to contact the URI with
+    /// the lowest priority out of all the ones it can reach.
+    pub priority: u16,
+
+    /// The weight of the URI, which specifies a relative weight for entries
+    /// with the same priority.
+    pub weight: u16,
+
+    /// The URI contained in the record. Since all we are doing is displaying
+    /// it to the user, we do not need to parse it for accuracy.
+    pub contents: String,
+}
+
+impl Wire for URI {
+    const NAME: &'static str = "URI";
+    const RR_TYPE: u16 = 256;
+
+    #[cfg_attr(all(test, feature = "with_mutagen"), ::mutagen::mutate)]
+    fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireError> {
+        let priority = c.read_u16::<BigEndian>()?;
+        trace!("Parsed priority -> {:?}", priority);
+
+        let weight = c.read_u16::<BigEndian>()?;
+        trace!("Parsed weight -> {:?}", weight);
+
+        if stated_length <= 4 {
+            let mandated_length = MandatedLength::AtLeast(5);
+            return Err(WireError::WrongRecordLength { stated_length, mandated_length });
+        }
+
+        let remaining_length = stated_length - 4;
+        let mut buf = Vec::with_capacity(usize::from(remaining_length));
+
+        for _ in 0 .. remaining_length {
+            buf.push(c.read_u8()?);
+        }
+
+        let contents = String::from_utf8_lossy(&buf).to_string();
+        trace!("Parsed contents -> {:?}", contents);
+
+        Ok(Self { priority, weight, contents })
+    }
+}
+
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use pretty_assertions::assert_eq;
+
+    #[test]
+    fn parses() {
+        let buf = &[
+            0x00, 0x0A,  // priority
+            0x00, 0x10,  // weight
+            0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x72, 0x66, 0x63,
+            0x73, 0x2e, 0x69, 0x6f, 0x2f,  // uri
+        ];
+
+        assert_eq!(URI::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(),
+                   URI {
+                       priority: 10,
+                       weight: 16,
+                       contents: String::from("https://rfcs.io/"),
+                   });
+    }
+
+    #[test]
+    fn missing_any_data() {
+        let buf = &[
+            0x00, 0x0A,  // priority
+            0x00, 0x10,  // weight
+        ];
+
+        assert_eq!(URI::read(buf.len() as _, &mut Cursor::new(buf)),
+                   Err(WireError::WrongRecordLength { stated_length: 4, mandated_length: MandatedLength::AtLeast(5) }));
+    }
+
+    #[test]
+    fn record_empty() {
+        assert_eq!(URI::read(0, &mut Cursor::new(&[])),
+                   Err(WireError::IO));
+    }
+
+    #[test]
+    fn buffer_ends_abruptly() {
+        let buf = &[
+            0x00, 0x0A,  // half a priority
+        ];
+
+        assert_eq!(URI::read(23, &mut Cursor::new(buf)),
+                   Err(WireError::IO));
+    }
+}

+ 2 - 0
dns/src/wire.rs

@@ -187,6 +187,7 @@ impl Record {
         try_record!(SRV);
         try_record!(TLSA);
         try_record!(TXT);
+        try_record!(URI);
 
         // Otherwise, collect the bytes into a vector and return an unknown
         // record type.
@@ -250,6 +251,7 @@ pub fn find_qtype_number(record_type: &str) -> Option<TypeInt> {
     try_record!(SRV);
     try_record!(TLSA);
     try_record!(TXT);
+    try_record!(URI);
 
     None
 }

+ 2 - 0
src/colours.rs

@@ -29,6 +29,7 @@ pub struct Colours {
     pub srv: Style,
     pub tlsa: Style,
     pub txt: Style,
+    pub uri: Style,
     pub unknown: Style,
 }
 
@@ -60,6 +61,7 @@ impl Colours {
             srv: Cyan.normal(),
             tlsa: Yellow.normal(),
             txt: Yellow.normal(),
+            uri: Yellow.normal(),
             unknown: White.on(Red),
         }
     }

+ 11 - 0
src/output.rs

@@ -259,6 +259,9 @@ impl TextFormat {
                 let messages = txt.messages.iter().map(|t| format!("{:?}", t)).collect::<Vec<_>>();
                 messages.join(", ")
             }
+            Record::URI(ref uri) => {
+                format!("{} {} {:?}", uri.priority, uri.weight, uri.contents)
+            }
             Record::Other { ref bytes, .. } => {
                 format!("{:?}", bytes)
             }
@@ -473,6 +476,14 @@ fn json_record(record: &Record) -> JsonValue {
                 "messages": rec.messages,
             })
         }
+        Record::URI(rec) => {
+            json!({
+                "type": "URI",
+                "priority": rec.priority,
+                "weight": rec.weight,
+                "contents": rec.contents,
+            })
+        }
         Record::Other { type_number, bytes } => {
             let type_name = match type_number {
                 UnknownQtype::HeardOf(name) => json!(name),

+ 1 - 0
src/table.rs

@@ -129,6 +129,7 @@ impl Table {
             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()),
         }