diff --git a/src/packet.rs b/src/packet.rs index 3c753d5..eb51056 100644 --- a/src/packet.rs +++ b/src/packet.rs @@ -1,6 +1,7 @@ use std::convert::TryInto; use rand::Rng; +use thiserror::Error; use crate::attributes::Attributes; use crate::avp::{AVPType, AVP}; @@ -9,9 +10,7 @@ use crate::code::Code; const MAX_PACKET_LENGTH: usize = 4096; const RADIUS_PACKET_HEADER_LENGTH: usize = 20; // i.e. minimum packet length -use thiserror::Error; - -#[derive(Error, Debug)] +#[derive(Error, Debug, PartialEq)] pub enum PacketError { #[error("radius packet doesn't have enough length of bytes; it has to be at least {0} bytes")] InsufficientPacketLengthError(usize), @@ -49,6 +48,10 @@ impl Packet { } } + pub fn get_code(&self) -> Code { + self.code + } + pub fn get_identifier(&self) -> u8 { self.identifier } @@ -243,10 +246,12 @@ impl Packet { #[cfg(test)] mod tests { + use std::net::Ipv4Addr; + + use crate::avp::AVP; use crate::code::Code; use crate::packet::{Packet, PacketError}; use crate::rfc2865; - use std::net::Ipv4Addr; #[test] fn test_for_rfc2865_7_1() -> Result<(), PacketError> { @@ -303,4 +308,183 @@ mod tests { Ok(()) } + + #[test] + fn test_for_rfc2865_7_2() -> Result<(), PacketError> { + let secret: Vec = "xyzzy5461".as_bytes().to_vec(); + let request: Vec = vec![ + 0x01, 0x01, 0x00, 0x47, 0x2a, 0xee, 0x86, 0xf0, 0x8d, 0x0d, 0x55, 0x96, 0x9c, 0xa5, + 0x97, 0x8e, 0x0d, 0x33, 0x67, 0xa2, 0x01, 0x08, 0x66, 0x6c, 0x6f, 0x70, 0x73, 0x79, + 0x03, 0x13, 0x16, 0xe9, 0x75, 0x57, 0xc3, 0x16, 0x18, 0x58, 0x95, 0xf2, 0x93, 0xff, + 0x63, 0x44, 0x07, 0x72, 0x75, 0x04, 0x06, 0xc0, 0xa8, 0x01, 0x10, 0x05, 0x06, 0x00, + 0x00, 0x00, 0x14, 0x06, 0x06, 0x00, 0x00, 0x00, 0x02, 0x07, 0x06, 0x00, 0x00, 0x00, + 0x01, + ]; + + let request_packet = Packet::decode(&request, &secret)?; + assert_eq!(request_packet.get_code(), Code::AccessRequest); + assert_eq!(request_packet.identifier, 1); + assert_eq!( + rfc2865::lookup_user_name(&request_packet).unwrap().unwrap(), + "flopsy" + ); + assert_eq!( + rfc2865::lookup_nas_ip_address(&request_packet) + .unwrap() + .unwrap(), + Ipv4Addr::from([192, 168, 1, 16]), + ); + assert_eq!( + rfc2865::lookup_nas_port(&request_packet).unwrap().unwrap(), + 20 + ); + assert_eq!( + rfc2865::lookup_service_type(&request_packet) + .unwrap() + .unwrap(), + rfc2865::SERVICE_TYPE_FRAMED_USER, + ); + assert_eq!( + rfc2865::lookup_framed_protocol(&request_packet) + .unwrap() + .unwrap(), + rfc2865::FRAMED_PROTOCOL_PPP, + ); + + let response: Vec = vec![ + 0x02, 0x01, 0x00, 0x38, 0x15, 0xef, 0xbc, 0x7d, 0xab, 0x26, 0xcf, 0xa3, 0xdc, 0x34, + 0xd9, 0xc0, 0x3c, 0x86, 0x01, 0xa4, 0x06, 0x06, 0x00, 0x00, 0x00, 0x02, 0x07, 0x06, + 0x00, 0x00, 0x00, 0x01, 0x08, 0x06, 0xff, 0xff, 0xff, 0xfe, 0x0a, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x0d, 0x06, 0x00, 0x00, 0x00, 0x01, 0x0c, 0x06, 0x00, 0x00, 0x05, + // ^ incorrectly a 2 in the document + 0xdc, + ]; + let response_packet = Packet::decode(&response, &secret).unwrap(); + + assert_eq!(response_packet.get_code(), Code::AccessAccept); + assert_eq!(response_packet.get_identifier(), 1); + assert_eq!( + rfc2865::lookup_service_type(&response_packet) + .unwrap() + .unwrap(), + rfc2865::SERVICE_TYPE_FRAMED_USER + ); + assert_eq!( + rfc2865::lookup_framed_protocol(&response_packet) + .unwrap() + .unwrap(), + rfc2865::FRAMED_PROTOCOL_PPP, + ); + assert_eq!( + rfc2865::lookup_framed_ip_address(&response_packet) + .unwrap() + .unwrap(), + Ipv4Addr::from([255, 255, 255, 254]), + ); + assert_eq!( + rfc2865::lookup_framed_routing(&response_packet) + .unwrap() + .unwrap(), + rfc2865::FRAMED_ROUTING_NONE, + ); + assert_eq!( + rfc2865::lookup_framed_compression(&response_packet) + .unwrap() + .unwrap(), + rfc2865::FRAMED_COMPRESSION_VAN_JACOBSON_TCP_IP, + ); + assert_eq!( + rfc2865::lookup_framed_mtu(&response_packet) + .unwrap() + .unwrap(), + 1500, + ); + + Ok(()) + } + + #[test] + fn test_passwords() { + let passwords = vec![ + b"".to_vec(), + b"qwerty".to_vec(), + b"helloworld1231231231231233489hegufudhsgdsfygdf8g".to_vec(), + ]; + + let secret = b"xyzzy5461"; + + for password in passwords { + let mut request_packet = Packet::new(Code::AccessRequest, secret); + rfc2865::add_user_password(&mut request_packet, &password).unwrap(); + + let encoded = request_packet.encode().unwrap(); + + let decoded = Packet::decode(&encoded, secret).unwrap(); + assert_eq!( + rfc2865::lookup_user_password(&decoded).unwrap().unwrap(), + password + ); + } + } + + #[test] + fn test_parse_invalid() { + struct TestCase<'a> { + plain_text: &'a str, + expected_error: PacketError, + }; + + let test_cases = &[ + TestCase { + plain_text: "\x01", + expected_error: PacketError::InsufficientPacketLengthError(20), + }, + TestCase { + plain_text: "\x01\x7f\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01", + expected_error: PacketError::InvalidPacketLengthError(0), + }, + TestCase { + plain_text: "\x01\x7f\x7f\x7f\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01", + expected_error: PacketError::InvalidPacketLengthError(32639), + }, + TestCase { + plain_text: "\x00\x7f\x00\x16\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00", + expected_error: PacketError::InvalidPacketLengthError(22), + }, + TestCase { + plain_text: "\x01\x01\x00\x16\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00", + expected_error: PacketError::DecodingError("invalid attribute length".to_owned()), + } + ]; + + let secret = b"12345"; + for test_case in test_cases { + let result = Packet::decode(test_case.plain_text.as_bytes(), secret); + assert_eq!(result.is_err(), true); + assert_eq!(result.err().unwrap(), test_case.expected_error); + } + } + + #[test] + fn test_packet_attribute_length_boundary() { + let mut packet = Packet::new(Code::AccessRequest, b"12345"); + packet.add(AVP { + typ: 1, + value: vec![1; 253], + }); + let encoded = packet.encode(); + assert_eq!(encoded.is_err(), false); + + let mut packet = Packet::new(Code::AccessRequest, b"12345"); + packet.add(AVP { + typ: 1, + value: vec![1; 254], + }); + let encoded = packet.encode(); + assert_eq!(encoded.is_err(), true); + assert_eq!( + encoded.err().unwrap(), + PacketError::EncodingError("attribute is too large".to_owned()), + ); + } }