use pcap_file::pcap::{PcapPacket, PcapWriter};
use std::{
fmt::{self, Write},
fs::File,
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Packet {
pub data: Vec<u8>,
}
impl Packet {
pub fn packet_to_pcap(&self) -> Result<(), Box<dyn std::error::Error>> {
let file = File::create("output.pcap")?;
let writer = PcapWriter::new(file);
let orig_len = self.data.len() as u32;
let timestamp: std::time::Duration =
std::time::SystemTime::now().duration_since(std::time::SystemTime::UNIX_EPOCH)?;
let packet = PcapPacket::new(timestamp, orig_len, &self.data);
writer?.write_packet(&packet)?;
Ok(())
}
}
impl From<&str> for Packet {
fn from(hex: &str) -> Self {
Packet {
data: hex_stream_to_bytes(hex),
}
}
}
impl fmt::Display for Packet {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", format_hex_array(&self.data))
}
}
pub fn hex_stream_to_bytes(hex: &str) -> Vec<u8> {
let mut bytes = Vec::new();
assert!(
hex.len().is_multiple_of(2),
"La chaîne hexadécimale doit avoir une longueur paire"
);
for i in (0..hex.len()).step_by(2) {
let byte_str = &hex[i..i + 2];
let byte = u8::from_str_radix(byte_str, 16).expect("Valeur hex invalide");
bytes.push(byte);
}
bytes
}
pub fn bytes_to_hex_string(bytes: &[u8]) -> String {
let mut hex_string = String::with_capacity(bytes.len() * 2);
for byte in bytes {
write!(&mut hex_string, "{byte:02X}").unwrap();
}
hex_string
}
pub fn format_hex_array(bytes: &[u8]) -> String {
const BYTES_PER_LINE: usize = 8;
if bytes.is_empty() {
return "[\n];".to_string();
}
let mut out = String::from("[\n");
for chunk in bytes.chunks(BYTES_PER_LINE) {
out.push_str(" ");
for (i, byte) in chunk.iter().enumerate() {
if i > 0 {
out.push_str(", ");
}
write!(&mut out, "0x{:02X}", byte).unwrap();
}
out.push_str(",\n");
}
out.push_str("];");
out
}
pub fn display_packet(bytes: &[u8]) {
println!("Packet: {}", format_hex_array(bytes));
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
use std::path::Path;
#[test]
fn test_hex_stream_to_bytes_valid_ascii() {
let hex = "48656C6C6F"; let expected = vec![0x48, 0x65, 0x6C, 0x6C, 0x6F];
assert_eq!(hex_stream_to_bytes(hex), expected);
}
#[test]
fn test_hex_stream_to_bytes_empty() {
let hex = "";
let expected: Vec<u8> = vec![];
assert_eq!(hex_stream_to_bytes(hex), expected);
}
#[test]
fn test_hex_stream_to_bytes_lowercase() {
let hex = "deadbeef";
let expected = vec![0xDE, 0xAD, 0xBE, 0xEF];
assert_eq!(hex_stream_to_bytes(hex), expected);
}
#[test]
#[should_panic(expected = "La chaîne hexadécimale doit avoir une longueur paire")]
fn test_hex_stream_to_bytes_odd_length_should_panic() {
let _ = hex_stream_to_bytes("ABC");
}
#[test]
#[should_panic(expected = "Valeur hex invalide")]
fn test_hex_stream_to_bytes_invalid_hex_should_panic() {
let _ = hex_stream_to_bytes("ZZ");
}
#[test]
fn test_bytes_to_hex_string_ascii() {
let bytes = vec![0x48, 0x65, 0x6C, 0x6C, 0x6F];
let expected = "48656C6C6F";
assert_eq!(bytes_to_hex_string(&bytes), expected);
}
#[test]
fn test_bytes_to_hex_string_empty() {
let bytes: Vec<u8> = vec![];
let expected = "";
assert_eq!(bytes_to_hex_string(&bytes), expected);
}
#[test]
fn test_bytes_to_hex_string_uppercase_output() {
let bytes = vec![0xDE, 0xAD, 0xBE, 0xEF];
let expected = "DEADBEEF";
assert_eq!(bytes_to_hex_string(&bytes), expected);
}
#[test]
fn test_format_hex_array_empty() {
let bytes: Vec<u8> = vec![];
let expected = "[\n];";
assert_eq!(format_hex_array(&bytes), expected);
}
#[test]
fn test_format_hex_array_single_line() {
let bytes = vec![0x00, 0x01, 0x02];
let expected = "[\n 0x00, 0x01, 0x02,\n];";
assert_eq!(format_hex_array(&bytes), expected);
}
#[test]
fn test_format_hex_array_exactly_one_full_line() {
let bytes = vec![0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07];
let expected = "[\n 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\n];";
assert_eq!(format_hex_array(&bytes), expected);
}
#[test]
fn test_format_hex_array_multiple_lines() {
let bytes = vec![0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09];
let expected = concat!(
"[\n",
" 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\n",
" 0x08, 0x09,\n",
"];"
);
assert_eq!(format_hex_array(&bytes), expected);
}
#[test]
fn test_packet_from_str() {
let packet = Packet::from("48656C6C6F");
let expected = Packet {
data: vec![0x48, 0x65, 0x6C, 0x6C, 0x6F],
};
assert_eq!(packet, expected);
}
#[test]
fn test_packet_display() {
let packet = Packet::from("48656C6C6F");
let expected = "[\n 0x48, 0x65, 0x6C, 0x6C, 0x6F,\n];";
assert_eq!(packet.to_string(), expected);
}
#[test]
fn test_packet_clone_and_eq() {
let packet1 = Packet::from("DEADBEEF");
let packet2 = packet1.clone();
assert_eq!(packet1, packet2);
}
#[test]
fn test_packet_to_pcap_creates_file_and_is_non_empty() {
let pcap_path = Path::new("output.pcap");
if pcap_path.exists() {
fs::remove_file(pcap_path).unwrap();
}
let packet = Packet::from("48656C6C6F");
let result = packet.packet_to_pcap();
assert!(result.is_ok());
assert!(pcap_path.exists());
let metadata = fs::metadata(pcap_path).unwrap();
assert!(metadata.len() > 0);
fs::remove_file(pcap_path).unwrap();
}
}