use libremetaverse::packets::{ AgentThrottlePacket, Header, Packet, PacketType, UseCircuitCodePacket, }; use libremetaverse::{Error, Helpers, PacketFrequency}; use libremetaverse_types::UUID; fn header(frequency: PacketFrequency, id: u16) -> Header { Header { ack_list: None, appended_acks: false, frequency, id, reliable: true, resent: false, sequence: 0x0102_0304, zerocoded: false, } } fn header_bytes(header: &Header) -> Vec { let length = match header.frequency { PacketFrequency::High => 7, PacketFrequency::Medium => 8, PacketFrequency::Low => 10, }; let mut bytes = vec![0_u8; length]; let mut position = 0; header .to_bytes(&mut bytes, &mut position) .expect("encode header"); assert_eq!(usize::try_from(position).unwrap(), bytes.len()); bytes } #[test] fn header_frequencies_match_the_reference_wire_bytes() { // Golden source: codegen/inputs/message_template.msg frequency rules and // LibreMetaverse.Tests/PacketTests.cs::HeaderFlags (translated in // tests/compat/tests/wire_semantics.rs::packet_header_flags). assert_eq!( header_bytes(&header(PacketFrequency::High, 0x2a)), [0x40, 1, 2, 3, 4, 0, 0x2a] ); assert_eq!( header_bytes(&header(PacketFrequency::Medium, 0x2a)), [0x40, 1, 2, 3, 4, 0, 0xff, 0x2a] ); assert_eq!( header_bytes(&header(PacketFrequency::Low, 0x1234)), [0x40, 1, 2, 3, 4, 0, 0xff, 0xff, 0x12, 0x34] ); } #[test] fn appended_acks_are_big_endian_and_removed_from_the_payload_boundary() { let mut value = header(PacketFrequency::High, 0x2a); value.appended_acks = true; value.ack_list = Some(vec![0x0102_0304, 0xa0b0_c0d0]); let mut bytes = header_bytes(&value); let mut ack_bytes = [0_u8; 9]; let mut ack_position = 0; value .acks_to_bytes(&mut ack_bytes, &mut ack_position) .expect("encode ACKs"); assert_eq!(ack_bytes, [1, 2, 3, 4, 0xa0, 0xb0, 0xc0, 0xd0, 2]); bytes.extend_from_slice(&ack_bytes); let mut position = 0; let mut packet_end = i32::try_from(bytes.len()).unwrap() - 1; let decoded = Header::build_header(bytes, &mut position, &mut packet_end).expect("decode"); assert!(decoded.appended_acks); assert_eq!(decoded.ack_list, value.ack_list); assert_eq!(position, 7); assert_eq!(packet_end, 6); } #[test] fn use_circuit_code_matches_the_reference_golden_packet() { // Golden schema: codegen/inputs/message_template.msg::UseCircuitCode // (Low 3, CircuitCode/U32 + SessionID/LLUUID + ID/LLUUID). Header behavior // is linked to the translated HeaderFlags test above. let session = (0_u8..16).collect::>(); let agent = (16_u8..32).collect::>(); let mut packet = UseCircuitCodePacket::new_with_constructor().expect("packet constructor"); packet.circuit_code.code = 0x1122_3344; packet.circuit_code.session_id = UUID::new_with_bytes_int32(session.clone(), 0).expect("session UUID"); packet.circuit_code.id = UUID::new_with_bytes_int32(agent.clone(), 0).expect("agent UUID"); let encoded = packet.to_bytes_with_method().expect("encode packet"); let mut expected = vec![ 0x40, 0, 0, 0, 0, 0, 0xff, 0xff, 0, 3, 0x44, 0x33, 0x22, 0x11, ]; expected.extend_from_slice(&session); expected.extend_from_slice(&agent); assert_eq!(encoded, expected); let mut position = 0; let decoded = UseCircuitCodePacket::new_with_bytes_int32(encoded.clone(), &mut position).expect("decode"); assert_eq!(usize::try_from(position).unwrap(), encoded.len()); assert_eq!(decoded.circuit_code.code, 0x1122_3344); assert_eq!( decoded.circuit_code.session_id.get_bytes().unwrap(), session ); assert_eq!(decoded.circuit_code.id.get_bytes().unwrap(), agent); assert_eq!(decoded.to_bytes_with_method().unwrap(), encoded); } #[test] fn malformed_and_unknown_packets_return_errors_without_panicking() { let valid = [0x40, 0, 0, 0, 0, 0, 0xff, 0xff, 0, 3]; for length in 0..valid.len() { let bytes = valid[..length].to_vec(); let outcome = std::panic::catch_unwind(|| { let mut end = i32::try_from(bytes.len()).unwrap() - 1; Packet::build_packet_with_bytes_int32_bytes(bytes.clone(), &mut end, vec![0; 64]) }); assert!(outcome.is_ok(), "truncation at {length} panicked"); assert!( outcome.unwrap().is_err(), "truncation at {length} was accepted" ); } let missing_payload = valid.to_vec(); let mut end = i32::try_from(missing_payload.len()).unwrap() - 1; assert!( Packet::build_packet_with_bytes_int32_bytes(missing_payload, &mut end, vec![0; 64]) .is_err() ); let unknown = vec![0x40, 0, 0, 0, 0, 0, 0xfe]; let mut end = i32::try_from(unknown.len()).unwrap() - 1; let Err(error) = Packet::build_packet_with_bytes_int32_bytes(unknown, &mut end, vec![0; 64]) else { panic!("unknown packet ID was accepted"); }; assert!(matches!(error, Error::Parse { .. })); } #[test] fn packet_factory_decodes_zerocoded_payloads() { let mut packet = AgentThrottlePacket::new_with_constructor().expect("packet constructor"); packet.agent_data.circuit_code = 0x1122_3344; packet.throttle.gen_counter = 0x5566_7788; packet.throttle.throttles = vec![1, 0, 0, 2, 3]; let raw = packet.to_bytes_with_method().expect("raw packet"); let mut encoded = vec![0_u8; raw.len() * 2 + 2]; let encoded_length = Helpers::zero_encode( Some(&raw), i32::try_from(raw.len()).unwrap(), Some(&mut encoded), ) .expect("zero encode"); encoded.truncate(usize::try_from(encoded_length).unwrap()); let mut end = encoded_length - 1; let built = Packet::build_packet_with_bytes_int32_bytes(encoded, &mut end, vec![0_u8; raw.len()]) .expect("packet factory"); assert_eq!(built.type_, PacketType::AgentThrottle); assert_eq!(end, i32::try_from(raw.len()).unwrap() - 1); } #[test] fn packet_id_dispatch_covers_known_and_unknown_ids() { assert_eq!( Packet::get_type(3, PacketFrequency::Low).expect("packet type"), PacketType::UseCircuitCode ); assert_eq!( Packet::get_type(u16::MAX, PacketFrequency::High).expect("unknown type"), PacketType::Default ); assert_eq!(Helpers::MSG_RELIABLE, 0x40); }