use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
use crate::bytes::Reader;
use crate::packet::{self, AllowedPackets, Packet};
use crate::replay::ReplayProtection;
use crate::token::{
self, CHALLENGE_TOKEN_BYTES, CONNECT_TOKEN_NONCE_BYTES, CONNECT_TOKEN_PRIVATE_BYTES,
ConnectToken, PrivateConnectToken,
};
use crate::{
CONNECT_TOKEN_BYTES, KEY_BYTES, Key, MAX_PACKET_BYTES, MAX_PAYLOAD_BYTES,
MAX_SERVERS_PER_CONNECT, USER_DATA_BYTES,
};
const FUZZ_PROTOCOL_ID: u64 = 0x1122334455667788;
const FUZZ_KEY: Key = [0x5A; KEY_BYTES];
const FUZZ_PRIVATE_KEY: Key = [0xA5; KEY_BYTES];
pub fn read_packet_raw(data: &[u8]) {
let mut replay_protection = ReplayProtection::new();
let mut buffer = data.to_vec();
let _ = packet::read_packet(
&mut buffer,
Some(&FUZZ_KEY),
FUZZ_PROTOCOL_ID,
0,
Some(&FUZZ_PRIVATE_KEY),
AllowedPackets::SERVER,
Some(&mut replay_protection),
);
let mut buffer = data.to_vec();
let _ = packet::read_packet(
&mut buffer,
Some(&FUZZ_KEY),
FUZZ_PROTOCOL_ID,
0,
None,
AllowedPackets::CLIENT,
Some(&mut replay_protection),
);
}
pub fn packet_write_read_round_trip(data: &[u8]) {
if data.len() < 9 {
return;
}
let selector = data[0];
let sequence = u64::from_le_bytes(data[1..9].try_into().unwrap());
let rest = &data[9..];
let mut reader = Reader::new(rest);
let (packet, allowed_packets) = match selector % 7 {
0 => (Packet::Denied, AllowedPackets::CLIENT),
1 | 2 => {
let Some(challenge_token_sequence) = reader.read_u64() else {
return;
};
let Some(challenge_token_data) = reader.read_bytes::<CHALLENGE_TOKEN_BYTES>() else {
return;
};
if selector % 7 == 1 {
(
Packet::Challenge { challenge_token_sequence, challenge_token_data },
AllowedPackets::CLIENT,
)
} else {
(
Packet::Response { challenge_token_sequence, challenge_token_data },
AllowedPackets::SERVER,
)
}
}
3 => {
let Some(client_index) = reader.read_u32() else {
return;
};
let Some(max_clients) = reader.read_u32() else {
return;
};
(Packet::KeepAlive { client_index, max_clients }, AllowedPackets::CLIENT)
}
4 => {
let payload = &rest[..rest.len().min(MAX_PAYLOAD_BYTES)];
if payload.is_empty() {
return;
}
(Packet::Payload(payload.to_vec()), AllowedPackets::CLIENT)
}
5 => (Packet::Disconnect, AllowedPackets::CLIENT),
6 => return connection_request_round_trip(&mut reader),
_ => unreachable!(),
};
let mut buffer = [0u8; MAX_PACKET_BYTES];
let written = packet::write_packet(&packet, &mut buffer, sequence, &FUZZ_KEY, FUZZ_PROTOCOL_ID)
.expect("write_packet must succeed for a valid packet");
let (output, output_sequence) = packet::read_packet(
&mut buffer[..written],
Some(&FUZZ_KEY),
FUZZ_PROTOCOL_ID,
0,
None,
allowed_packets,
None,
)
.expect("a packet produced by write_packet must read back");
assert_eq!(output_sequence, sequence);
assert_eq!(output, packet);
}
fn connection_request_round_trip(reader: &mut Reader<'_>) {
let Some(client_id) = reader.read_u64() else {
return;
};
let Some(timeout_raw) = reader.read_u32() else {
return;
};
let Some(num_addresses_byte) = reader.read_u8() else {
return;
};
let num_addresses = (num_addresses_byte as usize % MAX_SERVERS_PER_CONNECT) + 1;
let mut server_addresses = Vec::with_capacity(num_addresses);
for _ in 0..num_addresses {
let Some(octets) = reader.read_bytes::<4>() else {
return;
};
let Some(port) = reader.read_u16() else {
return;
};
server_addresses.push(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::from(octets), port)));
}
let Some(client_to_server_key) = reader.read_bytes::<KEY_BYTES>() else {
return;
};
let Some(server_to_client_key) = reader.read_bytes::<KEY_BYTES>() else {
return;
};
let Some(user_data) = reader.read_bytes::<USER_DATA_BYTES>() else {
return;
};
let Some(nonce) = reader.read_bytes::<CONNECT_TOKEN_NONCE_BYTES>() else {
return;
};
let Some(expire_raw) = reader.read_u64() else {
return;
};
let expire_timestamp = expire_raw.max(1);
let private_token = PrivateConnectToken {
client_id,
timeout_seconds: timeout_raw as i32,
server_addresses,
client_to_server_key,
server_to_client_key,
user_data,
};
let mut private_data = Box::new([0u8; CONNECT_TOKEN_PRIVATE_BYTES]);
private_token.write(&mut private_data);
token::encrypt_connect_token_private(
&mut private_data,
FUZZ_PROTOCOL_ID,
expire_timestamp,
&nonce,
&FUZZ_PRIVATE_KEY,
)
.expect("encrypting the private connect token must succeed");
let request =
Packet::Request { protocol_id: FUZZ_PROTOCOL_ID, expire_timestamp, nonce, private_data };
let mut buffer = [0u8; MAX_PACKET_BYTES];
let written = packet::write_packet(&request, &mut buffer, 0, &FUZZ_KEY, FUZZ_PROTOCOL_ID)
.expect("write_packet must succeed for a valid connection request");
let (output, _) = packet::read_packet(
&mut buffer[..written],
None,
FUZZ_PROTOCOL_ID,
0,
Some(&FUZZ_PRIVATE_KEY),
AllowedPackets::SERVER,
None,
)
.expect("a connection request produced by write_packet must read back");
let Packet::Request { private_data: decrypted, .. } = output else {
panic!("connection request packet read back as a different type");
};
let output_token = PrivateConnectToken::read(&decrypted[..])
.expect("the decrypted private connect token must read back");
assert_eq!(output_token, private_token);
}
pub fn connect_token_round_trip(data: &[u8]) {
if data.len() < CONNECT_TOKEN_BYTES {
return;
}
let buffer: [u8; CONNECT_TOKEN_BYTES] = data[..CONNECT_TOKEN_BYTES].try_into().unwrap();
let Ok(token) = ConnectToken::read(&buffer) else {
return;
};
let mut written = [0u8; CONNECT_TOKEN_BYTES];
token.write(&mut written);
let reread = ConnectToken::read(&written).expect("a written connect token must re-read");
assert_eq!(reread, token);
}
pub fn private_connect_token_round_trip(data: &[u8]) {
if data.len() < CONNECT_TOKEN_PRIVATE_BYTES {
return;
}
let Ok(token) = PrivateConnectToken::read(&data[..CONNECT_TOKEN_PRIVATE_BYTES]) else {
return;
};
let mut written = [0u8; CONNECT_TOKEN_PRIVATE_BYTES];
token.write(&mut written);
let reread = PrivateConnectToken::read(&written[..])
.expect("a written private connect token must re-read");
assert_eq!(reread, token);
}