diff --git a/Cargo.toml b/Cargo.toml index 56ab3c424333ac8c8b127eed20a2f2ec815e9b11..af4294590b2861d8ff1c94266ab65b225fa7f35b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,4 +16,7 @@ mio = "0.3.7" threadpool = "0.1" url = "0.2.36" num = "0.1" -rand = "0.3" \ No newline at end of file +rand = "0.3" + +[dev-dependencies] +quickcheck = "*" \ No newline at end of file diff --git a/src/client.rs b/src/client.rs index d92077cde7dc8a75a721b760517b13d8e9ce8559..13ec0fb065568ec73ef5810219f1829f393d0c86 100644 --- a/src/client.rs +++ b/src/client.rs @@ -138,4 +138,26 @@ impl CoAPClient { _ => SchemeType::NonRelative, } } +} + + +#[cfg(test)] +mod test { + use super::*; + use packet::{Packet, PacketType}; + + #[test] + fn test_request_error_url() { + assert!(CoAPClient::request("http://127.0.0.1").is_err()); + assert!(CoAPClient::request("coap://127.0.0.").is_err()); + assert!(CoAPClient::request("127.0.0.1").is_err()); + } + + #[test] + fn test_reply_error() { + let client = CoAPClient::new("127.0.0.1:5683").unwrap(); + let mut packet = Packet::new(); + packet.header.set_type(PacketType::Acknowledgement); + assert!(client.reply(&packet, b"Test".to_vec()).is_err()); + } } \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 664ace205f8c81abdb46152b2fb57f7868aa1f26..2eaa9c7163a092c6b8105a7fcf51b2f293cf31a1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -71,6 +71,7 @@ extern crate threadpool; extern crate url; extern crate num; extern crate rand; +#[cfg(test)] extern crate quickcheck; pub use server::CoAPServer; pub use client::CoAPClient; diff --git a/src/packet.rs b/src/packet.rs index 691e95ff5ddb9f83565cfb5366a0328ab4e0b02f..878c1a12d244cb15a326bc460bf9bcd03cf9aa58 100644 --- a/src/packet.rs +++ b/src/packet.rs @@ -229,9 +229,16 @@ impl Packet { idx += 1; if delta == 13 { + if idx >= buf.len() { + return Err(ParseError::InvalidOptionLength); + } delta = buf[idx] as usize + 13; idx += 1; } else if delta == 14 { + if idx + 1 >= buf.len() { + return Err(ParseError::InvalidOptionLength); + } + delta = (u16::from_be(u8_to_unsigned_be!(buf, idx, idx + 1, u16)) + 269) as usize; idx += 2; } else if delta == 15 { @@ -239,9 +246,17 @@ impl Packet { } if length == 13 { + if idx >= buf.len() { + return Err(ParseError::InvalidOptionLength); + } + length = buf[idx] as usize + 13; idx += 1; } else if length == 14 { + if idx + 1 >= buf.len() { + return Err(ParseError::InvalidOptionLength); + } + length = (u16::from_be(u8_to_unsigned_be!(buf, idx, idx + 1, u16)) + 269) as usize; idx += 2; } else if length == 15 { @@ -251,6 +266,9 @@ impl Packet { options_number += delta; let end = idx + length; + if end > buf.len() { + return Err(ParseError::InvalidOptionLength); + } let options_value = buf[idx..end].to_vec(); if options.contains_key(&options_number) { @@ -474,4 +492,19 @@ mod test { packet.payload = "Hello".as_bytes().to_vec(); assert_eq!(packet.to_bytes().unwrap(), vec!(0x64, 0x45, 0x13, 0xFD, 0xD0, 0xE2, 0x4D, 0xAC, 0xFF, 0x48, 0x65, 0x6C, 0x6C, 0x6F)); } + + #[test] + fn test_malicious_packet() { + use quickcheck::{QuickCheck, TestResult}; + + fn run(x: Vec<u8>) -> TestResult { + match Packet::from_bytes(&x[..]) { + Ok(packet) => { + TestResult::from_bool(packet.get_token().len() == packet.header.get_token_length() as usize) + }, + Err(_) => TestResult::passed() + } + } + QuickCheck::new().tests(10000).quickcheck(run as fn(Vec<u8>) -> TestResult) + } } \ No newline at end of file