Browse Source

Increase test coverage

Benjamin Sago 4 years ago
parent
commit
6252c24b5a

+ 13 - 3
dns/src/record/a.rs

@@ -56,7 +56,7 @@ mod test {
     }
 
     #[test]
-    fn too_short() {
+    fn record_too_short() {
         let buf = &[
             0x7F, 0x00, 0x00,  // Too short IPv4 address
         ];
@@ -66,7 +66,7 @@ mod test {
     }
 
     #[test]
-    fn too_long() {
+    fn record_too_long() {
         let buf = &[
             0x7F, 0x00, 0x00, 0x00,  // IPv4 address
             0x01,  // Unexpected extra byte
@@ -77,8 +77,18 @@ mod test {
     }
 
     #[test]
-    fn empty() {
+    fn record_empty() {
         assert_eq!(A::read(0, &mut Cursor::new(&[])),
                    Err(WireError::WrongRecordLength { expected: 4, got: 0 }));
     }
+
+    #[test]
+    fn buffer_ends_abruptly() {
+        let buf = &[
+            0x7F, 0x00,  // Half an IPv4 address
+        ];
+
+        assert_eq!(A::read(4, &mut Cursor::new(buf)),
+                   Err(WireError::IO));
+    }
 }

+ 13 - 3
dns/src/record/aaaa.rs

@@ -59,7 +59,7 @@ mod test {
     }
 
     #[test]
-    fn too_long() {
+    fn record_too_long() {
         let buf = &[
             0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
             0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,  // IPv6 address
@@ -71,7 +71,7 @@ mod test {
     }
 
     #[test]
-    fn too_short() {
+    fn record_too_short() {
         let buf = &[
             0x05, 0x05, 0x05, 0x05, 0x05,  // Five arbitrary bytes
         ];
@@ -81,8 +81,18 @@ mod test {
     }
 
     #[test]
-    fn empty() {
+    fn record_empty() {
         assert_eq!(AAAA::read(0, &mut Cursor::new(&[])),
                    Err(WireError::WrongRecordLength { expected: 16, got: 0 }));
     }
+
+    #[test]
+    fn buffer_ends_abruptly() {
+        let buf = &[
+            0x05, 0x05, 0x05, 0x05, 0x05,  // Five arbitrary bytes
+        ];
+
+        assert_eq!(AAAA::read(16, &mut Cursor::new(buf)),
+                   Err(WireError::IO));
+    }
 }

+ 31 - 4
dns/src/record/caa.rs

@@ -8,7 +8,7 @@ use crate::wire::*;
 ///
 /// # References
 ///
-/// - [RFC 6844](https://tools.ietf.org/html/rfc6844) — DNS Certification Authority Authorization Resource Record (January 2013s
+/// - [RFC 6844](https://tools.ietf.org/html/rfc6844) — DNS Certification Authority Authorization Resource Record (January 2013)
 #[derive(PartialEq, Debug, Clone)]
 pub struct CAA {
 
@@ -74,9 +74,9 @@ mod test {
     use super::*;
 
     #[test]
-    fn parses() {
+    fn parses_non_critical() {
         let buf = &[
-            0x00,  // flags
+            0x00,  // flags (all unset)
             0x09,  // tag length
             0x69, 0x73, 0x73, 0x75, 0x65, 0x77, 0x69, 0x6c, 0x64,  // tag
             0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74,  // value
@@ -91,8 +91,35 @@ mod test {
     }
 
     #[test]
-    fn empty() {
+    fn parses_critical() {
+        let buf = &[
+            0x80,  // flags (critical bit set)
+            0x09,  // tag length
+            0x69, 0x73, 0x73, 0x75, 0x65, 0x77, 0x69, 0x6c, 0x64,  // tag
+            0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74,  // value
+        ];
+
+        assert_eq!(CAA::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(),
+                   CAA {
+                       critical: true,
+                       tag: String::from("issuewild"),
+                       value: String::from("entrust.net"),
+                   });
+    }
+
+    #[test]
+    fn record_empty() {
         assert_eq!(CAA::read(0, &mut Cursor::new(&[])),
                    Err(WireError::IO));
     }
+
+    #[test]
+    fn buffer_ends_abruptly() {
+        let buf = &[
+            0x00,  // flags
+        ];
+
+        assert_eq!(CAA::read(23, &mut Cursor::new(buf)),
+                   Err(WireError::IO));
+    }
 }

+ 11 - 1
dns/src/record/cname.rs

@@ -55,9 +55,19 @@ mod test {
     }
 
     #[test]
-    fn empty() {
+    fn record_empty() {
         assert_eq!(CNAME::read(0, &mut Cursor::new(&[])),
                    Err(WireError::IO));
     }
+
+    #[test]
+    fn buffer_ends_abruptly() {
+        let buf = &[
+            0x05, 0x62, 0x73,  // the stard of a string
+        ];
+
+        assert_eq!(CNAME::read(23, &mut Cursor::new(buf)),
+                   Err(WireError::IO));
+    }
 }
 

+ 11 - 1
dns/src/record/mx.rs

@@ -66,8 +66,18 @@ mod test {
     }
 
     #[test]
-    fn empty() {
+    fn record_empty() {
         assert_eq!(MX::read(0, &mut Cursor::new(&[])),
                    Err(WireError::IO));
     }
+
+    #[test]
+    fn buffer_ends_abruptly() {
+        let buf = &[
+            0x00, 0x0A,  // half a preference
+        ];
+
+        assert_eq!(MX::read(23, &mut Cursor::new(buf)),
+                   Err(WireError::IO));
+    }
 }

+ 11 - 1
dns/src/record/ns.rs

@@ -57,8 +57,18 @@ mod test {
     }
 
     #[test]
-    fn empty() {
+    fn record_empty() {
         assert_eq!(NS::read(0, &mut Cursor::new(&[])),
                    Err(WireError::IO));
     }
+
+    #[test]
+    fn buffer_ends_abruptly() {
+        let buf = &[
+            0x01,  // the first byte of a string
+        ];
+
+        assert_eq!(NS::read(23, &mut Cursor::new(buf)),
+                   Err(WireError::IO));
+    }
 }

+ 34 - 3
dns/src/record/opt.rs

@@ -112,13 +112,13 @@ mod test {
     use super::*;
 
     #[test]
-    fn parses() {
+    fn parses_no_data() {
         let buf = &[
             0x05, 0xAC,  // UDP payload size
             0x00,        // higher bits
             0x00, 0x00,  // EDNS(0) version
             0x00, 0x00,  // flags
-            0x00,        // data
+            0x00,        // data length (followed by no data)
         ];
 
         assert_eq!(OPT::read(&mut Cursor::new(buf)).unwrap(),
@@ -132,8 +132,39 @@ mod test {
     }
 
     #[test]
-    fn empty() {
+    fn parses_with_data() {
+        let buf = &[
+            0x05, 0xAC,  // UDP payload size
+            0x00,        // higher bits
+            0x00, 0x00,  // EDNS(0) version
+            0x00, 0x00,  // flags
+            0x04,        // data length
+            0x01, 0x02, 0x03, 0x04,  // data
+        ];
+
+        assert_eq!(OPT::read(&mut Cursor::new(buf)).unwrap(),
+                   OPT {
+                       udp_payload_size: 1452,
+                       higher_bits: 0,
+                       edns0_version: 0,
+                       flags: 0,
+                       data: vec![1, 2, 3, 4],
+                   });
+    }
+
+    #[test]
+    fn record_empty() {
         assert_eq!(OPT::read(&mut Cursor::new(&[])),
                    Err(WireError::IO));
     }
+
+    #[test]
+    fn buffer_ends_abruptly() {
+        let buf = &[
+            0x05,  // half a UDP payload size
+        ];
+
+        assert_eq!(OPT::read(&mut Cursor::new(buf)),
+                   Err(WireError::IO));
+    }
 }

+ 11 - 1
dns/src/record/ptr.rs

@@ -61,8 +61,18 @@ mod test {
     }
 
     #[test]
-    fn empty() {
+    fn record_empty() {
         assert_eq!(PTR::read(0, &mut Cursor::new(&[])),
                    Err(WireError::IO));
     }
+
+    #[test]
+    fn buffer_ends_abruptly() {
+        let buf = &[
+            0x03, 0x64,  // the start of a cname
+        ];
+
+        assert_eq!(PTR::read(23, &mut Cursor::new(buf)),
+                   Err(WireError::IO));
+    }
 }

+ 11 - 1
dns/src/record/soa.rs

@@ -115,8 +115,18 @@ mod test {
     }
 
     #[test]
-    fn empty() {
+    fn record_empty() {
         assert_eq!(SOA::read(0, &mut Cursor::new(&[])),
                    Err(WireError::IO));
     }
+
+    #[test]
+    fn buffer_ends_abruptly() {
+        let buf = &[
+            0x05, 0x62,  // the start of an mname
+        ];
+
+        assert_eq!(SOA::read(23, &mut Cursor::new(buf)),
+                   Err(WireError::IO));
+    }
 }

+ 11 - 1
dns/src/record/srv.rs

@@ -85,8 +85,18 @@ mod test {
     }
 
     #[test]
-    fn empty() {
+    fn record_empty() {
         assert_eq!(SRV::read(0, &mut Cursor::new(&[])),
                    Err(WireError::IO));
     }
+
+    #[test]
+    fn buffer_ends_abruptly() {
+        let buf = &[
+            0x00,  // half a priority
+        ];
+
+        assert_eq!(SRV::read(23, &mut Cursor::new(buf)),
+                   Err(WireError::IO));
+    }
 }

+ 61 - 3
dns/src/record/txt.rs

@@ -73,9 +73,10 @@ mod test {
     use super::*;
 
     #[test]
-    fn parses() {
+    fn parses_one_iteration() {
         let buf = &[
-            0x06, 0x74, 0x78, 0x74, 0x20, 0x6d, 0x65,  // message
+            0x06,  // message chunk length
+            0x74, 0x78, 0x74, 0x20, 0x6d, 0x65,  // message chunk
         ];
 
         assert_eq!(TXT::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(),
@@ -85,8 +86,65 @@ mod test {
     }
 
     #[test]
-    fn empty() {
+    fn parses_two_iterations() {
+        let buf = &[
+            0xFF,  // message chunk length
+            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+            0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+            0x41, 0x41,  // exactly two hundred and fifty five ‘A’s (screaming)
+            0x04,  // message chunk length
+            0x41, 0x41, 0x41, 0x41,  // four more ‘A’s (the scream abruptly stops)
+        ];
+
+        assert_eq!(TXT::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(),
+                   TXT {
+                       message: String::from("AAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
+                                              AAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
+                                              AAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
+                                              AAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
+                                              AAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
+                                              AAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
+                                              AAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
+                                              AAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
+                                              AAAAAAAAAAAAAAAAAAAAAAAAAAA"),
+                   });
+        // did you know you can just _write_ code like this, and nobody will stop you?
+    }
+
+    #[test]
+    fn record_empty() {
         assert_eq!(TXT::read(0, &mut Cursor::new(&[])),
                    Err(WireError::IO));
     }
+
+    #[test]
+    fn buffer_ends_abruptly() {
+        let buf = &[
+            0x06, 0x74,  // the start of a message
+        ];
+
+        assert_eq!(TXT::read(23, &mut Cursor::new(buf)),
+                   Err(WireError::IO));
+    }
 }

+ 146 - 1
dns/tests/wire_parsing_tests.rs

@@ -1,7 +1,152 @@
-use dns::Response;
+use std::net::Ipv4Addr;
+
+use dns::{Response, Query, Answer, Flags, QClass, qtype};
+use dns::record::{Record, A, CNAME, OPT};
 
 
 #[test]
 fn parse_nothing() {
     assert!(Response::from_bytes(&[]).is_err());
 }
+
+
+#[test]
+fn parse_response_standard() {
+    let buf = &[
+        0x0d, 0xcd,  // transaction ID
+        0x81, 0x80,  // flags (standard query, response, no error)
+        0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,  // counts (1, 1, 0, 1)
+
+        // the query:
+        0x03, 0x64, 0x6e, 0x73, 0x06, 0x6c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x03,
+        0x64, 0x6f, 0x67, 0x00,  // "dns.lookup.dog."
+        0x00, 0x01,  // type A
+        0x00, 0x01,  // class IN
+
+        // the answer:
+        0xc0, 0x0c,  // to find the name, backtrack to position 0x0c (12)
+        0x00, 0x01,  // type A
+        0x00, 0x01,  // class IN
+        0x00, 0x00, 0x03, 0xa5,  // TTL (933 seconds)
+        0x00, 0x04,  // record data length 4
+        0x8a, 0x44, 0x75, 0x5e,  // record date (138.68.117.94)
+
+        // the additional:
+        0x00,        // no name
+        0x00, 0x29,  // type OPT
+        0x02, 0x00,  // UDP payload size (512)
+        0x00, 0x00,  // higher bits (all 0)
+        0x00,        // EDNS version
+        0x00, 0x00,  // extra bits (DO bit unset)
+        0x00,        // data length 0
+    ];
+
+    let response = Response {
+        transaction_id: 0x0dcd,
+        flags: Flags {
+            response: true,
+            opcode: 0,
+            authoritative: false,
+            truncated: false,
+            recursion_desired: true,
+            recursion_available: true,
+            authentic_data: false,
+            checking_disabled: false,
+            error_code: None,
+        },
+        queries: vec![
+            Query {
+                qname: "dns.lookup.dog.".into(),
+                qclass: QClass::IN,
+                qtype: qtype!(A),
+            },
+        ],
+        answers: vec![
+            Answer::Standard {
+                qname: "dns.lookup.dog.".into(),
+                qclass: QClass::IN,
+                ttl: 933,
+                record: Record::A(A {
+                    address: Ipv4Addr::new(138, 68, 117, 94),
+                }),
+            }
+        ],
+        authorities: vec![],
+        additionals: vec![
+            Answer::Pseudo {
+                qname: "".into(),
+                opt: OPT {
+                    udp_payload_size: 512,
+                    higher_bits: 0,
+                    edns0_version: 0,
+                    flags: 0,
+                    data: vec![],
+                },
+            },
+        ],
+    };
+
+    assert_eq!(Response::from_bytes(buf), Ok(response));
+}
+
+
+#[test]
+fn parse_response_with_mixed_string() {
+    let buf = &[
+        0x06, 0x9f,  // transaction ID
+        0x81, 0x80,  // flags (standard query, response, no error)
+        0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,  // counts (1, 1, 0, 0)
+
+        // the query:
+        0x0d, 0x63, 0x6e, 0x61, 0x6d, 0x65, 0x2d, 0x65, 0x78, 0x61, 0x6d, 0x70,
+        0x6c, 0x65, 0x06, 0x6c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x03, 0x64, 0x6f,
+        0x67, 0x00,  // "cname-example.lookup.dog"
+        0x00, 0x05,  // type CNAME
+        0x00, 0x01,  // class IN
+
+        // the answer:
+        0xc0, 0x0c,  // to find the name, backtrack to position 0x0c (12)
+        0x00, 0x05,  // type CNAME
+        0x00, 0x01,  // class IN
+        0x00, 0x00, 0x03, 0x69,  // TTL (873 seconds)
+        0x00, 0x06,  // record data length 6
+        0x03, 0x64, 0x6e, 0x73, 0xc0, 0x1a,
+        // "dns.lookup.dog.", which is "dns." + backtrack to position 0x1a (28)
+    ];
+
+    let response = Response {
+        transaction_id: 0x069f,
+        flags: Flags {
+            response: true,
+            opcode: 0,
+            authoritative: false,
+            truncated: false,
+            recursion_desired: true,
+            recursion_available: true,
+            authentic_data: false,
+            checking_disabled: false,
+            error_code: None,
+        },
+        queries: vec![
+            Query {
+                qname: "cname-example.lookup.dog.".into(),
+                qclass: QClass::IN,
+                qtype: qtype!(CNAME),
+            },
+        ],
+        answers: vec![
+            Answer::Standard {
+                qname: "cname-example.lookup.dog.".into(),
+                qclass: QClass::IN,
+                ttl: 873,
+                record: Record::CNAME(CNAME {
+                    domain: "dns.lookup.dog.".into(),
+                }),
+            }
+        ],
+        authorities: vec![],
+        additionals: vec![],
+    };
+
+    assert_eq!(Response::from_bytes(buf), Ok(response));
+}