Files
RustyDHCP/tests/test_options.rs
T

355 lines
13 KiB
Rust

//! Exact-functionality regression tests for `dhcp4r::options`.
use dhcp4r::options::*;
use std::net::Ipv4Addr;
#[test]
fn constants_have_exact_wire_values() {
assert_eq!(SUBNET_MASK, 1);
assert_eq!(TIME_OFFSET, 2);
assert_eq!(ROUTER, 3);
assert_eq!(TIME_SERVER, 4);
assert_eq!(NAME_SERVER, 5);
assert_eq!(DOMAIN_NAME_SERVER, 6);
assert_eq!(LOG_SERVER, 7);
assert_eq!(COOKIE_SERVER, 8);
assert_eq!(LPR_SERVER, 9);
assert_eq!(IMPRESS_SERVER, 10);
assert_eq!(RESOURCE_LOCATION_SERVER, 11);
assert_eq!(HOST_NAME, 12);
assert_eq!(BOOT_FILE_SIZE, 13);
assert_eq!(MERIT_DUMP_FILE, 14);
assert_eq!(DOMAIN_NAME, 15);
assert_eq!(SWAP_SERVER, 16);
assert_eq!(ROOT_PATH, 17);
assert_eq!(EXTENSIONS_PATH, 18);
assert_eq!(IP_FORWARDING_ENABLE_DISABLE, 19);
assert_eq!(NON_LOCAL_SOURCE_ROUTING_ENABLE_DISABLE, 20);
assert_eq!(POLICY_FILTER, 21);
assert_eq!(MAXIMUM_DATAGRAM_REASSEMBLY_SIZE, 22);
assert_eq!(DEFAULT_IP_TIME_TO_LIVE, 23);
assert_eq!(PATH_MTU_AGING_TIMEOUT, 24);
assert_eq!(PATH_MTU_PLATEAU_TABLE, 25);
assert_eq!(INTERFACE_MTU, 26);
assert_eq!(ALL_SUBNETS_ARE_LOCAL, 27);
assert_eq!(BROADCAST_ADDRESS, 28);
assert_eq!(PERFORM_MASK_DISCOVERY, 29);
assert_eq!(MASK_SUPPLIER, 30);
assert_eq!(PERFORM_ROUTER_DISCOVERY, 31);
assert_eq!(ROUTER_SOLICITATION_ADDRESS, 32);
assert_eq!(STATIC_ROUTE, 33);
assert_eq!(TRAILER_ENCAPSULATION, 34);
assert_eq!(ARP_CACHE_TIMEOUT, 35);
assert_eq!(ETHERNET_ENCAPSULATION, 36);
assert_eq!(TCP_DEFAULT_TTL, 37);
assert_eq!(TCP_KEEPALIVE_INTERVAL, 38);
assert_eq!(TCP_KEEPALIVE_GARBAGE, 39);
assert_eq!(NETWORK_INFORMATION_SERVICE_DOMAIN, 40);
assert_eq!(NETWORK_INFORMATION_SERVERS, 41);
assert_eq!(NETWORK_TIME_PROTOCOL_SERVERS, 42);
assert_eq!(VENDOR_SPECIFIC_INFORMATION, 43);
assert_eq!(NETBIOS_OVER_TCPIP_NAME_SERVER, 44);
assert_eq!(NETBIOS_OVER_TCPIP_DATAGRAM_DISTRIBUTION_SERVER, 45);
assert_eq!(NETBIOS_OVER_TCPIP_NODE_TYPE, 46);
assert_eq!(NETBIOS_OVER_TCPIP_SCOPE, 47);
assert_eq!(XWINDOW_SYSTEM_FONT_SERVER, 48);
assert_eq!(XWINDOW_SYSTEM_DISPLAY_MANAGER, 49);
assert_eq!(REQUESTED_IP_ADDRESS, 50);
assert_eq!(IP_ADDRESS_LEASE_TIME, 51);
assert_eq!(OVERLOAD, 52);
assert_eq!(DHCP_MESSAGE_TYPE, 53);
assert_eq!(SERVER_IDENTIFIER, 54);
assert_eq!(PARAMETER_REQUEST_LIST, 55);
assert_eq!(MESSAGE, 56);
assert_eq!(MAXIMUM_DHCP_MESSAGE_SIZE, 57);
assert_eq!(RENEWAL_TIME_VALUE, 58);
assert_eq!(REBINDING_TIME_VALUE, 59);
assert_eq!(VENDOR_CLASS_IDENTIFIER, 60);
assert_eq!(CLIENT_IDENTIFIER, 61);
assert_eq!(NETWORK_INFORMATION_SERVICEPLUS_DOMAIN, 64);
assert_eq!(NETWORK_INFORMATION_SERVICEPLUS_SERVERS, 65);
assert_eq!(TFTP_SERVER_NAME, 66);
assert_eq!(BOOTFILE_NAME, 67);
assert_eq!(MOBILE_IP_HOME_AGENT, 68);
assert_eq!(SIMPLE_MAIL_TRANSPORT_PROTOCOL, 69);
assert_eq!(POST_OFFICE_PROTOCOL_SERVER, 70);
assert_eq!(NETWORK_NEWS_TRANSPORT_PROTOCOL, 71);
assert_eq!(DEFAULT_WORLD_WIDE_WEB_SERVER, 72);
assert_eq!(DEFAULT_FINGER_SERVER, 73);
assert_eq!(DEFAULT_INTERNET_RELAY_CHAT_SERVER, 74);
assert_eq!(STREETTALK_SERVER, 75);
assert_eq!(STREETTALK_DIRECTORY_ASSISTANCE, 76);
assert_eq!(USER_CLASS, 77);
assert_eq!(RAPID_COMMIT, 80);
assert_eq!(RELAY_AGENT_INFORMATION, 82);
assert_eq!(CLIENT_ARCHITECTURE, 93);
assert_eq!(TZ_POSIX_STRING, 100);
assert_eq!(TZ_DATABASE_STRING, 101);
assert_eq!(CLASSLESS_ROUTE_FORMAT, 121);
}
#[test]
fn title_returns_exact_strings_for_known_codes() {
let cases: &[(u8, &str)] = &[
(1, "Subnet Mask"),
(2, "Time Offset"),
(3, "Router"),
(4, "Time Server"),
(5, "Name Server"),
(6, "Domain Name Server"),
(7, "Log Server"),
(8, "Cookie Server"),
(9, "LPR Server"),
(10, "Impress Server"),
(11, "Resource Location Server"),
(12, "Host Name"),
(13, "Boot File Size"),
(14, "Merit Dump File"),
(15, "Domain Name"),
(16, "Swap Server"),
(17, "Root Path"),
(18, "Extensions Path"),
(19, "IP Forwarding Enable/Disable"),
(20, "Non-Local Source Routing Enable/Disable"),
(21, "Policy Filter"),
(22, "Maximum Datagram Reassembly Size"),
(23, "Default IP Time-to-live"),
(24, "Path MTU Aging Timeout"),
(25, "Path MTU Plateau Table"),
(26, "Interface MTU"),
(27, "All Subnets are Local"),
(28, "Broadcast Address"),
(29, "Perform Mask Discovery"),
(30, "Mask Supplier"),
(31, "Perform Router Discovery"),
(32, "Router Solicitation Address"),
(33, "Static Route"),
(34, "Trailer Encapsulation"),
(35, "ARP Cache Timeout"),
(36, "Ethernet Encapsulation"),
(37, "TCP Default TTL"),
(38, "TCP Keepalive Interval"),
(39, "TCP Keepalive Garbage"),
(40, "Network Information Service Domain"),
(41, "Network Information Servers"),
(42, "Network Time Protocol Servers"),
(43, "Vendor Specific Information"),
(44, "NetBIOS over TCP/IP Name Server"),
(45, "NetBIOS over TCP/IP Datagram Distribution Server"),
(46, "NetBIOS over TCP/IP Node Type"),
(47, "NetBIOS over TCP/IP Scope"),
(48, "X Window System Font Server"),
(49, "X Window System Display Manager"),
(50, "Requested IP Address"),
(51, "IP Address Lease Time"),
(52, "Overload"),
(53, "DHCP Message Type"),
(54, "Server Identifier"),
(55, "Parameter Request List"),
(56, "Message"),
(57, "Maximum DHCP Message Size"),
(58, "Renewal (T1) Time Value"),
(59, "Rebinding (T2) Time Value"),
(60, "Vendor class identifier"),
(61, "Client-identifier"),
(64, "Network Information Service+ Domain"),
(65, "Network Information Service+ Servers"),
(66, "TFTP server name"),
(67, "Bootfile name"),
(68, "Mobile IP Home Agent"),
(69, "Simple Mail Transport Protocol (SMTP) Server"),
(70, "Post Office Protocol (POP3) Server"),
(71, "Network News Transport Protocol (NNTP) Server"),
(72, "Default World Wide Web (WWW) Server"),
(73, "Default Finger Server"),
(74, "Default Internet Relay Chat (IRC) Server"),
(75, "StreetTalk Server"),
(76, "StreetTalk Directory Assistance (STDA) Server"),
(77, "User Class"),
(80, "Rapid Commit"),
(82, "Relay Agent Information"),
(93, "Client Architecture"),
(100, "TZ-POSIX String"),
(101, "TZ-Database String"),
(121, "Classless Route Format"),
];
for (code, expected) in cases {
assert_eq!(title(*code), Some(*expected), "title({})", code);
}
}
#[test]
fn title_returns_none_for_unknown_codes() {
for code in [0u8, 62, 63, 78, 79, 81, 83, 99, 122, 200, 254, 255] {
assert_eq!(title(code), None, "title({}) should be None", code);
}
}
#[test]
fn message_type_from_valid_values() {
assert_eq!(MessageType::from(1), Ok(MessageType::Discover));
assert_eq!(MessageType::from(2), Ok(MessageType::Offer));
assert_eq!(MessageType::from(3), Ok(MessageType::Request));
assert_eq!(MessageType::from(4), Ok(MessageType::Decline));
assert_eq!(MessageType::from(5), Ok(MessageType::Ack));
assert_eq!(MessageType::from(6), Ok(MessageType::Nak));
assert_eq!(MessageType::from(7), Ok(MessageType::Release));
assert_eq!(MessageType::from(8), Ok(MessageType::Inform));
}
#[test]
fn message_type_discriminants_match_wire() {
assert_eq!(MessageType::Discover as u8, 1);
assert_eq!(MessageType::Offer as u8, 2);
assert_eq!(MessageType::Request as u8, 3);
assert_eq!(MessageType::Decline as u8, 4);
assert_eq!(MessageType::Ack as u8, 5);
assert_eq!(MessageType::Nak as u8, 6);
assert_eq!(MessageType::Release as u8, 7);
assert_eq!(MessageType::Inform as u8, 8);
}
#[test]
fn message_type_from_invalid_returns_err() {
for v in [0u8, 9, 10, 100, 255] {
let r = MessageType::from(v);
assert!(r.is_err(), "from({}) should err", v);
let msg = r.unwrap_err();
assert!(msg.contains("Invalid DHCP Message Type"), "msg: {}", msg);
assert!(msg.contains(&v.to_string()), "msg should contain value: {}", msg);
}
}
#[test]
fn dhcp_option_code_matches_wire() {
assert_eq!(DhcpOption::DhcpMessageType(MessageType::Discover).code(), 53);
assert_eq!(DhcpOption::ServerIdentifier(Ipv4Addr::new(1, 2, 3, 4)).code(), 54);
assert_eq!(DhcpOption::ParameterRequestList(vec![1]).code(), 55);
assert_eq!(DhcpOption::RequestedIpAddress(Ipv4Addr::new(1, 2, 3, 4)).code(), 50);
assert_eq!(DhcpOption::HostName("h".to_string()).code(), 12);
assert_eq!(DhcpOption::Router(vec![]).code(), 3);
assert_eq!(DhcpOption::DomainNameServer(vec![]).code(), 6);
assert_eq!(DhcpOption::IpAddressLeaseTime(1).code(), 51);
assert_eq!(DhcpOption::SubnetMask(Ipv4Addr::new(255, 255, 255, 0)).code(), 1);
assert_eq!(DhcpOption::Message("m".to_string()).code(), 56);
assert_eq!(
DhcpOption::Unrecognized(RawDhcpOption { code: 99, data: vec![1] }).code(),
99
);
}
#[test]
fn dhcp_option_to_raw_message_type() {
for (t, v) in [
(MessageType::Discover, 1),
(MessageType::Offer, 2),
(MessageType::Request, 3),
(MessageType::Decline, 4),
(MessageType::Ack, 5),
(MessageType::Nak, 6),
(MessageType::Release, 7),
(MessageType::Inform, 8),
] {
let raw = DhcpOption::DhcpMessageType(t).to_raw();
assert_eq!(raw.code, 53);
assert_eq!(raw.data, vec![v]);
}
}
#[test]
fn dhcp_option_to_raw_ipv4_single() {
let raw = DhcpOption::ServerIdentifier(Ipv4Addr::new(192, 168, 1, 1)).to_raw();
assert_eq!(raw.code, 54);
assert_eq!(raw.data, vec![192, 168, 1, 1]);
let raw = DhcpOption::RequestedIpAddress(Ipv4Addr::new(10, 0, 0, 5)).to_raw();
assert_eq!(raw.code, 50);
assert_eq!(raw.data, vec![10, 0, 0, 5]);
let raw = DhcpOption::SubnetMask(Ipv4Addr::new(255, 255, 255, 0)).to_raw();
assert_eq!(raw.code, 1);
assert_eq!(raw.data, vec![255, 255, 255, 0]);
}
#[test]
fn dhcp_option_to_raw_prl_clones() {
let prl = vec![1u8, 3, 6, 51];
let raw = DhcpOption::ParameterRequestList(prl.clone()).to_raw();
assert_eq!(raw.code, 55);
assert_eq!(raw.data, prl);
}
#[test]
fn dhcp_option_to_raw_hostname_and_message() {
let raw = DhcpOption::HostName("foo".to_string()).to_raw();
assert_eq!(raw.code, 12);
assert_eq!(raw.data, b"foo".to_vec());
let raw = DhcpOption::HostName(String::new()).to_raw();
assert_eq!(raw.code, 12);
assert!(raw.data.is_empty());
let raw = DhcpOption::Message("hello".to_string()).to_raw();
assert_eq!(raw.code, 56);
assert_eq!(raw.data, b"hello".to_vec());
}
#[test]
fn dhcp_option_to_raw_router_concatenates() {
let raw = DhcpOption::Router(vec![]).to_raw();
assert_eq!(raw.code, 3);
assert!(raw.data.is_empty());
let raw = DhcpOption::Router(vec![Ipv4Addr::new(192, 168, 1, 1)]).to_raw();
assert_eq!(raw.code, 3);
assert_eq!(raw.data, vec![192, 168, 1, 1]);
let raw = DhcpOption::Router(vec![
Ipv4Addr::new(192, 168, 1, 1),
Ipv4Addr::new(10, 0, 0, 1),
])
.to_raw();
assert_eq!(raw.code, 3);
assert_eq!(raw.data, vec![192, 168, 1, 1, 10, 0, 0, 1]);
}
#[test]
fn dhcp_option_to_raw_dns_concatenates() {
let raw = DhcpOption::DomainNameServer(vec![]).to_raw();
assert_eq!(raw.code, 6);
assert!(raw.data.is_empty());
let raw = DhcpOption::DomainNameServer(vec![Ipv4Addr::new(8, 8, 8, 8)]).to_raw();
assert_eq!(raw.code, 6);
assert_eq!(raw.data, vec![8, 8, 8, 8]);
let raw = DhcpOption::DomainNameServer(vec![
Ipv4Addr::new(8, 8, 8, 8),
Ipv4Addr::new(8, 8, 4, 4),
])
.to_raw();
assert_eq!(raw.code, 6);
assert_eq!(raw.data, vec![8, 8, 8, 8, 8, 8, 4, 4]);
}
#[test]
fn dhcp_option_to_raw_lease_time_is_big_endian() {
let raw = DhcpOption::IpAddressLeaseTime(86400).to_raw();
assert_eq!(raw.code, 51);
assert_eq!(raw.data, 86400u32.to_be_bytes().to_vec());
assert_eq!(raw.data, vec![0, 1, 81, 128]);
let raw = DhcpOption::IpAddressLeaseTime(0).to_raw();
assert_eq!(raw.data, vec![0, 0, 0, 0]);
let raw = DhcpOption::IpAddressLeaseTime(u32::MAX).to_raw();
assert_eq!(raw.data, vec![255, 255, 255, 255]);
}
#[test]
fn dhcp_option_to_raw_unrecognized_clones() {
let inner = RawDhcpOption { code: 200, data: vec![1, 2, 3] };
let raw = DhcpOption::Unrecognized(inner.clone()).to_raw();
assert_eq!(raw, inner);
}