1use std::convert::From;
12use std::fmt;
13use std::io::{Read, Write};
14use std::mem::transmute;
15use std::net::Ipv4Addr;
16use std::time::{Duration, SystemTime};
17
18use bitflags::bitflags;
19use thiserror::Error;
20
21const PCAP_MAGIC: u32 = 0xA1B2_3C4D;
23
24const PCAP_BAD_ENDIANNESS: u32 = 0x4D3C_B2A1;
26
27#[derive(Debug, Error)]
29pub enum IpParseErr {
30 #[error("Found version {0}, expected 4")]
32 Version(u8),
33 #[error("Found protocol {0}")]
35 Proto(u8),
36 #[error("IPv4 header is 20 bytes, only passed a buffer of size {0}")]
38 Size(usize),
39 #[error("Failed to parse the TCP payload")]
41 Tcp(#[from] TcpParseErr),
42 #[error("Error occurred while parsing UDP payload")]
44 Udp(#[from] UdpParseErr),
45 #[error("This library doesn't support ip options")]
47 Options,
48 #[error("Size field in packet is larger than data passed in ({0})")]
50 InvalidSize(usize),
51 #[error("IHL field was outside valid range of [6, 15] ({0})")]
53 InvalidIHL(usize),
54 #[error("Encountered IO error")]
56 IOError(#[from] std::io::Error),
57 #[error("PCAP file endianness does not match")]
59 EndianError,
60 #[error("Pcap file had bad magic: {0:0X}")]
62 BadMagic(u32),
63 #[error("Pcap file is invalid")]
65 PcapInvalid,
66 #[error("Pcap file too small")]
68 PcapFileSize,
69}
70
71#[non_exhaustive]
78#[derive(Debug)]
79pub enum DataType {
80 TCP(TcpPacket),
81 UDP(UdpDatagram),
82}
83
84impl DataType {
85 pub fn pcap_write<W: Write>(&self, f: &mut W) -> std::io::Result<()> {
90 match self {
91 Self::TCP(p) => p.pcap_write(f),
92 Self::UDP(p) => p.pcap_write(f),
93 }
94 }
95
96 pub fn get_proto_num(&self) -> u8 {
97 match self {
98 Self::TCP(_) => 6,
99 Self::UDP(_) => 17,
100 }
101 }
102
103 pub fn get_payload(&self) -> &Vec<u8> {
104 match self {
105 Self::TCP(p) => &p.data,
106 Self::UDP(p) => &p.payload,
107 }
108 }
109}
110
111impl From<TcpPacket> for DataType {
112 fn from(packet: TcpPacket) -> Self {
113 Self::TCP(packet)
114 }
115}
116
117impl From<UdpDatagram> for DataType {
118 fn from(packet: UdpDatagram) -> Self {
119 Self::UDP(packet)
120 }
121}
122
123#[derive(Debug)]
125pub struct IpPacket {
126 pub dscp: u8,
131 pub ecn: u8,
135 pub len: usize,
140 pub id: u16,
144 pub flags: u8,
148 pub frag_off: u16,
153 pub ttl: u8,
158 pub checksum: u16,
164 pub source: Ipv4Addr,
168 pub dest: Ipv4Addr,
172 pub options: Option<Vec<u8>>,
177 pub payload: DataType,
181 pub recv_time: SystemTime,
186}
187
188impl IpPacket {
189 pub fn parse_from_bytes(
202 data: &dyn AsRef<[u8]>,
203 time: Option<SystemTime>,
204 ) -> Result<Self, IpParseErr> {
205 let recv_time = match time {
208 None => SystemTime::now(),
209 Some(t) => t,
210 };
211 let data = data.as_ref();
212 if data.len() < 20 {
213 return Err(IpParseErr::Size(data.len()));
214 }
215 let version = data[0] >> 4;
216 if version != 4 {
217 return Err(IpParseErr::Version(version));
218 }
219 let ihl = data[0] & 0xf;
220 if !(5..=15).contains(&ihl) {
221 return Err(IpParseErr::InvalidIHL(ihl as usize));
222 }
223 if ihl as usize * 4 > data.len() {
224 return Err(IpParseErr::InvalidIHL(ihl as usize));
225 }
226 let dscp = (data[1] & 0xfc) >> 2;
227 let ecn = data[1] & 0x3;
228 let len = u16::from_be_bytes(data[2..4].try_into().unwrap()) as usize;
229 if len > data.len() {
230 return Err(IpParseErr::InvalidSize(len));
231 }
232 let id = u16::from_be_bytes(data[4..6].try_into().unwrap());
233 let flags = data[6] >> 5;
234 let frag_off: u16 = (u16::from(data[6] & 0x1f) << 8) + u16::from(data[7]);
235 let ttl = data[8];
236 let proto = data[9];
237 let checksum = u16::from_be_bytes(data[10..12].try_into().unwrap());
238 let source = Ipv4Addr::new(data[12], data[13], data[14], data[15]);
239 let dest = Ipv4Addr::new(data[16], data[17], data[18], data[19]);
240 let end: usize;
241 let options = if ihl == 5 {
242 end = 20;
243 None
244 } else {
245 end = ihl as usize * 4;
246 Some(data[20..end].to_vec())
247 };
248 if len < end {
249 return Err(IpParseErr::Size(len));
250 }
251 let payload: DataType = match proto {
252 6 => TcpPacket::parse_from_bytes(&&data[end..len])?.into(),
253 17 => UdpDatagram::parse_from_bytes(&&data[end..len])?.into(),
254 p => return Err(IpParseErr::Proto(p)),
255 };
256 Ok(IpPacket {
257 dscp,
258 ecn,
259 len,
260 id,
261 flags,
262 frag_off,
263 ttl,
264 checksum,
265 source,
266 dest,
267 options,
268 payload,
269 recv_time,
270 })
271 }
272
273 pub fn pcap_write<W: Write>(&self, file: &mut W) -> std::io::Result<()> {
278 let diff = self
279 .recv_time
280 .duration_since(SystemTime::UNIX_EPOCH)
281 .unwrap();
282 let secs = diff.as_secs() as u32;
283 let nanos = diff.as_nanos() as u32;
284 file.write_all(&secs.to_ne_bytes())?;
285 file.write_all(&nanos.to_ne_bytes())?;
286 let size = self.len as u32;
287 file.write_all(&size.to_ne_bytes())?;
288 file.write_all(&size.to_ne_bytes())?;
289 let ihl = self.options.clone().map_or(0, |v| v.len() >> 2) as u8;
290 let version_ihl = 0x40 | (ihl + 5);
291 file.write_all(&version_ihl.to_be_bytes())?;
292 let dscp_ecn = (self.dscp << 2) | self.ecn;
293 file.write_all(&dscp_ecn.to_be_bytes())?;
294 let total_len = self.len as u16;
295 file.write_all(&total_len.to_be_bytes())?;
296 file.write_all(&self.id.to_be_bytes())?;
297 let frag_off = ((self.flags as u16) << 13) | self.frag_off;
298 file.write_all(&frag_off.to_be_bytes())?;
299 file.write_all(&self.ttl.to_be_bytes())?;
300 file.write_all(&self.payload.get_proto_num().to_be_bytes())?;
301 file.write_all(&self.checksum.to_be_bytes())?;
302 file.write_all(&self.source.octets())?;
303 file.write_all(&self.dest.octets())?;
304 if let Some(ref o) = self.options {
305 file.write_all(o)?;
306 }
307 self.payload.pcap_write(file)?;
308 Ok(())
309 }
310}
311
312impl fmt::Display for IpPacket {
313 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
314 f.debug_struct("IPv4 Header")
315 .field("Total length", &self.len)
316 .field("Source addr", &self.source)
317 .field("Dest addr", &self.dest)
318 .field("Payload", &self.payload)
319 .finish_non_exhaustive()
320 }
321}
322
323bitflags! {
324 #[repr(transparent)]
328 pub struct TcpFlags: u16 {
329 const NS = 0b1_0000_0000;
331 const CWR = 0b1000_0000;
333 const ECE = 0b100_0000;
339 const URG = 0b10_0000;
341 const ACK = 0b10000;
343 const PSH = 0b1000;
345 const RST = 0b100;
347 const SYN = 0b10;
349 const FIN = 0b1;
351 }
352}
353
354#[derive(Clone)]
356pub struct TcpPacket {
357 pub source: u16,
359 pub dest: u16,
361 pub seq: u32,
363 pub ack: u32,
365 pub flags: TcpFlags,
367 pub window: u16,
369 pub checksum: u16,
371 pub urg: u16,
373 pub option_data: Option<Vec<u8>>,
375 pub data: Vec<u8>,
377}
378
379impl fmt::Debug for TcpPacket {
380 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
381 f.debug_struct("TCP Packet")
382 .field("Source Addr", &self.source)
383 .field("Dest Addr", &self.source)
384 .field("Data Len", &self.data)
385 .finish_non_exhaustive()
386 }
387}
388
389#[derive(Error, Debug)]
391pub enum TcpParseErr {
392 #[error("Data is too small to be a TCP packet {0}")]
394 Size(usize),
395 #[error("Header has invalid size field {0}")]
397 InvalidSize(usize),
398}
399
400impl TcpPacket {
401 pub fn parse_from_bytes(data: &dyn AsRef<[u8]>) -> Result<Self, TcpParseErr> {
408 let data = data.as_ref();
409 if data.len() < 20 {
410 return Err(TcpParseErr::Size(data.len()));
411 }
412 let source = u16::from_be_bytes(data[0..2].try_into().unwrap());
413 let dest = u16::from_be_bytes(data[2..4].try_into().unwrap());
414 let seq = u32::from_be_bytes(data[4..8].try_into().unwrap());
415 let ack = u32::from_be_bytes(data[8..12].try_into().unwrap());
416 let data_off = data[12] >> 4;
417 if data_off as usize * 4 > data.len() {
418 return Err(TcpParseErr::InvalidSize(data_off as usize));
419 }
420 let mut flag_bits: u16 = u16::from(data[12] & 1) << 8;
421 flag_bits += u16::from(data[13]);
422 let flags: TcpFlags = unsafe { transmute(flag_bits) };
426 let window = u16::from_be_bytes(data[14..16].try_into().unwrap());
427 let checksum = u16::from_be_bytes(data[16..18].try_into().unwrap());
428 let urg = u16::from_be_bytes(data[18..20].try_into().unwrap());
429 let option_data: Option<Vec<u8>> = if data_off > 5 {
430 Some(data[20..(data_off as usize) * 4].to_vec())
431 } else {
432 None
433 };
434 let data = data[(data_off as usize) * 4..].to_vec();
435 Ok(TcpPacket {
436 source,
437 dest,
438 seq,
439 ack,
440 flags,
441 window,
442 checksum,
443 urg,
444 option_data,
445 data,
446 })
447 }
448
449 pub fn pcap_write<W: Write>(&self, file: &mut W) -> std::io::Result<()> {
456 file.write_all(&self.source.to_be_bytes())?;
457 file.write_all(&self.dest.to_be_bytes())?;
458 file.write_all(&self.seq.to_be_bytes())?;
459 file.write_all(&self.ack.to_be_bytes())?;
460 let mut data_off = self.option_data.clone().map_or(0_usize, |v| v.len() >> 2);
461 data_off += 5;
462 data_off <<= 4;
463 data_off |= (self.flags.bits >> 8) as usize;
464 let data_off_ns = data_off as u8;
465 file.write_all(&data_off_ns.to_be_bytes())?;
466 let options = (self.flags.bits & 0xFF) as u8;
467 file.write_all(&options.to_be_bytes())?;
468 file.write_all(&self.window.to_be_bytes())?;
469 file.write_all(&self.checksum.to_be_bytes())?;
470 file.write_all(&self.urg.to_be_bytes())?;
471 if let Some(ref v) = self.option_data {
472 file.write_all(v)?;
473 }
474 file.write_all(&self.data)?;
475 Ok(())
476 }
477}
478
479#[derive(Debug, Error)]
481pub enum UdpParseErr {
482 #[error("Size in header did not match size of data")]
484 SizeMismatch,
485 #[error("Passed in data is not large enough for UDP header")]
487 MissingHeader,
488}
489
490pub struct UdpDatagram {
492 pub source: u16,
494 pub dest: u16,
496 pub checksum: u16,
498 pub payload: Vec<u8>,
500}
501
502impl fmt::Debug for UdpDatagram {
503 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
504 f.debug_struct("UDP Packet")
505 .field("Source Port", &self.source)
506 .field("Dest Port", &self.dest)
507 .field("Data len", &self.payload.len())
508 .finish_non_exhaustive()
509 }
510}
511
512impl UdpDatagram {
513 pub fn parse_from_bytes(data: &dyn AsRef<[u8]>) -> Result<Self, UdpParseErr> {
521 let data = data.as_ref();
522 if data.len() < 8 {
523 return Err(UdpParseErr::MissingHeader);
524 }
525 let source = u16::from_be_bytes(data[0..2].try_into().unwrap());
526 let dest = u16::from_be_bytes(data[2..4].try_into().unwrap());
527 let length = u16::from_be_bytes(data[4..6].try_into().unwrap()) as usize;
528 let checksum = u16::from_be_bytes(data[6..8].try_into().unwrap());
529 if !(8..data.len()).contains(&length) {
530 return Err(UdpParseErr::SizeMismatch);
531 }
532 let payload = data[8..length].to_vec();
533 Ok(Self {
534 source,
535 dest,
536 checksum,
537 payload,
538 })
539 }
540
541 pub fn pcap_write<W: Write>(&self, f: &mut W) -> std::io::Result<()> {
546 f.write_all(&self.source.to_be_bytes())?;
547 f.write_all(&self.dest.to_be_bytes())?;
548 f.write_all(&self.payload.len().to_be_bytes())?;
549 f.write_all(&self.checksum.to_be_bytes())?;
550 f.write_all(&self.payload)?;
551 Ok(())
552 }
553}
554
555pub fn write_pcap_file<W: Write>(
562 packets: &Vec<IpPacket>,
563 pcap_file: &mut W,
564) -> std::io::Result<()> {
565 pcap_file.write_all(&PCAP_MAGIC.to_ne_bytes())?;
566 pcap_file.write_all(&2_u16.to_ne_bytes())?;
567 pcap_file.write_all(&4_u16.to_ne_bytes())?;
568 pcap_file.write_all(&0_u32.to_ne_bytes())?;
569 pcap_file.write_all(&0_u32.to_ne_bytes())?;
570 pcap_file.write_all(&0xFFFF_u32.to_ne_bytes())?;
571 pcap_file.write_all(&101_u32.to_ne_bytes())?;
572
573 for packet in packets {
574 packet.pcap_write(pcap_file)?;
575 }
576
577 pcap_file.flush()?;
578 Ok(())
579}
580
581pub fn read_pcap_file<R: Read>(pcap_file: &mut R) -> Result<Vec<IpPacket>, IpParseErr> {
593 let mut pcap_data = Vec::new();
594 let data_size = pcap_file.read_to_end(&mut pcap_data)?;
595 if data_size < 24 {
596 return Err(IpParseErr::PcapFileSize);
597 }
598 let magic = u32::from_ne_bytes(pcap_data[0..4].try_into().unwrap());
599 if magic == PCAP_BAD_ENDIANNESS {
600 return Err(IpParseErr::EndianError);
601 }
602 if magic != PCAP_MAGIC {
603 return Err(IpParseErr::BadMagic(magic));
604 }
605 if u16::from_ne_bytes(pcap_data[4..6].try_into().unwrap()) != 2 {
607 return Err(IpParseErr::PcapInvalid);
608 }
609 if u16::from_ne_bytes(pcap_data[6..8].try_into().unwrap()) != 4 {
611 return Err(IpParseErr::PcapInvalid);
612 }
613 if u32::from_ne_bytes(pcap_data[20..24].try_into().unwrap()) != 101 {
616 return Err(IpParseErr::PcapInvalid);
617 }
618 let mut packets = Vec::new();
619 let mut index: usize = 24;
620 while index + 16 < data_size {
621 let seconds = u32::from_ne_bytes(pcap_data[index..index + 4].try_into().unwrap()) as u64;
622 let nanos = u32::from_ne_bytes(pcap_data[index + 4..index + 8].try_into().unwrap());
623 let time = Some(
624 SystemTime::UNIX_EPOCH
625 .checked_add(Duration::new(seconds, nanos))
626 .unwrap(),
627 );
628 let packet_size =
629 u32::from_ne_bytes(pcap_data[index + 8..index + 12].try_into().unwrap()) as usize;
630 if index + 16 + packet_size > data_size {
631 break;
632 }
633 let packet = IpPacket::parse_from_bytes(&&pcap_data[index + 16..][..packet_size], time);
634 if let Ok(p) = packet {
635 packets.push(p);
636 }
637 index += 16;
638 index += packet_size;
639 }
640 Ok(packets)
641}
642
643#[cfg(test)]
644mod ip_testing {
645 use super::*;
646 use std::io::Cursor;
647
648 const EMPTY_PACKET_BYTES: &[u8] = &[
649 0x45, 0x00, 0x00, 0x28, 0xde, 0xad, 0x00, 0x00, 0x00, 0x06, 0xbe, 0xef, 0x12, 0x34, 0x56,
650 0x78, 0xab, 0xcd, 0xef, 0x12, 0x43, 0x21, 0xfe, 0xdc, 0xde, 0xad, 0xbe, 0xef, 0x69, 0x69,
651 0x69, 0x69, 0x50, 0x00, 0x10, 0x00, 0x99, 0x99, 0x00, 0x00,
652 ];
653
654 #[test]
655 fn empty_packet() {
656 let packet = IpPacket::parse_from_bytes(&EMPTY_PACKET_BYTES, None).unwrap();
657 assert_eq!(packet.id, 0xdead);
658 assert_eq!(packet.checksum, 0xbeef);
659 assert_eq!(packet.len, 40);
660 }
661
662 #[test]
663 fn test_pcap_write() {
664 let packet = IpPacket::parse_from_bytes(&EMPTY_PACKET_BYTES, None).unwrap();
665 let mut file = Cursor::new(Vec::new());
666 packet.pcap_write(&mut file).unwrap();
667 let buffer = file.into_inner();
668 assert_eq!(buffer[8], 40);
669 assert_eq!(buffer[9], 0);
670 assert_eq!(buffer[10], 0);
671 assert_eq!(buffer[11], 0);
672 assert_eq!(buffer[12], 40);
673 assert_eq!(buffer[13], 0);
674 assert_eq!(buffer[14], 0);
675 assert_eq!(buffer[15], 0);
676 assert_eq!(&buffer[16..], EMPTY_PACKET_BYTES);
677 }
678
679 #[test]
680 fn test_read_pcap() {
681 use std::fs::File;
682 let mut file = File::open("./test/test.pcap").unwrap();
683 let packets = read_pcap_file(&mut file).unwrap();
684 assert!(packets.len() > 0);
685 }
686
687 #[test]
688 fn test_fuzz_crash_pcap_read() {
689 use std::fs::File;
690 let mut file =
691 File::open("test/test.pcap")
692 .unwrap();
693 let _data = read_pcap_file(&mut file);
694 }
695}