pcap-file-2.0.0/.cargo_vcs_info.json0000644000000001360000000000100126730ustar { "git": { "sha1": "b84ebe50893619d3c39d1105589962d23598b9a1" }, "path_in_vcs": "" }pcap-file-2.0.0/.gitattributes000064400000000000000000000001301046102023000143500ustar 00000000000000*.pcapng filter=lfs diff=lfs merge=lfs -text *.pcap filter=lfs diff=lfs merge=lfs -text pcap-file-2.0.0/.gitignore000064400000000000000000000000411046102023000134460ustar 00000000000000.idea target Cargo.lock .DS_Storepcap-file-2.0.0/Cargo.toml0000644000000025000000000000100106660ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "pcap-file" version = "2.0.0" authors = ["Courvoif "] exclude = [ "benches/bench.pcap", "benches/bench.pcapng", "fuzz", "tests", ] description = "A crate to parse, read and write Pcap and PcapNg" documentation = "https://docs.rs/pcap-file/" readme = "README.md" keywords = [ "pcap", "pcapng", "parse", "read", "write", ] categories = [ "encoding", "parsing", ] license = "MIT" repository = "https://github.com/courvoif/pcap-file" [profile.bench] lto = "fat" [[bench]] name = "benches" harness = false [dependencies.byteorder_slice] version = "3.0.0" [dependencies.derive-into-owned] version = "0.2.0" [dependencies.thiserror] version = "1.0.35" [dev-dependencies.criterion] version = "0.4.0" [dev-dependencies.glob] version = "0.3.0" [dev-dependencies.hex] version = "0.4.3" pcap-file-2.0.0/Cargo.toml.orig000064400000000000000000000013031046102023000143470ustar 00000000000000[package] name = "pcap-file" edition = "2021" version = "2.0.0" authors = ["Courvoif "] description = "A crate to parse, read and write Pcap and PcapNg" license = "MIT" documentation = "https://docs.rs/pcap-file/" repository = "https://github.com/courvoif/pcap-file" readme = "README.md" keywords = ["pcap", "pcapng", "parse", "read", "write"] categories = ["encoding", "parsing"] exclude = ["benches/bench.pcap", "benches/bench.pcapng", "fuzz", "tests"] [dependencies] byteorder_slice = "3.0.0" derive-into-owned = "0.2.0" thiserror = "1.0.35" [dev-dependencies] criterion = "0.4.0" glob = "0.3.0" hex = "0.4.3" [[bench]] name = "benches" harness = false [profile.bench] lto = "fat" pcap-file-2.0.0/LICENSE000064400000000000000000000020511046102023000124660ustar 00000000000000MIT License Copyright (c) 2017 courvoif Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. pcap-file-2.0.0/README.md000064400000000000000000000046541046102023000127530ustar 00000000000000# pcap-file Provides parsers, readers and writers for Pcap and PcapNg files. For Pcap files see the pcap module. For PcapNg files see the pcapng module. [![Crates.io](https://img.shields.io/crates/v/pcap-file.svg)](https://crates.io/crates/pcap-file) [![rustdoc](https://img.shields.io/badge/Doc-pcap--file-green.svg)](https://docs.rs/pcap-file/) [![Crates.io](https://img.shields.io/crates/l/pcap-file.svg)](https://github.com/courvoif/pcap-file/blob/master/LICENSE) ## Documentation ## Installation This crate is on [crates.io](https://crates.io/crates/pcap-file). Add it to your `Cargo.toml`: ```toml [dependencies] pcap-file = "2.0.0-rc1" ``` ## Examples ### PcapReader ```rust,no_run use std::fs::File; use pcap_file::pcap::PcapReader; let file_in = File::open("test.pcap").expect("Error opening file"); let mut pcap_reader = PcapReader::new(file_in).unwrap(); // Read test.pcap while let Some(pkt) = pcap_reader.next_packet() { //Check if there is no error let pkt = pkt.unwrap(); //Do something } ``` ### PcapNgReader ```rust,no_run use std::fs::File; use pcap_file::pcapng::PcapNgReader; let file_in = File::open("test.pcapng").expect("Error opening file"); let mut pcapng_reader = PcapNgReader::new(file_in).unwrap(); // Read test.pcapng while let Some(block) = pcapng_reader.next_block() { // Check if there is no error let block = block.unwrap(); // Do something } ``` ## Fuzzing Currently there are 4 crude harnesses to check that the parser won't panic in any situation. To start fuzzing you must install `cargo-fuzz` with the command: ```bash $ cargo install cargo-fuzz ``` And then, in the root of the repository, you can run the harnesses as: ```bash $ cargo fuzz run pcap_reader $ cargo fuzz run pcap_ng_reader $ cargo fuzz run pcap_parser $ cargo fuzz run pcap_ng_parser ``` Keep in mind that libfuzzer by default uses only one core, so you can either run all the harnesses in different terminals, or you can pass the `-jobs` and `-workers` attributes. More info can be found in its documentation [here](https://llvm.org/docs/LibFuzzer.html). To get better crash reports add to you rust flags: `-Zsanitizer=address`. E.g. ```bash RUSTFLAGS="-Zsanitizer=address" cargo fuzz run pcap_reader ``` ## License Licensed under MIT. ## Disclaimer To test the library I used the excellent PcapNg testing suite provided by [hadrielk](https://github.com/hadrielk/pcapng-test-generator). pcap-file-2.0.0/benches/benches.rs000064400000000000000000000067671046102023000150670ustar 00000000000000use criterion::{criterion_group, criterion_main, Criterion}; use pcap_file::pcap::{PcapParser, PcapReader}; use pcap_file::pcapng::{PcapNgParser, PcapNgReader}; use pcap_file::PcapError; /// Bench and compare Pcap readers and parsers pub fn pcap(c: &mut Criterion) { let pcap = std::fs::read("benches/bench.pcap").unwrap(); let mut group = c.benchmark_group("Pcap"); group.throughput(criterion::Throughput::Bytes(pcap.len() as u64)); group.bench_function("Parser", |b| { b.iter(|| { let (mut src, parser) = PcapParser::new(&pcap).unwrap(); loop { match parser.next_packet(src) { Ok((rem, _)) => src = rem, Err(PcapError::IncompleteBuffer) => break, Err(_) => panic!(), } } }) }); group.bench_function("ParserRaw", |b| { b.iter(|| { let (mut src, parser) = PcapParser::new(&pcap).unwrap(); loop { match parser.next_raw_packet(src) { Ok((rem, _)) => src = rem, Err(PcapError::IncompleteBuffer) => break, Err(_) => panic!(), } } }) }); group.bench_function("Reader", |b| { b.iter(|| { let mut src = &pcap[..]; let mut reader = PcapReader::new(&mut src).unwrap(); while let Some(pkt) = reader.next_packet() { pkt.unwrap(); } }) }); group.bench_function("ReaderRaw", |b| { b.iter(|| { let mut src = &pcap[..]; let mut reader = PcapReader::new(&mut src).unwrap(); while let Some(pkt) = reader.next_raw_packet() { pkt.unwrap(); } }) }); } /// Bench and compare PcapNg readers and parsers pub fn pcapng(c: &mut Criterion) { let pcapng = std::fs::read("benches/bench.pcapng").unwrap(); let mut group = c.benchmark_group("PcapNg"); group.throughput(criterion::Throughput::Bytes(pcapng.len() as u64)); group.bench_function("Parser", |b| { b.iter(|| { let (mut src, mut parser) = PcapNgParser::new(&pcapng).unwrap(); loop { match parser.next_block(src) { Ok((rem, _)) => src = rem, Err(PcapError::IncompleteBuffer) => break, Err(_) => panic!(), } } }) }); group.bench_function("ParserRaw", |b| { b.iter(|| { let (mut src, mut parser) = PcapNgParser::new(&pcapng).unwrap(); loop { match parser.next_raw_block(src) { Ok((rem, _)) => src = rem, Err(PcapError::IncompleteBuffer) => break, Err(_) => panic!(), } } }) }); group.bench_function("Reader", |b| { b.iter(|| { let mut src = &pcapng[..]; let mut reader = PcapNgReader::new(&mut src).unwrap(); while let Some(pkt) = reader.next_block() { pkt.unwrap(); } }) }); group.bench_function("ReaderRaw", |b| { b.iter(|| { let mut src = &pcapng[..]; let mut reader = PcapNgReader::new(&mut src).unwrap(); while let Some(pkt) = reader.next_raw_block() { pkt.unwrap(); } }) }); } criterion_group!(benches, pcap, pcapng); criterion_main!(benches); pcap-file-2.0.0/rustfmt.toml000064400000000000000000000032741046102023000140720ustar 00000000000000array_width = 120 attr_fn_like_width = 120 binop_separator = "Front" blank_lines_lower_bound = 0 blank_lines_upper_bound = 2 brace_style = "SameLineWhere" chain_width = 100 color = "Always" combine_control_expr = false comment_width = 140 condense_wildcard_suffixes = true control_brace_style = "ClosingNextLine" empty_item_single_line = true enum_discrim_align_threshold = 0 fn_args_layout = "Tall" fn_call_width = 120 fn_single_line = false force_explicit_abi = true force_multiline_blocks = false format_code_in_doc_comments = true format_generated_files = false format_macro_matchers = true format_macro_bodies = true format_strings = true hard_tabs = false hex_literal_case = "Upper" imports_indent = "Block" imports_layout = "Mixed" indent_style = "Block" inline_attribute_width = 0 match_arm_blocks = true match_arm_leading_pipes = "Never" match_block_trailing_comma = true max_width = 140 merge_derives = true imports_granularity = "Module" newline_style = "Unix" normalize_comments = false normalize_doc_attributes = true overflow_delimited_expr = false remove_nested_parens = true reorder_impl_items = true reorder_imports = true group_imports = "StdExternalCrate" reorder_modules = true skip_children = false single_line_if_else_max_width = 80 space_after_colon = true space_before_colon = false spaces_around_ranges = false struct_field_align_threshold = 0 struct_lit_single_line = true struct_lit_width = 80 struct_variant_width = 80 tab_spaces = 4 trailing_comma = "Vertical" trailing_semicolon = true type_punctuation_density = "Wide" unstable_features = true use_field_init_shorthand = true use_small_heuristics = "Off" use_try_shorthand = true version = "Two" where_single_line = false wrap_comments = true pcap-file-2.0.0/src/common.rs000064400000000000000000000540751046102023000141230ustar 00000000000000use byteorder_slice::{BigEndian, ByteOrder, LittleEndian}; /// Timestamp resolution of the pcap #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum TsResolution { /// Microsecond resolution MicroSecond, /// Nanosecond resolution NanoSecond, } /// Endianness of the pcap #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum Endianness { /// Big endian Big, /// Little endian Little, } impl Endianness { /// True if LitlleEndian pub fn is_little(self) -> bool { match self { Endianness::Big => false, Endianness::Little => true, } } /// True if BigEndian pub fn is_big(self) -> bool { match self { Endianness::Big => true, Endianness::Little => false, } } /// Return the endianness of the given ByteOrder pub fn from_byteorder() -> Self { if B::read_u32(&[0, 0, 0, 1]) == 1 { Endianness::Big } else { Endianness::Little } } /// Return the native endianness of the system pub fn native() -> Self { #[cfg(target_endian = "big")] return Endianness::Big; #[cfg(target_endian = "little")] return Endianness::Little; } } pub(crate) trait RuntimeByteorder: ByteOrder { fn endianness() -> Endianness; } impl RuntimeByteorder for BigEndian { fn endianness() -> Endianness { Endianness::Big } } impl RuntimeByteorder for LittleEndian { fn endianness() -> Endianness { Endianness::Little } } /// Data link type /// /// The link-layer header type specifies the first protocol of the packet. /// /// See [http://www.tcpdump.org/linktypes.html](http://www.tcpdump.org/linktypes.html) #[allow(non_camel_case_types)] #[allow(missing_docs)] #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum DataLink { NULL, ETHERNET, EXP_ETHERNET, AX25, PRONET, CHAOS, IEEE802_5, ARCNET_BSD, SLIP, PPP, FDDI, PPP_HDLC, PPP_ETHER, SYMANTEC_FIREWALL, ATM_RFC1483, RAW, SLIP_BSDOS, PPP_BSDOS, MATCHING_MIN, C_HDLC, IEEE802_11, ATM_CLIP, FRELAY, LOOP, ENC, LANE8023, HIPPI, NETBSD_HDLC, LINUX_SLL, LTALK, ECONET, IPFILTER, PFLOG, CISCO_IOS, IEEE802_11_PRISM, IEEE802_11_AIRONET, HHDLC, IP_OVER_FC, SUNATM, RIO, PCI_EXP, AURORA, IEEE802_11_RADIOTAP, TZSP, ARCNET_LINUX, JUNIPER_MLPPP, JUNIPER_MLFR, JUNIPER_ES, JUNIPER_GGSN, JUNIPER_MFR, JUNIPER_ATM2, JUNIPER_SERVICES, JUNIPER_ATM1, APPLE_IP_OVER_IEEE1394, MTP2_WITH_PHDR, MTP2, MTP3, SCCP, DOCSIS, LINUX_IRDA, IBM_SP, IBM_SN, USER0, USER1, USER2, USER3, USER4, USER5, USER6, USER7, USER8, USER9, USER10, USER11, USER12, USER13, USER14, USER15, IEEE802_11_AVS, JUNIPER_MONITOR, BACNET_MS_TP, PPP_PPPD, JUNIPER_PPPOE, JUNIPER_PPPOE_ATM, GPRS_LLC, GPF_T, GPF_F, GCOM_T1E1, GCOM_SERIAL, JUNIPER_PIC_PEER, ERF_ETH, ERF_POS, LINUX_LAPD, JUNIPER_ETHER, JUNIPER_PPP, JUNIPER_FRELAY, JUNIPER_CHDLC, MFR, JUNIPER_VP, A429, A653_ICM, USB_FREEBSD, BLUETOOTH_HCI_H4, IEEE802_16_MAC_CPS, USB_LINUX, CAN20B, IEEE802_15_4_LINUX, PPI, IEEE802_16_MAC_CPS_RADIO, JUNIPER_ISM, IEEE802_15_4, SITA, ERF, RAIF1, IPMB_KONTRON, JUNIPER_ST, BLUETOOTH_HCI_H4_WITH_PHDR, AX25_KISS, LAPD, PPP_WITH_DIR, C_HDLC_WITH_DIR, FRELAY_WITH_DIR, LAPB_WITH_DIR, IPMB_LINUX, FLEXRAY, MOST, LIN, X2E_SERIAL, X2E_XORAYA, IEEE802_15_4_NONASK_PHY, LINUX_EVDEV, GSMTAP_UM, GSMTAP_ABIS, MPLS, USB_LINUX_MMAPPED, DECT, AOS, WIHART, FC_2, FC_2_WITH_FRAME_DELIMS, IPNET, CAN_SOCKETCAN, IPV4, IPV6, IEEE802_15_4_NOFCS, DBUS, JUNIPER_VS, JUNIPER_SRX_E2E, JUNIPER_FIBRECHANNEL, DVB_CI, MUX27010, STANAG_5066_D_PDU, JUNIPER_ATM_CEMIC, NFLOG, NETANALYZER, NETANALYZER_TRANSPARENT, IPOIB, MPEG_2_TS, NG40, NFC_LLCP, PFSYNC, INFINIBAND, SCTP, USBPCAP, RTAC_SERIAL, BLUETOOTH_LE_LL, WIRESHARK_UPPER_PDU, NETLINK, BLUETOOTH_LINUX_MONITOR, BLUETOOTH_BREDR_BB, BLUETOOTH_LE_LL_WITH_PHDR, PROFIBUS_DL, PKTAP, EPON, IPMI_HPM_2, ZWAVE_R1_R2, ZWAVE_R3, WATTSTOPPER_DLM, ISO_14443, RDS, USB_DARWIN, OPENFLOW, SDLC, TI_LLN_SNIFFER, LORATAP, VSOCK, NORDIC_BLE, DOCSIS31_XRA31, ETHERNET_MPACKET, DISPLAYPORT_AUX, LINUX_SLL2, SERCOS_MONITOR, OPENVIZSLA, EBHSCR, VPP_DISPATCH, DSA_TAG_BRCM, DSA_TAG_BRCM_PREPEND, IEEE802_15_4_TAP, DSA_TAG_DSA, DSA_TAG_EDSA, ELEE, Z_WAVE_SERIAL, USB_2_0, ATSC_ALP, ETW, NETANALYZER_NG, ZBOSS_NCP, USB_2_0_LOW_SPEED, USB_2_0_FULL_SPEED, USB_2_0_HIGH_SPEED, AUERSWALD_LOG, Unknown(u32), } impl From for DataLink { fn from(n: u32) -> DataLink { match n { 0 => DataLink::NULL, 1 => DataLink::ETHERNET, 2 => DataLink::EXP_ETHERNET, 3 => DataLink::AX25, 4 => DataLink::PRONET, 5 => DataLink::CHAOS, 6 => DataLink::IEEE802_5, 7 => DataLink::ARCNET_BSD, 8 => DataLink::SLIP, 9 => DataLink::PPP, 10 => DataLink::FDDI, 50 => DataLink::PPP_HDLC, 51 => DataLink::PPP_ETHER, 99 => DataLink::SYMANTEC_FIREWALL, 100 => DataLink::ATM_RFC1483, 101 => DataLink::RAW, 102 => DataLink::SLIP_BSDOS, 103 => DataLink::PPP_BSDOS, 104 => DataLink::C_HDLC, 105 => DataLink::IEEE802_11, 106 => DataLink::ATM_CLIP, 107 => DataLink::FRELAY, 108 => DataLink::LOOP, 109 => DataLink::ENC, 110 => DataLink::LANE8023, 111 => DataLink::HIPPI, 112 => DataLink::NETBSD_HDLC, 113 => DataLink::LINUX_SLL, 114 => DataLink::LTALK, 115 => DataLink::ECONET, 116 => DataLink::IPFILTER, 117 => DataLink::PFLOG, 118 => DataLink::CISCO_IOS, 119 => DataLink::IEEE802_11_PRISM, 120 => DataLink::IEEE802_11_AIRONET, 121 => DataLink::HHDLC, 122 => DataLink::IP_OVER_FC, 123 => DataLink::SUNATM, 124 => DataLink::RIO, 125 => DataLink::PCI_EXP, 126 => DataLink::AURORA, 127 => DataLink::IEEE802_11_RADIOTAP, 128 => DataLink::TZSP, 129 => DataLink::ARCNET_LINUX, 130 => DataLink::JUNIPER_MLPPP, 131 => DataLink::JUNIPER_MLFR, 132 => DataLink::JUNIPER_ES, 133 => DataLink::JUNIPER_GGSN, 134 => DataLink::JUNIPER_MFR, 135 => DataLink::JUNIPER_ATM2, 136 => DataLink::JUNIPER_SERVICES, 137 => DataLink::JUNIPER_ATM1, 138 => DataLink::APPLE_IP_OVER_IEEE1394, 139 => DataLink::MTP2_WITH_PHDR, 140 => DataLink::MTP2, 141 => DataLink::MTP3, 142 => DataLink::SCCP, 143 => DataLink::DOCSIS, 144 => DataLink::LINUX_IRDA, 145 => DataLink::IBM_SP, 146 => DataLink::IBM_SN, 147 => DataLink::USER0, 148 => DataLink::USER1, 149 => DataLink::USER2, 150 => DataLink::USER3, 151 => DataLink::USER4, 152 => DataLink::USER5, 153 => DataLink::USER6, 154 => DataLink::USER7, 155 => DataLink::USER8, 156 => DataLink::USER9, 157 => DataLink::USER10, 158 => DataLink::USER11, 159 => DataLink::USER12, 160 => DataLink::USER13, 161 => DataLink::USER14, 162 => DataLink::USER15, 163 => DataLink::IEEE802_11_AVS, 164 => DataLink::JUNIPER_MONITOR, 165 => DataLink::BACNET_MS_TP, 166 => DataLink::PPP_PPPD, 167 => DataLink::JUNIPER_PPPOE, 168 => DataLink::JUNIPER_PPPOE_ATM, 169 => DataLink::GPRS_LLC, 170 => DataLink::GPF_T, 171 => DataLink::GPF_F, 172 => DataLink::GCOM_T1E1, 173 => DataLink::GCOM_SERIAL, 174 => DataLink::JUNIPER_PIC_PEER, 175 => DataLink::ERF_ETH, 176 => DataLink::ERF_POS, 177 => DataLink::LINUX_LAPD, 178 => DataLink::JUNIPER_ETHER, 179 => DataLink::JUNIPER_PPP, 180 => DataLink::JUNIPER_FRELAY, 181 => DataLink::JUNIPER_CHDLC, 182 => DataLink::MFR, 183 => DataLink::JUNIPER_VP, 184 => DataLink::A429, 185 => DataLink::A653_ICM, 186 => DataLink::USB_FREEBSD, 187 => DataLink::BLUETOOTH_HCI_H4, 188 => DataLink::IEEE802_16_MAC_CPS, 189 => DataLink::USB_LINUX, 190 => DataLink::CAN20B, 191 => DataLink::IEEE802_15_4_LINUX, 192 => DataLink::PPI, 193 => DataLink::IEEE802_16_MAC_CPS_RADIO, 194 => DataLink::JUNIPER_ISM, 195 => DataLink::IEEE802_15_4, 196 => DataLink::SITA, 197 => DataLink::ERF, 198 => DataLink::RAIF1, 199 => DataLink::IPMB_KONTRON, 200 => DataLink::JUNIPER_ST, 201 => DataLink::BLUETOOTH_HCI_H4_WITH_PHDR, 202 => DataLink::AX25_KISS, 203 => DataLink::LAPD, 204 => DataLink::PPP_WITH_DIR, 205 => DataLink::C_HDLC_WITH_DIR, 206 => DataLink::FRELAY_WITH_DIR, 207 => DataLink::LAPB_WITH_DIR, 209 => DataLink::IPMB_LINUX, 210 => DataLink::FLEXRAY, 211 => DataLink::MOST, 212 => DataLink::LIN, 213 => DataLink::X2E_SERIAL, 214 => DataLink::X2E_XORAYA, 215 => DataLink::IEEE802_15_4_NONASK_PHY, 216 => DataLink::LINUX_EVDEV, 217 => DataLink::GSMTAP_UM, 218 => DataLink::GSMTAP_ABIS, 219 => DataLink::MPLS, 220 => DataLink::USB_LINUX_MMAPPED, 221 => DataLink::DECT, 222 => DataLink::AOS, 223 => DataLink::WIHART, 224 => DataLink::FC_2, 225 => DataLink::FC_2_WITH_FRAME_DELIMS, 226 => DataLink::IPNET, 227 => DataLink::CAN_SOCKETCAN, 228 => DataLink::IPV4, 229 => DataLink::IPV6, 230 => DataLink::IEEE802_15_4_NOFCS, 231 => DataLink::DBUS, 232 => DataLink::JUNIPER_VS, 233 => DataLink::JUNIPER_SRX_E2E, 234 => DataLink::JUNIPER_FIBRECHANNEL, 235 => DataLink::DVB_CI, 236 => DataLink::MUX27010, 237 => DataLink::STANAG_5066_D_PDU, 238 => DataLink::JUNIPER_ATM_CEMIC, 239 => DataLink::NFLOG, 240 => DataLink::NETANALYZER, 241 => DataLink::NETANALYZER_TRANSPARENT, 242 => DataLink::IPOIB, 243 => DataLink::MPEG_2_TS, 244 => DataLink::NG40, 245 => DataLink::NFC_LLCP, 246 => DataLink::PFSYNC, 247 => DataLink::INFINIBAND, 248 => DataLink::SCTP, 249 => DataLink::USBPCAP, 250 => DataLink::RTAC_SERIAL, 251 => DataLink::BLUETOOTH_LE_LL, 252 => DataLink::WIRESHARK_UPPER_PDU, 253 => DataLink::NETLINK, 254 => DataLink::BLUETOOTH_LINUX_MONITOR, 255 => DataLink::BLUETOOTH_BREDR_BB, 256 => DataLink::BLUETOOTH_LE_LL_WITH_PHDR, 257 => DataLink::PROFIBUS_DL, 258 => DataLink::PKTAP, 259 => DataLink::EPON, 260 => DataLink::IPMI_HPM_2, 261 => DataLink::ZWAVE_R1_R2, 262 => DataLink::ZWAVE_R3, 263 => DataLink::WATTSTOPPER_DLM, 264 => DataLink::ISO_14443, 265 => DataLink::RDS, 266 => DataLink::USB_DARWIN, 267 => DataLink::OPENFLOW, 268 => DataLink::SDLC, 269 => DataLink::TI_LLN_SNIFFER, 270 => DataLink::LORATAP, 271 => DataLink::VSOCK, 272 => DataLink::NORDIC_BLE, 273 => DataLink::DOCSIS31_XRA31, 274 => DataLink::ETHERNET_MPACKET, 275 => DataLink::DISPLAYPORT_AUX, 276 => DataLink::LINUX_SLL2, 277 => DataLink::SERCOS_MONITOR, 278 => DataLink::OPENVIZSLA, 279 => DataLink::EBHSCR, 280 => DataLink::VPP_DISPATCH, 281 => DataLink::DSA_TAG_BRCM, 282 => DataLink::DSA_TAG_BRCM_PREPEND, 283 => DataLink::IEEE802_15_4_TAP, 284 => DataLink::DSA_TAG_DSA, 285 => DataLink::DSA_TAG_EDSA, 286 => DataLink::ELEE, 287 => DataLink::Z_WAVE_SERIAL, 288 => DataLink::USB_2_0, 289 => DataLink::ATSC_ALP, 290 => DataLink::ETW, 291 => DataLink::NETANALYZER_NG, 292 => DataLink::ZBOSS_NCP, 293 => DataLink::USB_2_0_LOW_SPEED, 294 => DataLink::USB_2_0_FULL_SPEED, 295 => DataLink::USB_2_0_HIGH_SPEED, 296 => DataLink::AUERSWALD_LOG, _ => DataLink::Unknown(n), } } } impl From for u32 { fn from(link: DataLink) -> u32 { match link { DataLink::NULL => 0, DataLink::ETHERNET => 1, DataLink::EXP_ETHERNET => 2, DataLink::AX25 => 3, DataLink::PRONET => 4, DataLink::CHAOS => 5, DataLink::IEEE802_5 => 6, DataLink::ARCNET_BSD => 7, DataLink::SLIP => 8, DataLink::PPP => 9, DataLink::FDDI => 10, DataLink::PPP_HDLC => 50, DataLink::PPP_ETHER => 51, DataLink::SYMANTEC_FIREWALL => 99, DataLink::ATM_RFC1483 => 100, DataLink::RAW => 101, DataLink::SLIP_BSDOS => 102, DataLink::PPP_BSDOS => 103, DataLink::MATCHING_MIN => 104, DataLink::C_HDLC => 104, DataLink::IEEE802_11 => 105, DataLink::ATM_CLIP => 106, DataLink::FRELAY => 107, DataLink::LOOP => 108, DataLink::ENC => 109, DataLink::LANE8023 => 110, DataLink::HIPPI => 111, DataLink::NETBSD_HDLC => 112, DataLink::LINUX_SLL => 113, DataLink::LTALK => 114, DataLink::ECONET => 115, DataLink::IPFILTER => 116, DataLink::PFLOG => 117, DataLink::CISCO_IOS => 118, DataLink::IEEE802_11_PRISM => 119, DataLink::IEEE802_11_AIRONET => 120, DataLink::HHDLC => 121, DataLink::IP_OVER_FC => 122, DataLink::SUNATM => 123, DataLink::RIO => 124, DataLink::PCI_EXP => 125, DataLink::AURORA => 126, DataLink::IEEE802_11_RADIOTAP => 127, DataLink::TZSP => 128, DataLink::ARCNET_LINUX => 129, DataLink::JUNIPER_MLPPP => 130, DataLink::JUNIPER_MLFR => 131, DataLink::JUNIPER_ES => 132, DataLink::JUNIPER_GGSN => 133, DataLink::JUNIPER_MFR => 134, DataLink::JUNIPER_ATM2 => 135, DataLink::JUNIPER_SERVICES => 136, DataLink::JUNIPER_ATM1 => 137, DataLink::APPLE_IP_OVER_IEEE1394 => 138, DataLink::MTP2_WITH_PHDR => 139, DataLink::MTP2 => 140, DataLink::MTP3 => 141, DataLink::SCCP => 142, DataLink::DOCSIS => 143, DataLink::LINUX_IRDA => 144, DataLink::IBM_SP => 145, DataLink::IBM_SN => 146, DataLink::USER0 => 147, DataLink::USER1 => 148, DataLink::USER2 => 149, DataLink::USER3 => 150, DataLink::USER4 => 151, DataLink::USER5 => 152, DataLink::USER6 => 153, DataLink::USER7 => 154, DataLink::USER8 => 155, DataLink::USER9 => 156, DataLink::USER10 => 157, DataLink::USER11 => 158, DataLink::USER12 => 159, DataLink::USER13 => 160, DataLink::USER14 => 161, DataLink::USER15 => 162, DataLink::IEEE802_11_AVS => 163, DataLink::JUNIPER_MONITOR => 164, DataLink::BACNET_MS_TP => 165, DataLink::PPP_PPPD => 166, DataLink::JUNIPER_PPPOE => 167, DataLink::JUNIPER_PPPOE_ATM => 168, DataLink::GPRS_LLC => 169, DataLink::GPF_T => 170, DataLink::GPF_F => 171, DataLink::GCOM_T1E1 => 172, DataLink::GCOM_SERIAL => 173, DataLink::JUNIPER_PIC_PEER => 174, DataLink::ERF_ETH => 175, DataLink::ERF_POS => 176, DataLink::LINUX_LAPD => 177, DataLink::JUNIPER_ETHER => 178, DataLink::JUNIPER_PPP => 179, DataLink::JUNIPER_FRELAY => 180, DataLink::JUNIPER_CHDLC => 181, DataLink::MFR => 182, DataLink::JUNIPER_VP => 183, DataLink::A429 => 184, DataLink::A653_ICM => 185, DataLink::USB_FREEBSD => 186, DataLink::BLUETOOTH_HCI_H4 => 187, DataLink::IEEE802_16_MAC_CPS => 188, DataLink::USB_LINUX => 189, DataLink::CAN20B => 190, DataLink::IEEE802_15_4_LINUX => 191, DataLink::PPI => 192, DataLink::IEEE802_16_MAC_CPS_RADIO => 193, DataLink::JUNIPER_ISM => 194, DataLink::IEEE802_15_4 => 195, DataLink::SITA => 196, DataLink::ERF => 197, DataLink::RAIF1 => 198, DataLink::IPMB_KONTRON => 199, DataLink::JUNIPER_ST => 200, DataLink::BLUETOOTH_HCI_H4_WITH_PHDR => 201, DataLink::AX25_KISS => 202, DataLink::LAPD => 203, DataLink::PPP_WITH_DIR => 204, DataLink::C_HDLC_WITH_DIR => 205, DataLink::FRELAY_WITH_DIR => 206, DataLink::LAPB_WITH_DIR => 207, DataLink::IPMB_LINUX => 209, DataLink::FLEXRAY => 210, DataLink::MOST => 211, DataLink::LIN => 212, DataLink::X2E_SERIAL => 213, DataLink::X2E_XORAYA => 214, DataLink::IEEE802_15_4_NONASK_PHY => 215, DataLink::LINUX_EVDEV => 216, DataLink::GSMTAP_UM => 217, DataLink::GSMTAP_ABIS => 218, DataLink::MPLS => 219, DataLink::USB_LINUX_MMAPPED => 220, DataLink::DECT => 221, DataLink::AOS => 222, DataLink::WIHART => 223, DataLink::FC_2 => 224, DataLink::FC_2_WITH_FRAME_DELIMS => 225, DataLink::IPNET => 226, DataLink::CAN_SOCKETCAN => 227, DataLink::IPV4 => 228, DataLink::IPV6 => 229, DataLink::IEEE802_15_4_NOFCS => 230, DataLink::DBUS => 231, DataLink::JUNIPER_VS => 232, DataLink::JUNIPER_SRX_E2E => 233, DataLink::JUNIPER_FIBRECHANNEL => 234, DataLink::DVB_CI => 235, DataLink::MUX27010 => 236, DataLink::STANAG_5066_D_PDU => 237, DataLink::JUNIPER_ATM_CEMIC => 238, DataLink::NFLOG => 239, DataLink::NETANALYZER => 240, DataLink::NETANALYZER_TRANSPARENT => 241, DataLink::IPOIB => 242, DataLink::MPEG_2_TS => 243, DataLink::NG40 => 244, DataLink::NFC_LLCP => 245, DataLink::PFSYNC => 246, DataLink::INFINIBAND => 247, DataLink::SCTP => 248, DataLink::USBPCAP => 249, DataLink::RTAC_SERIAL => 250, DataLink::BLUETOOTH_LE_LL => 251, DataLink::WIRESHARK_UPPER_PDU => 252, DataLink::NETLINK => 253, DataLink::BLUETOOTH_LINUX_MONITOR => 254, DataLink::BLUETOOTH_BREDR_BB => 255, DataLink::BLUETOOTH_LE_LL_WITH_PHDR => 256, DataLink::PROFIBUS_DL => 257, DataLink::PKTAP => 258, DataLink::EPON => 259, DataLink::IPMI_HPM_2 => 260, DataLink::ZWAVE_R1_R2 => 261, DataLink::ZWAVE_R3 => 262, DataLink::WATTSTOPPER_DLM => 263, DataLink::ISO_14443 => 264, DataLink::RDS => 265, DataLink::USB_DARWIN => 266, DataLink::OPENFLOW => 267, DataLink::SDLC => 268, DataLink::TI_LLN_SNIFFER => 269, DataLink::LORATAP => 270, DataLink::VSOCK => 271, DataLink::NORDIC_BLE => 272, DataLink::DOCSIS31_XRA31 => 273, DataLink::ETHERNET_MPACKET => 274, DataLink::DISPLAYPORT_AUX => 275, DataLink::LINUX_SLL2 => 276, DataLink::SERCOS_MONITOR => 277, DataLink::OPENVIZSLA => 278, DataLink::EBHSCR => 279, DataLink::VPP_DISPATCH => 280, DataLink::DSA_TAG_BRCM => 281, DataLink::DSA_TAG_BRCM_PREPEND => 282, DataLink::IEEE802_15_4_TAP => 283, DataLink::DSA_TAG_DSA => 284, DataLink::DSA_TAG_EDSA => 285, DataLink::ELEE => 286, DataLink::Z_WAVE_SERIAL => 287, DataLink::USB_2_0 => 288, DataLink::ATSC_ALP => 289, DataLink::ETW => 290, DataLink::NETANALYZER_NG => 291, DataLink::ZBOSS_NCP => 292, DataLink::USB_2_0_LOW_SPEED => 293, DataLink::USB_2_0_FULL_SPEED => 294, DataLink::USB_2_0_HIGH_SPEED => 295, DataLink::AUERSWALD_LOG => 296, DataLink::Unknown(n) => n, } } } pcap-file-2.0.0/src/errors.rs000064400000000000000000000021501046102023000141320ustar 00000000000000use thiserror::Error; /// Result type for the pcap/pcapng parsing pub type PcapResult = Result; /// Error type for the pcap/pcapng parsing #[derive(Error, Debug)] pub enum PcapError { /// Buffer too small #[error("Need more bytes")] IncompleteBuffer, /// Generic IO error #[error("Error reading bytes")] IoError(#[source] std::io::Error), /// Invalid field #[error("Invalid field value: {0}")] InvalidField(&'static str), /// UTF8 conversion error #[error("UTF8 error")] Utf8Error(#[source] std::str::Utf8Error), /// From UTF8 conversion error #[error("UTF8 error")] FromUtf8Error(#[source] std::string::FromUtf8Error), /// Invalid interface ID (only for Pcap NG) #[error("No corresponding interface id: {0}")] InvalidInterfaceId(u32), } impl From for PcapError { fn from(err: std::str::Utf8Error) -> Self { PcapError::Utf8Error(err) } } impl From for PcapError { fn from(err: std::string::FromUtf8Error) -> Self { PcapError::FromUtf8Error(err) } } pcap-file-2.0.0/src/lib.rs000064400000000000000000000013421046102023000133660ustar 00000000000000#![allow(clippy::unreadable_literal)] #![deny(missing_docs)] //! Provides parsers, readers and writers for Pcap and PcapNg files. //! //! For Pcap files see the [`pcap`] module, especially [`PcapParser`](pcap::PcapParser), //! [`PcapReader`](pcap::PcapReader) and [`PcapWriter`](pcap::PcapWriter). //! //! For PcapNg files see the [`pcapng`] module, especially [`PcapNgParser`](pcapng::PcapNgParser), //! [`PcapNgReader`](pcapng::PcapNgReader) and [`PcapNgWriter`](pcapng::PcapNgWriter) pub use common::*; pub use errors::*; pub(crate) mod common; pub(crate) mod errors; pub(crate) mod read_buffer; pub mod pcap; pub mod pcapng; #[allow(dead_code)] #[doc = include_str!("../README.md")] fn readme_compile_exemples() {} pcap-file-2.0.0/src/pcap/header.rs000064400000000000000000000117001046102023000147720ustar 00000000000000use std::io::Write; use byteorder_slice::byteorder::WriteBytesExt; use byteorder_slice::result::ReadSlice; use byteorder_slice::{BigEndian, ByteOrder, LittleEndian}; use crate::errors::*; use crate::{DataLink, Endianness, TsResolution}; /// Pcap Global Header #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct PcapHeader { /// Major version number pub version_major: u16, /// Minor version number pub version_minor: u16, /// GMT to local timezone correction, should always be 0 pub ts_correction: i32, /// Timestamp accuracy, should always be 0 pub ts_accuracy: u32, /// Max length of captured packet, typically 65535 pub snaplen: u32, /// DataLink type (first layer in the packet) pub datalink: DataLink, /// Timestamp resolution of the pcap (microsecond or nanosecond) pub ts_resolution: TsResolution, /// Endianness of the pcap (excluding the packet data) pub endianness: Endianness, } impl PcapHeader { /// Creates a new [`PcapHeader`] from a slice of bytes. /// /// Returns an error if the reader doesn't contain a valid pcap /// or if there is a reading error. /// /// [`PcapError::IncompleteBuffer`] indicates that there is not enough data in the buffer. pub fn from_slice(mut slice: &[u8]) -> PcapResult<(&[u8], PcapHeader)> { // Check that slice.len() > PcapHeader length if slice.len() < 24 { return Err(PcapError::IncompleteBuffer); } let magic_number = slice.read_u32::().unwrap(); match magic_number { 0xA1B2C3D4 => return init_pcap_header::(slice, TsResolution::MicroSecond, Endianness::Big), 0xA1B23C4D => return init_pcap_header::(slice, TsResolution::NanoSecond, Endianness::Big), 0xD4C3B2A1 => return init_pcap_header::(slice, TsResolution::MicroSecond, Endianness::Little), 0x4D3CB2A1 => return init_pcap_header::(slice, TsResolution::NanoSecond, Endianness::Little), _ => return Err(PcapError::InvalidField("PcapHeader: wrong magic number")), }; // Inner function used for the initialisation of the PcapHeader. // Must check the srcclength before calling it. fn init_pcap_header( mut src: &[u8], ts_resolution: TsResolution, endianness: Endianness, ) -> PcapResult<(&[u8], PcapHeader)> { let header = PcapHeader { version_major: src.read_u16::().unwrap(), version_minor: src.read_u16::().unwrap(), ts_correction: src.read_i32::().unwrap(), ts_accuracy: src.read_u32::().unwrap(), snaplen: src.read_u32::().unwrap(), datalink: DataLink::from(src.read_u32::().unwrap()), ts_resolution, endianness, }; Ok((src, header)) } } /// Writes a [`PcapHeader`] to a writer. /// /// Uses the endianness of the header. pub fn write_to(&self, writer: &mut W) -> PcapResult { return match self.endianness { Endianness::Big => write_header::<_, BigEndian>(self, writer), Endianness::Little => write_header::<_, LittleEndian>(self, writer), }; fn write_header(header: &PcapHeader, writer: &mut W) -> PcapResult { let magic_number = match header.ts_resolution { TsResolution::MicroSecond => 0xA1B2C3D4, TsResolution::NanoSecond => 0xA1B23C4D, }; writer.write_u32::(magic_number).map_err(PcapError::IoError)?; writer.write_u16::(header.version_major).map_err(PcapError::IoError)?; writer.write_u16::(header.version_minor).map_err(PcapError::IoError)?; writer.write_i32::(header.ts_correction).map_err(PcapError::IoError)?; writer.write_u32::(header.ts_accuracy).map_err(PcapError::IoError)?; writer.write_u32::(header.snaplen).map_err(PcapError::IoError)?; writer.write_u32::(header.datalink.into()).map_err(PcapError::IoError)?; Ok(24) } } } /// Creates a new [`PcapHeader`] with these parameters: /// /// ```rust,ignore /// PcapHeader { /// version_major: 2, /// version_minor: 4, /// ts_correction: 0, /// ts_accuracy: 0, /// snaplen: 65535, /// datalink: DataLink::ETHERNET, /// ts_resolution: TsResolution::MicroSecond, /// endianness: Endianness::Big /// }; /// ``` impl Default for PcapHeader { fn default() -> Self { PcapHeader { version_major: 2, version_minor: 4, ts_correction: 0, ts_accuracy: 0, snaplen: 65535, datalink: DataLink::ETHERNET, ts_resolution: TsResolution::MicroSecond, endianness: Endianness::Big, } } } pcap-file-2.0.0/src/pcap/mod.rs000064400000000000000000000003151046102023000143210ustar 00000000000000//! Contains the Pcap parser, reader and writer mod header; mod packet; mod parser; mod reader; mod writer; pub use header::*; pub use packet::*; pub use parser::*; pub use reader::*; pub use writer::*; pcap-file-2.0.0/src/pcap/packet.rs000064400000000000000000000143271046102023000150210ustar 00000000000000use std::borrow::Cow; use std::io::Write; use std::time::Duration; use byteorder_slice::byteorder::WriteBytesExt; use byteorder_slice::result::ReadSlice; use byteorder_slice::ByteOrder; use derive_into_owned::IntoOwned; use crate::errors::*; use crate::TsResolution; /// Pcap packet. /// /// The payload can be owned or borrowed. #[derive(Clone, Debug, IntoOwned)] pub struct PcapPacket<'a> { /// Timestamp EPOCH of the packet with a nanosecond resolution pub timestamp: Duration, /// Original length of the packet when captured on the wire pub orig_len: u32, /// Payload, owned or borrowed, of the packet pub data: Cow<'a, [u8]>, } impl<'a> PcapPacket<'a> { /// Creates a new borrowed [`PcapPacket`] with the given parameters. pub fn new(timestamp: Duration, orig_len: u32, data: &'a [u8]) -> PcapPacket<'a> { PcapPacket { timestamp, orig_len, data: Cow::Borrowed(data) } } /// Creates a new owned [`PcapPacket`] with the given parameters. pub fn new_owned(timestamp: Duration, orig_len: u32, data: Vec) -> PcapPacket<'static> { PcapPacket { timestamp, orig_len, data: Cow::Owned(data) } } /// Parses a new borrowed [`PcapPacket`] from a slice. pub fn from_slice(slice: &'a [u8], ts_resolution: TsResolution, snap_len: u32) -> PcapResult<(&'a [u8], PcapPacket<'a>)> { let (rem, raw_packet) = RawPcapPacket::from_slice::(slice)?; let s = Self::try_from_raw_packet(raw_packet, ts_resolution, snap_len)?; Ok((rem, s)) } /// Writes a [`PcapPacket`] to a writer. pub fn write_to(&self, writer: &mut W, ts_resolution: TsResolution, snap_len: u32) -> PcapResult { // Transforms PcapPacket::ts into ts_sec and ts_frac // let ts_sec = self .timestamp .as_secs() .try_into() .map_err(|_| PcapError::InvalidField("PcapPacket: timestamp_secs > u32::MAX"))?; let mut ts_frac = self.timestamp.subsec_nanos(); if ts_resolution == TsResolution::MicroSecond { ts_frac /= 1000; } // Validate the packet length // let incl_len = self.data.len().try_into().map_err(|_| PcapError::InvalidField("PcapPacket: incl_len > u32::MAX"))?; let orig_len = self.orig_len; if incl_len > snap_len { return Err(PcapError::InvalidField("PcapPacket: incl_len > snap_len")); } if incl_len > orig_len { return Err(PcapError::InvalidField("PcapPacket: incl_len > orig_len")); } let raw_packet = RawPcapPacket { ts_sec, ts_frac, incl_len, orig_len, data: Cow::Borrowed(&self.data[..]) }; raw_packet.write_to::<_, B>(writer) } /// Tries to create a [`PcapPacket`] from a [`RawPcapPacket`]. pub fn try_from_raw_packet(raw: RawPcapPacket<'a>, ts_resolution: TsResolution, snap_len: u32) -> PcapResult { // Validate timestamps // let ts_sec = raw.ts_sec; let mut ts_nsec = raw.ts_frac; if ts_resolution == TsResolution::MicroSecond { ts_nsec = ts_nsec.checked_mul(1000).ok_or(PcapError::InvalidField("PacketHeader ts_nanosecond is invalid"))?; } if ts_nsec >= 1_000_000_000 { return Err(PcapError::InvalidField("PacketHeader ts_nanosecond >= 1_000_000_000")); } // Validate lengths // let incl_len = raw.incl_len; let orig_len = raw.orig_len; if incl_len > snap_len { return Err(PcapError::InvalidField("PacketHeader incl_len > snap_len")); } if orig_len > snap_len { return Err(PcapError::InvalidField("PacketHeader orig_len > snap_len")); } if incl_len > orig_len { return Err(PcapError::InvalidField("PacketHeader incl_len > orig_len")); } Ok(PcapPacket { timestamp: Duration::new(ts_sec as u64, ts_nsec), orig_len, data: raw.data }) } } /// Raw Pcap packet with its header and data. /// The fields of the packet are not validated. /// The payload can be owned or borrowed. #[derive(Clone, Debug, IntoOwned)] pub struct RawPcapPacket<'a> { /// Timestamp in seconds pub ts_sec: u32, /// Nanosecond or microsecond part of the timestamp pub ts_frac: u32, /// Number of octets of the packet saved in file pub incl_len: u32, /// Original length of the packet on the wire pub orig_len: u32, /// Payload, owned or borrowed, of the packet pub data: Cow<'a, [u8]>, } impl<'a> RawPcapPacket<'a> { /// Parses a new borrowed [`RawPcapPacket`] from a slice. pub fn from_slice(mut slice: &'a [u8]) -> PcapResult<(&'a [u8], Self)> { // Check header length if slice.len() < 16 { return Err(PcapError::IncompleteBuffer); } // Read packet header // // Can unwrap because the length check is done before let ts_sec = slice.read_u32::().unwrap(); let ts_frac = slice.read_u32::().unwrap(); let incl_len = slice.read_u32::().unwrap(); let orig_len = slice.read_u32::().unwrap(); let pkt_len = incl_len as usize; if slice.len() < pkt_len { return Err(PcapError::IncompleteBuffer); } let packet = RawPcapPacket { ts_sec, ts_frac, incl_len, orig_len, data: Cow::Borrowed(&slice[..pkt_len]) }; let rem = &slice[pkt_len..]; Ok((rem, packet)) } /// Writes a [`RawPcapPacket`] to a writer. /// The fields of the packet are not validated. pub fn write_to(&self, writer: &mut W) -> PcapResult { writer.write_u32::(self.ts_sec).map_err(PcapError::IoError)?; writer.write_u32::(self.ts_frac).map_err(PcapError::IoError)?; writer.write_u32::(self.incl_len).map_err(PcapError::IoError)?; writer.write_u32::(self.orig_len).map_err(PcapError::IoError)?; writer.write_all(&self.data).map_err(PcapError::IoError)?; Ok(16 + self.data.len()) } /// Tries to convert a [`RawPcapPacket`] into a [`PcapPacket`]. pub fn try_into_pcap_packet(self, ts_resolution: TsResolution, snap_len: u32) -> PcapResult> { PcapPacket::try_from_raw_packet(self, ts_resolution, snap_len) } } pcap-file-2.0.0/src/pcap/parser.rs000064400000000000000000000047571046102023000150540ustar 00000000000000use byteorder_slice::{BigEndian, LittleEndian}; use super::RawPcapPacket; use crate::errors::*; use crate::pcap::{PcapHeader, PcapPacket}; use crate::Endianness; /// Parses a Pcap from a slice of bytes. /// /// You can match on [`PcapError::IncompleteBuffer`](crate::errors::PcapError) to known if the parser need more data. /// /// # Example /// ```no_run /// use pcap_file::pcap::PcapParser; /// use pcap_file::PcapError; /// /// let pcap = vec![0_u8; 0]; /// let mut src = &pcap[..]; /// /// // Creates a new parser and parse the pcap header /// let (rem, pcap_parser) = PcapParser::new(&pcap[..]).unwrap(); /// src = rem; /// /// loop { /// match pcap_parser.next_packet(src) { /// Ok((rem, packet)) => { /// // Do something /// /// // Don't forget to update src /// src = rem; /// /// // No more data, if no more incoming either then this is the end of the file /// if rem.is_empty() { /// break; /// } /// }, /// Err(PcapError::IncompleteBuffer) => {}, // Load more data into src /// Err(_) => {}, // Parsing error /// } /// } /// ``` #[derive(Debug)] pub struct PcapParser { header: PcapHeader, } impl PcapParser { /// Creates a new [`PcapParser`]. /// /// Returns the remainder and the parser. pub fn new(slice: &[u8]) -> PcapResult<(&[u8], PcapParser)> { let (slice, header) = PcapHeader::from_slice(slice)?; let parser = PcapParser { header }; Ok((slice, parser)) } /// Returns the remainder and the next [`PcapPacket`]. pub fn next_packet<'a>(&self, slice: &'a [u8]) -> PcapResult<(&'a [u8], PcapPacket<'a>)> { match self.header.endianness { Endianness::Big => PcapPacket::from_slice::(slice, self.header.ts_resolution, self.header.snaplen), Endianness::Little => PcapPacket::from_slice::(slice, self.header.ts_resolution, self.header.snaplen), } } /// Returns the remainder and the next [`RawPcapPacket`]. pub fn next_raw_packet<'a>(&self, slice: &'a [u8]) -> PcapResult<(&'a [u8], RawPcapPacket<'a>)> { match self.header.endianness { Endianness::Big => RawPcapPacket::from_slice::(slice), Endianness::Little => RawPcapPacket::from_slice::(slice), } } /// Returns the header of the pcap file. pub fn header(&self) -> PcapHeader { self.header } } pcap-file-2.0.0/src/pcap/reader.rs000064400000000000000000000050341046102023000150070ustar 00000000000000use std::io::Read; use super::{PcapParser, RawPcapPacket}; use crate::errors::*; use crate::pcap::{PcapHeader, PcapPacket}; use crate::read_buffer::ReadBuffer; /// Reads a pcap from a reader. /// /// # Example /// /// ```rust,no_run /// use std::fs::File; /// /// use pcap_file::pcap::PcapReader; /// /// let file_in = File::open("test.pcap").expect("Error opening file"); /// let mut pcap_reader = PcapReader::new(file_in).unwrap(); /// /// // Read test.pcap /// while let Some(pkt) = pcap_reader.next_packet() { /// //Check if there is no error /// let pkt = pkt.unwrap(); /// /// //Do something /// } /// ``` #[derive(Debug)] pub struct PcapReader { parser: PcapParser, reader: ReadBuffer, } impl PcapReader { /// Creates a new [`PcapReader`] from an existing reader. /// /// This function reads the global pcap header of the file to verify its integrity. /// /// The underlying reader must point to a valid pcap file/stream. /// /// # Errors /// The data stream is not in a valid pcap file format. /// /// The underlying data are not readable. pub fn new(reader: R) -> Result, PcapError> { let mut reader = ReadBuffer::new(reader); let parser = reader.parse_with(PcapParser::new)?; Ok(PcapReader { parser, reader }) } /// Consumes [`Self`], returning the wrapped reader. pub fn into_reader(self) -> R { self.reader.into_inner() } /// Returns the next [`PcapPacket`]. pub fn next_packet(&mut self) -> Option> { match self.reader.has_data_left() { Ok(has_data) => { if has_data { Some(self.reader.parse_with(|src| self.parser.next_packet(src))) } else { None } }, Err(e) => Some(Err(PcapError::IoError(e))), } } /// Returns the next [`RawPcapPacket`]. pub fn next_raw_packet(&mut self) -> Option> { match self.reader.has_data_left() { Ok(has_data) => { if has_data { Some(self.reader.parse_with(|src| self.parser.next_raw_packet(src))) } else { None } }, Err(e) => Some(Err(PcapError::IoError(e))), } } /// Returns the global header of the pcap. pub fn header(&self) -> PcapHeader { self.parser.header() } } pcap-file-2.0.0/src/pcap/writer.rs000064400000000000000000000073171046102023000150670ustar 00000000000000use std::io::Write; use byteorder_slice::{BigEndian, LittleEndian}; use super::RawPcapPacket; use crate::errors::*; use crate::pcap::{PcapHeader, PcapPacket}; use crate::{Endianness, TsResolution}; /// Writes a pcap to a writer. /// /// # Example /// ```rust,no_run /// use std::fs::File; /// /// use pcap_file::pcap::{PcapReader, PcapWriter}; /// /// let file_in = File::open("test.pcap").expect("Error opening file"); /// let mut pcap_reader = PcapReader::new(file_in).unwrap(); /// /// let file_out = File::create("out.pcap").expect("Error creating file out"); /// let mut pcap_writer = PcapWriter::new(file_out).expect("Error writing file"); /// /// // Read test.pcap /// while let Some(pkt) = pcap_reader.next_packet() { /// //Check if there is no error /// let pkt = pkt.unwrap(); /// /// //Write each packet of test.pcap in out.pcap /// pcap_writer.write_packet(&pkt).unwrap(); /// } /// ``` #[derive(Debug)] pub struct PcapWriter { endianness: Endianness, snaplen: u32, ts_resolution: TsResolution, writer: W, } impl PcapWriter { /// Creates a new [`PcapWriter`] from an existing writer. /// /// Defaults to the native endianness of the CPU. /// /// Writes this default global pcap header to the file: /// ```rust, ignore /// PcapHeader { /// version_major: 2, /// version_minor: 4, /// ts_correction: 0, /// ts_accuracy: 0, /// snaplen: 65535, /// datalink: DataLink::ETHERNET, /// ts_resolution: TsResolution::MicroSecond, /// endianness: Endianness::Native /// }; /// ``` /// /// # Errors /// The writer can't be written to. pub fn new(writer: W) -> PcapResult> { let header = PcapHeader { endianness: Endianness::native(), ..Default::default() }; PcapWriter::with_header(writer, header) } /// Creates a new [`PcapWriter`] from an existing writer with a user defined [`PcapHeader`]. /// /// It also writes the pcap header to the file. /// /// # Errors /// The writer can't be written to. pub fn with_header(mut writer: W, header: PcapHeader) -> PcapResult> { header.write_to(&mut writer)?; Ok(PcapWriter { endianness: header.endianness, snaplen: header.snaplen, ts_resolution: header.ts_resolution, writer, }) } /// Consumes [`Self`], returning the wrapped writer. pub fn into_writer(self) -> W { self.writer } /// Writes a [`PcapPacket`]. pub fn write_packet(&mut self, packet: &PcapPacket) -> PcapResult { match self.endianness { Endianness::Big => packet.write_to::<_, BigEndian>(&mut self.writer, self.ts_resolution, self.snaplen), Endianness::Little => packet.write_to::<_, LittleEndian>(&mut self.writer, self.ts_resolution, self.snaplen), } } /// Writes a [`RawPcapPacket`]. pub fn write_raw_packet(&mut self, packet: &RawPcapPacket) -> PcapResult { match self.endianness { Endianness::Big => packet.write_to::<_, BigEndian>(&mut self.writer), Endianness::Little => packet.write_to::<_, LittleEndian>(&mut self.writer), } } /// Returns the endianess used by the writer. pub fn endianness(&self) -> Endianness { self.endianness } /// Returns the snaplen used by the writer, i.e. an unsigned value indicating the maximum number of octets captured /// from each packet. pub fn snaplen(&self) -> u32 { self.snaplen } /// Returns the timestamp resolution of the writer. pub fn ts_resolution(&self) -> TsResolution { self.ts_resolution } } pcap-file-2.0.0/src/pcapng/blocks/block_common.rs000064400000000000000000000312731046102023000200150ustar 00000000000000//! Common block types. use std::borrow::Cow; use std::io::{Result as IoResult, Write}; use byteorder_slice::byteorder::WriteBytesExt; use byteorder_slice::result::ReadSlice; use byteorder_slice::{BigEndian, ByteOrder, LittleEndian}; use derive_into_owned::IntoOwned; use super::enhanced_packet::EnhancedPacketBlock; use super::interface_description::InterfaceDescriptionBlock; use super::interface_statistics::InterfaceStatisticsBlock; use super::name_resolution::NameResolutionBlock; use super::packet::PacketBlock; use super::section_header::SectionHeaderBlock; use super::simple_packet::SimplePacketBlock; use super::systemd_journal_export::SystemdJournalExportBlock; use super::unknown::UnknownBlock; use crate::errors::PcapError; use crate::PcapResult; /// Section header block type pub const SECTION_HEADER_BLOCK: u32 = 0x0A0D0D0A; /// Interface description block type pub const INTERFACE_DESCRIPTION_BLOCK: u32 = 0x00000001; /// Packet block type pub const PACKET_BLOCK: u32 = 0x00000002; /// Simple packet block type pub const SIMPLE_PACKET_BLOCK: u32 = 0x00000003; /// Name resolution block type pub const NAME_RESOLUTION_BLOCK: u32 = 0x00000004; /// Interface statistic block type pub const INTERFACE_STATISTIC_BLOCK: u32 = 0x00000005; /// Enhanced packet block type pub const ENHANCED_PACKET_BLOCK: u32 = 0x00000006; /// Systemd journal export block type pub const SYSTEMD_JOURNAL_EXPORT_BLOCK: u32 = 0x00000009; // 0 1 2 3 // 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | Block Type | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | Block Total Length | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // / Block Body / // / /* variable length, aligned to 32 bits */ / // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | Block Total Length | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ /// PcapNg Block #[derive(Clone, Debug)] pub struct RawBlock<'a> { /// Type field pub type_: u32, /// Initial length field pub initial_len: u32, /// Body of the block pub body: Cow<'a, [u8]>, /// Trailer length field pub trailer_len: u32, } impl<'a> RawBlock<'a> { /// Parses a borrowed [`RawBlock`] from a slice. pub fn from_slice(mut slice: &'a [u8]) -> Result<(&'a [u8], Self), PcapError> { if slice.len() < 12 { return Err(PcapError::IncompleteBuffer); } let type_ = slice.read_u32::().unwrap(); // Special case for the section header because we don't know the endianness yet if type_ == SECTION_HEADER_BLOCK { let initial_len = slice.read_u32::().unwrap(); // Check the first field of the Section header to find the endianness let mut tmp_slice = slice; let magic = tmp_slice.read_u32::().unwrap(); let res = match magic { 0x1A2B3C4D => inner_parse::(slice, type_, initial_len), 0x4D3C2B1A => inner_parse::(slice, type_, initial_len.swap_bytes()), _ => Err(PcapError::InvalidField("SectionHeaderBlock: invalid magic number")), }; return res; } else { let initial_len = slice.read_u32::().map_err(|_| PcapError::IncompleteBuffer)?; return inner_parse::(slice, type_, initial_len); }; // Section Header parsing fn inner_parse(slice: &[u8], type_: u32, initial_len: u32) -> Result<(&[u8], RawBlock<'_>), PcapError> { if (initial_len % 4) != 0 { return Err(PcapError::InvalidField("Block: (initial_len % 4) != 0")); } if initial_len < 12 { return Err(PcapError::InvalidField("Block: initial_len < 12")); } // Check if there is enough data for the body and the trailer_len if slice.len() < initial_len as usize - 8 { return Err(PcapError::IncompleteBuffer); } let body_len = initial_len - 12; let body = &slice[..body_len as usize]; let mut rem = &slice[body_len as usize..]; let trailer_len = rem.read_u32::().unwrap(); if initial_len != trailer_len { return Err(PcapError::InvalidField("Block: initial_length != trailer_length")); } let block = RawBlock { type_, initial_len, body: Cow::Borrowed(body), trailer_len }; Ok((rem, block)) } } /// Writes a [`RawBlock`] to a writer. /// /// Uses the endianness of the header. pub fn write_to(&self, writer: &mut W) -> IoResult { writer.write_u32::(self.type_)?; writer.write_u32::(self.initial_len)?; writer.write_all(&self.body[..])?; writer.write_u32::(self.trailer_len)?; Ok(self.body.len() + 6) } /// Tries to convert a [`RawBlock`] into a [`Block`] pub fn try_into_block(self) -> PcapResult> { Block::try_from_raw_block::(self) } } /// PcapNg parsed blocks #[derive(Clone, Debug, IntoOwned, Eq, PartialEq)] pub enum Block<'a> { /// Section Header block SectionHeader(SectionHeaderBlock<'a>), /// Interface Description block InterfaceDescription(InterfaceDescriptionBlock<'a>), /// Packet block Packet(PacketBlock<'a>), /// Simple packet block SimplePacket(SimplePacketBlock<'a>), /// Name Resolution block NameResolution(NameResolutionBlock<'a>), /// Interface statistics block InterfaceStatistics(InterfaceStatisticsBlock<'a>), /// Enhanced packet block EnhancedPacket(EnhancedPacketBlock<'a>), /// Systemd Journal Export block SystemdJournalExport(SystemdJournalExportBlock<'a>), /// Unknown block Unknown(UnknownBlock<'a>), } impl<'a> Block<'a> { /// Parses a [`Block`] from a slice pub fn from_slice(slice: &'a [u8]) -> Result<(&'a [u8], Self), PcapError> { let (rem, raw_block) = RawBlock::from_slice::(slice)?; let block = Self::try_from_raw_block::(raw_block)?; Ok((rem, block)) } /// Writes a [`Block`] to a writer. pub fn write_to(&self, writer: &mut W) -> IoResult { return match self { Self::SectionHeader(b) => inner_write_to::(b, SECTION_HEADER_BLOCK, writer), Self::InterfaceDescription(b) => inner_write_to::(b, INTERFACE_DESCRIPTION_BLOCK, writer), Self::Packet(b) => inner_write_to::(b, PACKET_BLOCK, writer), Self::SimplePacket(b) => inner_write_to::(b, SIMPLE_PACKET_BLOCK, writer), Self::NameResolution(b) => inner_write_to::(b, NAME_RESOLUTION_BLOCK, writer), Self::InterfaceStatistics(b) => inner_write_to::(b, INTERFACE_STATISTIC_BLOCK, writer), Self::EnhancedPacket(b) => inner_write_to::(b, ENHANCED_PACKET_BLOCK, writer), Self::SystemdJournalExport(b) => inner_write_to::(b, SYSTEMD_JOURNAL_EXPORT_BLOCK, writer), Self::Unknown(b) => inner_write_to::(b, b.type_, writer), }; fn inner_write_to<'a, B: ByteOrder, BL: PcapNgBlock<'a>, W: Write>(block: &BL, block_code: u32, writer: &mut W) -> IoResult { // Fake write to compute the data length let data_len = block.write_to::(&mut std::io::sink()).unwrap(); let pad_len = (4 - (data_len % 4)) % 4; let block_len = data_len + pad_len + 12; writer.write_u32::(block_code)?; writer.write_u32::(block_len as u32)?; block.write_to::(writer)?; writer.write_all(&[0_u8; 3][..pad_len])?; writer.write_u32::(block_len as u32)?; Ok(block_len) } } /// Tries to create a [`Block`] from a [`RawBlock`]. /// /// The RawBlock must be Borrowed. pub fn try_from_raw_block(raw_block: RawBlock<'a>) -> Result, PcapError> { let body = match raw_block.body { Cow::Borrowed(b) => b, _ => panic!("The raw block is not borrowed"), }; match raw_block.type_ { SECTION_HEADER_BLOCK => { let (_, block) = SectionHeaderBlock::from_slice::(body)?; Ok(Block::SectionHeader(block)) }, INTERFACE_DESCRIPTION_BLOCK => { let (_, block) = InterfaceDescriptionBlock::from_slice::(body)?; Ok(Block::InterfaceDescription(block)) }, PACKET_BLOCK => { let (_, block) = PacketBlock::from_slice::(body)?; Ok(Block::Packet(block)) }, SIMPLE_PACKET_BLOCK => { let (_, block) = SimplePacketBlock::from_slice::(body)?; Ok(Block::SimplePacket(block)) }, NAME_RESOLUTION_BLOCK => { let (_, block) = NameResolutionBlock::from_slice::(body)?; Ok(Block::NameResolution(block)) }, INTERFACE_STATISTIC_BLOCK => { let (_, block) = InterfaceStatisticsBlock::from_slice::(body)?; Ok(Block::InterfaceStatistics(block)) }, ENHANCED_PACKET_BLOCK => { let (_, block) = EnhancedPacketBlock::from_slice::(body)?; Ok(Block::EnhancedPacket(block)) }, SYSTEMD_JOURNAL_EXPORT_BLOCK => { let (_, block) = SystemdJournalExportBlock::from_slice::(body)?; Ok(Block::SystemdJournalExport(block)) }, type_ => Ok(Block::Unknown(UnknownBlock::new(type_, raw_block.initial_len, body))), } } /// Tries to downcasts the current block into an [`EnhancedPacketBlock`] pub fn into_enhanced_packet(self) -> Option> { match self { Block::EnhancedPacket(a) => Some(a), _ => None, } } /// Tries to downcasts the current block into an [`InterfaceDescriptionBlock`] pub fn into_interface_description(self) -> Option> { match self { Block::InterfaceDescription(a) => Some(a), _ => None, } } /// Tries to downcasts the current block into an [`InterfaceStatisticsBlock`] pub fn into_interface_statistics(self) -> Option> { match self { Block::InterfaceStatistics(a) => Some(a), _ => None, } } /// Tries to downcast the current block into an [`NameResolutionBlock`], if possible pub fn into_name_resolution(self) -> Option> { match self { Block::NameResolution(a) => Some(a), _ => None, } } /// Tries to downcast the current block into an [`PacketBlock`], if possible pub fn into_packet(self) -> Option> { match self { Block::Packet(a) => Some(a), _ => None, } } /// Tries to downcast the current block into an [`SectionHeaderBlock`], if possible pub fn into_section_header(self) -> Option> { match self { Block::SectionHeader(a) => Some(a), _ => None, } } /// Tries to downcast the current block into an [`SimplePacketBlock`], if possible pub fn into_simple_packet(self) -> Option> { match self { Block::SimplePacket(a) => Some(a), _ => None, } } /// Tries to downcast the current block into an [`SystemdJournalExportBlock`], if possible pub fn into_systemd_journal_export(self) -> Option> { match self { Block::SystemdJournalExport(a) => Some(a), _ => None, } } } /// Common interface for the PcapNg blocks pub trait PcapNgBlock<'a> { /// Parse a new block from a slice fn from_slice(slice: &'a [u8]) -> Result<(&[u8], Self), PcapError> where Self: std::marker::Sized; /// Write the content of a block into a writer fn write_to(&self, writer: &mut W) -> IoResult; /// Convert a block into the [`Block`] enumeration fn into_block(self) -> Block<'a>; } pcap-file-2.0.0/src/pcapng/blocks/enhanced_packet.rs000064400000000000000000000146501046102023000204470ustar 00000000000000//! Enhanced Packet Block (EPB). use std::borrow::Cow; use std::io::{Result as IoResult, Write}; use std::time::Duration; use byteorder_slice::byteorder::WriteBytesExt; use byteorder_slice::result::ReadSlice; use byteorder_slice::ByteOrder; use derive_into_owned::IntoOwned; use super::block_common::{Block, PcapNgBlock}; use super::opt_common::{CustomBinaryOption, CustomUtf8Option, PcapNgOption, UnknownOption, WriteOptTo}; use crate::errors::PcapError; /// An Enhanced Packet Block (EPB) is the standard container for storing the packets coming from the network. #[derive(Clone, Debug, IntoOwned, Eq, PartialEq)] pub struct EnhancedPacketBlock<'a> { /// It specifies the interface this packet comes from. /// /// The correct interface will be the one whose Interface Description Block /// (within the current Section of the file) is identified by the same number of this field. pub interface_id: u32, /// Number of units of time that have elapsed since 1970-01-01 00:00:00 UTC. pub timestamp: Duration, /// Actual length of the packet when it was transmitted on the network. pub original_len: u32, /// The data coming from the network, including link-layer headers. pub data: Cow<'a, [u8]>, /// Options pub options: Vec>, } impl<'a> PcapNgBlock<'a> for EnhancedPacketBlock<'a> { fn from_slice(mut slice: &'a [u8]) -> Result<(&'a [u8], Self), PcapError> { if slice.len() < 20 { return Err(PcapError::InvalidField("EnhancedPacketBlock: block length length < 20")); } let interface_id = slice.read_u32::().unwrap(); let timestamp_high = slice.read_u32::().unwrap() as u64; let timestamp_low = slice.read_u32::().unwrap() as u64; let timestamp = (timestamp_high << 32) + timestamp_low; let captured_len = slice.read_u32::().unwrap(); let original_len = slice.read_u32::().unwrap(); let pad_len = (4 - (captured_len as usize % 4)) % 4; let tot_len = captured_len as usize + pad_len; if slice.len() < tot_len { return Err(PcapError::InvalidField("EnhancedPacketBlock: captured_len + padding > block length")); } let data = &slice[..captured_len as usize]; slice = &slice[tot_len..]; let (slice, options) = EnhancedPacketOption::opts_from_slice::(slice)?; let block = EnhancedPacketBlock { interface_id, timestamp: Duration::from_nanos(timestamp), original_len, data: Cow::Borrowed(data), options, }; Ok((slice, block)) } fn write_to(&self, writer: &mut W) -> IoResult { let pad_len = (4 - (&self.data.len() % 4)) % 4; writer.write_u32::(self.interface_id)?; let timestamp = self.timestamp.as_nanos(); let timestamp_high = (timestamp >> 32) as u32; writer.write_u32::(timestamp_high)?; let timestamp_low = (timestamp & 0xFFFFFFFF) as u32; writer.write_u32::(timestamp_low)?; writer.write_u32::(self.data.len() as u32)?; writer.write_u32::(self.original_len)?; writer.write_all(&self.data)?; writer.write_all(&[0_u8; 3][..pad_len])?; let opt_len = EnhancedPacketOption::write_opts_to::(&self.options, writer)?; Ok(20 + &self.data.len() + pad_len + opt_len) } fn into_block(self) -> Block<'a> { Block::EnhancedPacket(self) } } /// The Enhanced Packet Block (EPB) options #[derive(Clone, Debug, IntoOwned, Eq, PartialEq)] pub enum EnhancedPacketOption<'a> { /// Comment associated with the current block Comment(Cow<'a, str>), /// 32-bit flags word containing link-layer information. Flags(u32), /// Contains a hash of the packet. Hash(Cow<'a, [u8]>), /// 64-bit integer value specifying the number of packets lost /// (by the interface and the operating system) between this packet and the preceding one for /// the same interface or, for the first packet for an interface, between this packet /// and the start of the capture process. DropCount(u64), /// Custom option containing binary octets in the Custom Data portion CustomBinary(CustomBinaryOption<'a>), /// Custom option containing a UTF-8 string in the Custom Data portion CustomUtf8(CustomUtf8Option<'a>), /// Unknown option Unknown(UnknownOption<'a>), } impl<'a> PcapNgOption<'a> for EnhancedPacketOption<'a> { fn from_slice(code: u16, length: u16, mut slice: &'a [u8]) -> Result { let opt = match code { 1 => EnhancedPacketOption::Comment(Cow::Borrowed(std::str::from_utf8(slice)?)), 2 => { if slice.len() != 4 { return Err(PcapError::InvalidField("EnhancedPacketOption: Flags length != 4")); } EnhancedPacketOption::Flags(slice.read_u32::().map_err(|_| PcapError::IncompleteBuffer)?) }, 3 => EnhancedPacketOption::Hash(Cow::Borrowed(slice)), 4 => { if slice.len() != 8 { return Err(PcapError::InvalidField("EnhancedPacketOption: DropCount length != 8")); } EnhancedPacketOption::DropCount(slice.read_u64::().map_err(|_| PcapError::IncompleteBuffer)?) }, 2988 | 19372 => EnhancedPacketOption::CustomUtf8(CustomUtf8Option::from_slice::(code, slice)?), 2989 | 19373 => EnhancedPacketOption::CustomBinary(CustomBinaryOption::from_slice::(code, slice)?), _ => EnhancedPacketOption::Unknown(UnknownOption::new(code, length, slice)), }; Ok(opt) } fn write_to(&self, writer: &mut W) -> IoResult { match self { EnhancedPacketOption::Comment(a) => a.write_opt_to::(1, writer), EnhancedPacketOption::Flags(a) => a.write_opt_to::(2, writer), EnhancedPacketOption::Hash(a) => a.write_opt_to::(3, writer), EnhancedPacketOption::DropCount(a) => a.write_opt_to::(4, writer), EnhancedPacketOption::CustomBinary(a) => a.write_opt_to::(a.code, writer), EnhancedPacketOption::CustomUtf8(a) => a.write_opt_to::(a.code, writer), EnhancedPacketOption::Unknown(a) => a.write_opt_to::(a.code, writer), } } } pcap-file-2.0.0/src/pcapng/blocks/interface_description.rs000064400000000000000000000256051046102023000217200ustar 00000000000000#![allow(clippy::cast_lossless)] //! Interface Description Block (IDB). use std::borrow::Cow; use std::io::{Result as IoResult, Write}; use byteorder_slice::byteorder::WriteBytesExt; use byteorder_slice::result::ReadSlice; use byteorder_slice::ByteOrder; use derive_into_owned::IntoOwned; use super::block_common::{Block, PcapNgBlock}; use super::opt_common::{CustomBinaryOption, CustomUtf8Option, PcapNgOption, UnknownOption, WriteOptTo}; use crate::errors::PcapError; use crate::DataLink; /// An Interface Description Block (IDB) is the container for information describing an interface /// on which packet data is captured. #[derive(Clone, Debug, IntoOwned, Eq, PartialEq)] pub struct InterfaceDescriptionBlock<'a> { /// A value that defines the link layer type of this interface. /// /// The list of Standardized Link Layer Type codes is available in the /// [tcpdump.org link-layer header types registry.](http://www.tcpdump.org/linktypes.html). pub linktype: DataLink, /// Maximum number of octets captured from each packet. /// /// The portion of each packet that exceeds this value will not be stored in the file. /// A value of zero indicates no limit. pub snaplen: u32, /// Options pub options: Vec>, } impl<'a> PcapNgBlock<'a> for InterfaceDescriptionBlock<'a> { fn from_slice(mut slice: &'a [u8]) -> Result<(&'a [u8], Self), PcapError> { if slice.len() < 8 { return Err(PcapError::InvalidField("InterfaceDescriptionBlock: block length < 8")); } let linktype = (slice.read_u16::().unwrap() as u32).into(); let reserved = slice.read_u16::().unwrap(); if reserved != 0 { return Err(PcapError::InvalidField("InterfaceDescriptionBlock: reserved != 0")); } let snaplen = slice.read_u32::().unwrap(); let (slice, options) = InterfaceDescriptionOption::opts_from_slice::(slice)?; let block = InterfaceDescriptionBlock { linktype, snaplen, options }; Ok((slice, block)) } fn write_to(&self, writer: &mut W) -> IoResult { writer.write_u16::(u32::from(self.linktype) as u16)?; writer.write_u16::(0)?; writer.write_u32::(self.snaplen)?; let opt_len = InterfaceDescriptionOption::write_opts_to::(&self.options, writer)?; Ok(8 + opt_len) } fn into_block(self) -> Block<'a> { Block::InterfaceDescription(self) } } impl InterfaceDescriptionBlock<'static> { /// Creates a new [`InterfaceDescriptionBlock`] pub fn new(linktype: DataLink, snaplen: u32) -> Self { Self { linktype, snaplen, options: vec![] } } } /// The Interface Description Block (IDB) options #[derive(Clone, Debug, IntoOwned, Eq, PartialEq)] pub enum InterfaceDescriptionOption<'a> { /// The opt_comment option is a UTF-8 string containing human-readable comment text /// that is associated to the current block. Comment(Cow<'a, str>), /// The if_name option is a UTF-8 string containing the name of the device used to capture data. IfName(Cow<'a, str>), /// The if_description option is a UTF-8 string containing the description of the device used to capture data. IfDescription(Cow<'a, str>), /// The if_IPv4addr option is an IPv4 network address and corresponding netmask for the interface. IfIpv4Addr(Cow<'a, [u8]>), /// The if_IPv6addr option is an IPv6 network address and corresponding prefix length for the interface. IfIpv6Addr(Cow<'a, [u8]>), /// The if_MACaddr option is the Interface Hardware MAC address (48 bits), if available. IfMacAddr(Cow<'a, [u8]>), /// The if_EUIaddr option is the Interface Hardware EUI address (64 bits), if available. IfEuIAddr(u64), /// The if_speed option is a 64-bit number for the Interface speed (in bits per second). IfSpeed(u64), /// The if_tsresol option identifies the resolution of timestamps. IfTsResol(u8), /// The if_tzone option identifies the time zone for GMT support. IfTzone(u32), /// The if_filter option identifies the filter (e.g. "capture only TCP traffic") used to capture traffic. IfFilter(Cow<'a, [u8]>), /// The if_os option is a UTF-8 string containing the name of the operating system /// of the machine in which this interface is installed. IfOs(Cow<'a, str>), /// The if_fcslen option is an 8-bit unsigned integer value that specifies /// the length of the Frame Check Sequence (in bits) for this interface. IfFcsLen(u8), /// The if_tsoffset option is a 64-bit integer value that specifies an offset (in seconds) /// that must be added to the timestamp of each packet to obtain the absolute timestamp of a packet. IfTsOffset(u64), /// The if_hardware option is a UTF-8 string containing the description of the interface hardware. IfHardware(Cow<'a, str>), /// Custom option containing binary octets in the Custom Data portion CustomBinary(CustomBinaryOption<'a>), /// Custom option containing a UTF-8 string in the Custom Data portion CustomUtf8(CustomUtf8Option<'a>), /// Unknown option Unknown(UnknownOption<'a>), } impl<'a> PcapNgOption<'a> for InterfaceDescriptionOption<'a> { fn from_slice(code: u16, length: u16, mut slice: &'a [u8]) -> Result { let opt = match code { 1 => InterfaceDescriptionOption::Comment(Cow::Borrowed(std::str::from_utf8(slice)?)), 2 => InterfaceDescriptionOption::IfName(Cow::Borrowed(std::str::from_utf8(slice)?)), 3 => InterfaceDescriptionOption::IfDescription(Cow::Borrowed(std::str::from_utf8(slice)?)), 4 => { if slice.len() != 8 { return Err(PcapError::InvalidField("InterfaceDescriptionOption: IfIpv4Addr length != 8")); } InterfaceDescriptionOption::IfIpv4Addr(Cow::Borrowed(slice)) }, 5 => { if slice.len() != 17 { return Err(PcapError::InvalidField("InterfaceDescriptionOption: IfIpv6Addr length != 17")); } InterfaceDescriptionOption::IfIpv6Addr(Cow::Borrowed(slice)) }, 6 => { if slice.len() != 6 { return Err(PcapError::InvalidField("InterfaceDescriptionOption: IfMacAddr length != 6")); } InterfaceDescriptionOption::IfMacAddr(Cow::Borrowed(slice)) }, 7 => { if slice.len() != 8 { return Err(PcapError::InvalidField("InterfaceDescriptionOption: IfEuIAddr length != 8")); } InterfaceDescriptionOption::IfEuIAddr(slice.read_u64::().map_err(|_| PcapError::IncompleteBuffer)?) }, 8 => { if slice.len() != 8 { return Err(PcapError::InvalidField("InterfaceDescriptionOption: IfSpeed length != 8")); } InterfaceDescriptionOption::IfSpeed(slice.read_u64::().map_err(|_| PcapError::IncompleteBuffer)?) }, 9 => { if slice.len() != 1 { return Err(PcapError::InvalidField("InterfaceDescriptionOption: IfTsResol length != 1")); } InterfaceDescriptionOption::IfTsResol(slice.read_u8().map_err(|_| PcapError::IncompleteBuffer)?) }, 10 => { if slice.len() != 1 { return Err(PcapError::InvalidField("InterfaceDescriptionOption: IfTzone length != 1")); } InterfaceDescriptionOption::IfTzone(slice.read_u32::().map_err(|_| PcapError::IncompleteBuffer)?) }, 11 => { if slice.is_empty() { return Err(PcapError::InvalidField("InterfaceDescriptionOption: IfFilter is empty")); } InterfaceDescriptionOption::IfFilter(Cow::Borrowed(slice)) }, 12 => InterfaceDescriptionOption::IfOs(Cow::Borrowed(std::str::from_utf8(slice)?)), 13 => { if slice.len() != 1 { return Err(PcapError::InvalidField("InterfaceDescriptionOption: IfFcsLen length != 1")); } InterfaceDescriptionOption::IfFcsLen(slice.read_u8().map_err(|_| PcapError::IncompleteBuffer)?) }, 14 => { if slice.len() != 8 { return Err(PcapError::InvalidField("InterfaceDescriptionOption: IfTsOffset length != 8")); } InterfaceDescriptionOption::IfTsOffset(slice.read_u64::().map_err(|_| PcapError::IncompleteBuffer)?) }, 15 => InterfaceDescriptionOption::IfHardware(Cow::Borrowed(std::str::from_utf8(slice)?)), 2988 | 19372 => InterfaceDescriptionOption::CustomUtf8(CustomUtf8Option::from_slice::(code, slice)?), 2989 | 19373 => InterfaceDescriptionOption::CustomBinary(CustomBinaryOption::from_slice::(code, slice)?), _ => InterfaceDescriptionOption::Unknown(UnknownOption::new(code, length, slice)), }; Ok(opt) } fn write_to(&self, writer: &mut W) -> IoResult { match self { InterfaceDescriptionOption::Comment(a) => a.write_opt_to::(1, writer), InterfaceDescriptionOption::IfName(a) => a.write_opt_to::(2, writer), InterfaceDescriptionOption::IfDescription(a) => a.write_opt_to::(3, writer), InterfaceDescriptionOption::IfIpv4Addr(a) => a.write_opt_to::(4, writer), InterfaceDescriptionOption::IfIpv6Addr(a) => a.write_opt_to::(5, writer), InterfaceDescriptionOption::IfMacAddr(a) => a.write_opt_to::(6, writer), InterfaceDescriptionOption::IfEuIAddr(a) => a.write_opt_to::(7, writer), InterfaceDescriptionOption::IfSpeed(a) => a.write_opt_to::(8, writer), InterfaceDescriptionOption::IfTsResol(a) => a.write_opt_to::(9, writer), InterfaceDescriptionOption::IfTzone(a) => a.write_opt_to::(10, writer), InterfaceDescriptionOption::IfFilter(a) => a.write_opt_to::(11, writer), InterfaceDescriptionOption::IfOs(a) => a.write_opt_to::(12, writer), InterfaceDescriptionOption::IfFcsLen(a) => a.write_opt_to::(13, writer), InterfaceDescriptionOption::IfTsOffset(a) => a.write_opt_to::(14, writer), InterfaceDescriptionOption::IfHardware(a) => a.write_opt_to::(15, writer), InterfaceDescriptionOption::CustomBinary(a) => a.write_opt_to::(a.code, writer), InterfaceDescriptionOption::CustomUtf8(a) => a.write_opt_to::(a.code, writer), InterfaceDescriptionOption::Unknown(a) => a.write_opt_to::(a.code, writer), } } } pcap-file-2.0.0/src/pcapng/blocks/interface_statistics.rs000064400000000000000000000150531046102023000215630ustar 00000000000000//! Interface Statistics Block. use std::borrow::Cow; use std::io::{Result as IoResult, Write}; use byteorder_slice::byteorder::WriteBytesExt; use byteorder_slice::result::ReadSlice; use byteorder_slice::ByteOrder; use derive_into_owned::IntoOwned; use super::block_common::{Block, PcapNgBlock}; use super::opt_common::{CustomBinaryOption, CustomUtf8Option, PcapNgOption, UnknownOption, WriteOptTo}; use crate::errors::PcapError; /// The Interface Statistics Block contains the capture statistics for a given interface and it is optional. #[derive(Clone, Debug, IntoOwned, Eq, PartialEq)] pub struct InterfaceStatisticsBlock<'a> { /// Specifies the interface these statistics refers to. /// /// The correct interface will be the one whose Interface Description Block (within the current Section of the file) /// is identified by same number of this field. pub interface_id: u32, /// Time this statistics refers to. /// /// The format of the timestamp is the same already defined in the Enhanced Packet Block. /// The length of a unit of time is specified by the 'if_tsresol' option of the Interface Description Block referenced by this packet. pub timestamp: u64, /// Options pub options: Vec>, } impl<'a> PcapNgBlock<'a> for InterfaceStatisticsBlock<'a> { fn from_slice(mut slice: &'a [u8]) -> Result<(&[u8], Self), PcapError> { if slice.len() < 12 { return Err(PcapError::InvalidField("InterfaceStatisticsBlock: block length < 12")); } let interface_id = slice.read_u32::().unwrap(); let timestamp = slice.read_u64::().unwrap(); let (slice, options) = InterfaceStatisticsOption::opts_from_slice::(slice)?; let block = InterfaceStatisticsBlock { interface_id, timestamp, options }; Ok((slice, block)) } fn write_to(&self, writer: &mut W) -> IoResult { writer.write_u32::(self.interface_id)?; writer.write_u64::(self.timestamp)?; let opt_len = InterfaceStatisticsOption::write_opts_to::(&self.options, writer)?; Ok(12 + opt_len) } fn into_block(self) -> Block<'a> { Block::InterfaceStatistics(self) } } /// The Interface Statistics Block options #[derive(Clone, Debug, IntoOwned, Eq, PartialEq)] pub enum InterfaceStatisticsOption<'a> { /// The opt_comment option is a UTF-8 string containing human-readable comment text /// that is associated to the current block. Comment(Cow<'a, str>), /// The isb_starttime option specifies the time the capture started. IsbStartTime(u64), /// The isb_endtime option specifies the time the capture ended. IsbEndTime(u64), /// The isb_ifrecv option specifies the 64-bit unsigned integer number of packets received from the physical interface /// starting from the beginning of the capture. IsbIfRecv(u64), /// The isb_ifdrop option specifies the 64-bit unsigned integer number of packets dropped by the interface /// due to lack of resources starting from the beginning of the capture. IsbIfDrop(u64), /// The isb_filteraccept option specifies the 64-bit unsigned integer number of packets accepted /// by filter starting from the beginning of the capture. IsbFilterAccept(u64), /// The isb_osdrop option specifies the 64-bit unsigned integer number of packets dropped /// by the operating system starting from the beginning of the capture. IsbOsDrop(u64), /// The isb_usrdeliv option specifies the 64-bit unsigned integer number of packets delivered /// to the user starting from the beginning of the capture. IsbUsrDeliv(u64), /// Custom option containing binary octets in the Custom Data portion CustomBinary(CustomBinaryOption<'a>), /// Custom option containing a UTF-8 string in the Custom Data portion CustomUtf8(CustomUtf8Option<'a>), /// Unknown option Unknown(UnknownOption<'a>), } impl<'a> PcapNgOption<'a> for InterfaceStatisticsOption<'a> { fn from_slice(code: u16, length: u16, mut slice: &'a [u8]) -> Result { let opt = match code { 1 => InterfaceStatisticsOption::Comment(Cow::Borrowed(std::str::from_utf8(slice)?)), 2 => InterfaceStatisticsOption::IsbStartTime(slice.read_u64::().map_err(|_| PcapError::IncompleteBuffer)?), 3 => InterfaceStatisticsOption::IsbEndTime(slice.read_u64::().map_err(|_| PcapError::IncompleteBuffer)?), 4 => InterfaceStatisticsOption::IsbIfRecv(slice.read_u64::().map_err(|_| PcapError::IncompleteBuffer)?), 5 => InterfaceStatisticsOption::IsbIfDrop(slice.read_u64::().map_err(|_| PcapError::IncompleteBuffer)?), 6 => InterfaceStatisticsOption::IsbFilterAccept(slice.read_u64::().map_err(|_| PcapError::IncompleteBuffer)?), 7 => InterfaceStatisticsOption::IsbOsDrop(slice.read_u64::().map_err(|_| PcapError::IncompleteBuffer)?), 8 => InterfaceStatisticsOption::IsbUsrDeliv(slice.read_u64::().map_err(|_| PcapError::IncompleteBuffer)?), 2988 | 19372 => InterfaceStatisticsOption::CustomUtf8(CustomUtf8Option::from_slice::(code, slice)?), 2989 | 19373 => InterfaceStatisticsOption::CustomBinary(CustomBinaryOption::from_slice::(code, slice)?), _ => InterfaceStatisticsOption::Unknown(UnknownOption::new(code, length, slice)), }; Ok(opt) } fn write_to(&self, writer: &mut W) -> IoResult { match self { InterfaceStatisticsOption::Comment(a) => a.write_opt_to::(1, writer), InterfaceStatisticsOption::IsbStartTime(a) => a.write_opt_to::(2, writer), InterfaceStatisticsOption::IsbEndTime(a) => a.write_opt_to::(3, writer), InterfaceStatisticsOption::IsbIfRecv(a) => a.write_opt_to::(4, writer), InterfaceStatisticsOption::IsbIfDrop(a) => a.write_opt_to::(5, writer), InterfaceStatisticsOption::IsbFilterAccept(a) => a.write_opt_to::(6, writer), InterfaceStatisticsOption::IsbOsDrop(a) => a.write_opt_to::(7, writer), InterfaceStatisticsOption::IsbUsrDeliv(a) => a.write_opt_to::(8, writer), InterfaceStatisticsOption::CustomBinary(a) => a.write_opt_to::(a.code, writer), InterfaceStatisticsOption::CustomUtf8(a) => a.write_opt_to::(a.code, writer), InterfaceStatisticsOption::Unknown(a) => a.write_opt_to::(a.code, writer), } } } pcap-file-2.0.0/src/pcapng/blocks/mod.rs000064400000000000000000000005221046102023000161230ustar 00000000000000//! Contains the PcapNg blocks. pub(crate) mod block_common; pub mod enhanced_packet; pub mod interface_description; pub mod interface_statistics; pub mod name_resolution; pub(crate) mod opt_common; pub mod packet; pub mod section_header; pub mod simple_packet; pub mod systemd_journal_export; pub mod unknown; pub use block_common::*; pcap-file-2.0.0/src/pcapng/blocks/name_resolution.rs000064400000000000000000000261571046102023000205630ustar 00000000000000//! Name Resolution Block (NRB). use std::borrow::Cow; use std::io::{Result as IoResult, Write}; use byteorder_slice::byteorder::WriteBytesExt; use byteorder_slice::result::ReadSlice; use byteorder_slice::ByteOrder; use derive_into_owned::IntoOwned; use super::block_common::{Block, PcapNgBlock}; use super::opt_common::{CustomBinaryOption, CustomUtf8Option, PcapNgOption, UnknownOption, WriteOptTo}; use crate::errors::PcapError; /// The Name Resolution Block (NRB) is used to support the correlation of numeric addresses /// (present in the captured packets) and their corresponding canonical names and it is optional. #[derive(Clone, Debug, IntoOwned, Eq, PartialEq)] pub struct NameResolutionBlock<'a> { /// Records pub records: Vec>, /// Options pub options: Vec>, } impl<'a> PcapNgBlock<'a> for NameResolutionBlock<'a> { fn from_slice(mut slice: &'a [u8]) -> Result<(&'a [u8], Self), PcapError> { let mut records = Vec::new(); loop { let (slice_tmp, record) = Record::from_slice::(slice)?; slice = slice_tmp; match record { Record::End => break, _ => records.push(record), } } let (rem, options) = NameResolutionOption::opts_from_slice::(slice)?; let block = NameResolutionBlock { records, options }; Ok((rem, block)) } fn write_to(&self, writer: &mut W) -> IoResult { let mut len = 0; for record in &self.records { len += record.write_to::(writer)?; } len += Record::End.write_to::(writer)?; len += NameResolutionOption::write_opts_to::(&self.options, writer)?; Ok(len) } fn into_block(self) -> Block<'a> { Block::NameResolution(self) } } /// Resolution block record types #[derive(Clone, Debug, IntoOwned, Eq, PartialEq)] pub enum Record<'a> { /// End of the records End, /// Ipv4 records Ipv4(Ipv4Record<'a>), /// Ipv6 records Ipv6(Ipv6Record<'a>), /// Unknown records Unknown(UnknownRecord<'a>), } impl<'a> Record<'a> { /// Parse a [`Record`] from a slice pub fn from_slice(mut slice: &'a [u8]) -> Result<(&'a [u8], Self), PcapError> { let type_ = slice.read_u16::().map_err(|_| PcapError::IncompleteBuffer)?; let length = slice.read_u16::().map_err(|_| PcapError::IncompleteBuffer)?; let pad_len = (4 - length % 4) % 4; if slice.len() < length as usize { return Err(PcapError::InvalidField("NameResolutionBlock: Record length > slice.len()")); } let value = &slice[..length as usize]; let record = match type_ { 0 => { if length != 0 { return Err(PcapError::InvalidField("NameResolutionBlock: nrb_record_end length != 0")); } Record::End }, 1 => { let record = Ipv4Record::from_slice(value)?; Record::Ipv4(record) }, 2 => { let record = Ipv6Record::from_slice(value)?; Record::Ipv6(record) }, _ => { let record = UnknownRecord::new(type_, length, value); Record::Unknown(record) }, }; let len = length as usize + pad_len as usize; Ok((&slice[len..], record)) } /// Write a [`Record`] to a writer pub fn write_to(&self, writer: &mut W) -> IoResult { match self { Record::End => { writer.write_u16::(0)?; writer.write_u16::(0)?; Ok(4) }, Record::Ipv4(a) => { let len = a.write_to::(&mut std::io::sink()).unwrap(); let pad_len = (4 - len % 4) % 4; writer.write_u16::(1)?; writer.write_u16::(len as u16)?; a.write_to::(writer)?; writer.write_all(&[0_u8; 3][..pad_len])?; Ok(4 + len + pad_len) }, Record::Ipv6(a) => { let len = a.write_to::(&mut std::io::sink()).unwrap(); let pad_len = (4 - len % 4) % 4; writer.write_u16::(2)?; writer.write_u16::(len as u16)?; a.write_to::(writer)?; writer.write_all(&[0_u8; 3][..pad_len])?; Ok(4 + len + pad_len) }, Record::Unknown(a) => { let len = a.value.len(); let pad_len = (4 - len % 4) % 4; writer.write_u16::(a.type_)?; writer.write_u16::(a.length)?; writer.write_all(&a.value)?; writer.write_all(&[0_u8; 3][..pad_len])?; Ok(4 + len + pad_len) }, } } } /// Ipv4 records #[derive(Clone, Debug, IntoOwned, Eq, PartialEq)] pub struct Ipv4Record<'a> { /// IPv4 Addr pub ip_addr: Cow<'a, [u8]>, /// Fqdn pub names: Vec>, } impl<'a> Ipv4Record<'a> { /// Parse a [`Ipv4Record`] from a slice pub fn from_slice(mut slice: &'a [u8]) -> Result { if slice.len() < 6 { return Err(PcapError::InvalidField("NameResolutionBlock: Ipv4Record len < 6")); } let ip_addr = &slice[..4]; slice = &slice[4..]; let mut names = vec![]; for name in slice.split(|&b| b == 0) { if name.is_empty() { break; } names.push(Cow::Borrowed(std::str::from_utf8(name)?)); } if names.is_empty() { return Err(PcapError::InvalidField("NameResolutionBlock: Ipv4Record without any name")); } let record = Ipv4Record { ip_addr: Cow::Borrowed(ip_addr), names }; Ok(record) } /// Write a [`Ipv4Record`] to a writter pub fn write_to(&self, writer: &mut W) -> IoResult { let mut len = 4; writer.write_all(&self.ip_addr)?; for name in &self.names { writer.write_all(name.as_bytes())?; writer.write_u8(0)?; len += name.as_bytes().len(); len += 1; } Ok(len) } } /// Ipv6 records #[derive(Clone, Debug, IntoOwned, Eq, PartialEq)] pub struct Ipv6Record<'a> { /// Ipv6 addr pub ip_addr: Cow<'a, [u8]>, /// Fqdn pub names: Vec>, } impl<'a> Ipv6Record<'a> { /// Parse a [`Ipv6Record`] from a slice pub fn from_slice(mut slice: &'a [u8]) -> Result { if slice.len() < 18 { return Err(PcapError::InvalidField("NameResolutionBlock: Ipv6Record len < 18")); } let ip_addr = &slice[..16]; slice = &slice[16..]; let mut names = vec![]; for name in slice.split(|&b| b == 0) { if name.is_empty() { break; } names.push(Cow::Borrowed(std::str::from_utf8(name)?)); } if names.is_empty() { return Err(PcapError::InvalidField("NameResolutionBlock: Ipv6Record without any name")); } let record = Ipv6Record { ip_addr: Cow::Borrowed(ip_addr), names }; Ok(record) } /// Write a [`Ipv6Record`] to a writter pub fn write_to(&self, writer: &mut W) -> IoResult { let mut len = 16; writer.write_all(&self.ip_addr)?; for name in &self.names { writer.write_all(name.as_bytes())?; writer.write_u8(0)?; len += name.as_bytes().len(); len += 1; } Ok(len) } } /// Unknown records #[derive(Clone, Debug, IntoOwned, Eq, PartialEq)] pub struct UnknownRecord<'a> { /// Records type pub type_: u16, /// Record length pub length: u16, /// Record body pub value: Cow<'a, [u8]>, } impl<'a> UnknownRecord<'a> { /// Creates a new [`UnknownRecord`] fn new(type_: u16, length: u16, value: &'a [u8]) -> Self { UnknownRecord { type_, length, value: Cow::Borrowed(value) } } } /// The Name Resolution Block (NRB) options #[derive(Clone, Debug, IntoOwned, Eq, PartialEq)] pub enum NameResolutionOption<'a> { /// The opt_comment option is a UTF-8 string containing human-readable comment text /// that is associated to the current block. Comment(Cow<'a, str>), /// The ns_dnsname option is a UTF-8 string containing the name of the machine (DNS server) used to perform the name resolution. NsDnsName(Cow<'a, str>), /// The ns_dnsIP4addr option specifies the IPv4 address of the DNS server. NsDnsIpv4Addr(Cow<'a, [u8]>), /// The ns_dnsIP6addr option specifies the IPv6 address of the DNS server. NsDnsIpv6Addr(Cow<'a, [u8]>), /// Custom option containing binary octets in the Custom Data portion CustomBinary(CustomBinaryOption<'a>), /// Custom option containing a UTF-8 string in the Custom Data portion CustomUtf8(CustomUtf8Option<'a>), /// Unknown option Unknown(UnknownOption<'a>), } impl<'a> PcapNgOption<'a> for NameResolutionOption<'a> { fn from_slice(code: u16, length: u16, slice: &'a [u8]) -> Result { let opt = match code { 1 => NameResolutionOption::Comment(Cow::Borrowed(std::str::from_utf8(slice)?)), 2 => NameResolutionOption::NsDnsName(Cow::Borrowed(std::str::from_utf8(slice)?)), 3 => { if slice.len() != 4 { return Err(PcapError::InvalidField("NameResolutionOption: NsDnsIpv4Addr length != 4")); } NameResolutionOption::NsDnsIpv4Addr(Cow::Borrowed(slice)) }, 4 => { if slice.len() != 16 { return Err(PcapError::InvalidField("NameResolutionOption: NsDnsIpv6Addr length != 16")); } NameResolutionOption::NsDnsIpv6Addr(Cow::Borrowed(slice)) }, 2988 | 19372 => NameResolutionOption::CustomUtf8(CustomUtf8Option::from_slice::(code, slice)?), 2989 | 19373 => NameResolutionOption::CustomBinary(CustomBinaryOption::from_slice::(code, slice)?), _ => NameResolutionOption::Unknown(UnknownOption::new(code, length, slice)), }; Ok(opt) } fn write_to(&self, writer: &mut W) -> IoResult { match self { NameResolutionOption::Comment(a) => a.write_opt_to::(1, writer), NameResolutionOption::NsDnsName(a) => a.write_opt_to::(2, writer), NameResolutionOption::NsDnsIpv4Addr(a) => a.write_opt_to::(3, writer), NameResolutionOption::NsDnsIpv6Addr(a) => a.write_opt_to::(4, writer), NameResolutionOption::CustomBinary(a) => a.write_opt_to::(a.code, writer), NameResolutionOption::CustomUtf8(a) => a.write_opt_to::(a.code, writer), NameResolutionOption::Unknown(a) => a.write_opt_to::(a.code, writer), } } } pcap-file-2.0.0/src/pcapng/blocks/opt_common.rs000064400000000000000000000167621046102023000175330ustar 00000000000000use std::borrow::Cow; use std::io::{Result as IoResult, Write}; use byteorder_slice::byteorder::WriteBytesExt; use byteorder_slice::result::ReadSlice; use byteorder_slice::ByteOrder; use derive_into_owned::IntoOwned; use crate::errors::PcapError; /// Common fonctions of the PcapNg options pub(crate) trait PcapNgOption<'a> { /// Parse an option from a slice fn from_slice(code: u16, length: u16, slice: &'a [u8]) -> Result where Self: std::marker::Sized; /// Parse all options in a block fn opts_from_slice(mut slice: &'a [u8]) -> Result<(&'a [u8], Vec), PcapError> where Self: std::marker::Sized, { let mut options = vec![]; // If there is nothing left in the slice, it means that there is no option if slice.is_empty() { return Ok((slice, options)); } while !slice.is_empty() { if slice.len() < 4 { return Err(PcapError::InvalidField("Option: slice.len() < 4")); } let code = slice.read_u16::().unwrap(); let length = slice.read_u16::().unwrap() as usize; let pad_len = (4 - (length % 4)) % 4; if code == 0 { return Ok((slice, options)); } if slice.len() < length + pad_len { return Err(PcapError::InvalidField("Option: length + pad.len() > slice.len()")); } let tmp_slice = &slice[..length]; let opt = Self::from_slice::(code, length as u16, tmp_slice)?; // Jump over the padding slice = &slice[length + pad_len..]; options.push(opt); } Err(PcapError::InvalidField("Invalid option")) } /// Write the option to a writer fn write_to(&self, writer: &mut W) -> IoResult; /// Write all options in a block fn write_opts_to(opts: &[Self], writer: &mut W) -> IoResult where Self: std::marker::Sized, { let mut have_opt = false; let mut written = 0; for opt in opts { written += opt.write_to::(writer)?; have_opt = true; } if have_opt { writer.write_u16::(0)?; writer.write_u16::(0)?; written += 4; } Ok(written) } } /// Unknown options #[derive(Clone, Debug, IntoOwned, Eq, PartialEq)] pub struct UnknownOption<'a> { /// Option code pub code: u16, /// Option length pub length: u16, /// Option value pub value: Cow<'a, [u8]>, } impl<'a> UnknownOption<'a> { /// Creates a new [`UnknownOption`] pub fn new(code: u16, length: u16, value: &'a [u8]) -> Self { UnknownOption { code, length, value: Cow::Borrowed(value) } } } /// Custom binary option #[derive(Clone, Debug, IntoOwned, Eq, PartialEq)] pub struct CustomBinaryOption<'a> { /// Option code pub code: u16, /// Option PEN identifier pub pen: u32, /// Option value pub value: Cow<'a, [u8]>, } impl<'a> CustomBinaryOption<'a> { /// Parse an [`CustomBinaryOption`] from a slice pub fn from_slice(code: u16, mut src: &'a [u8]) -> Result { let pen = src.read_u32::().map_err(|_| PcapError::IncompleteBuffer)?; let opt = CustomBinaryOption { code, pen, value: Cow::Borrowed(src) }; Ok(opt) } } /// Custom string (UTF-8) option #[derive(Clone, Debug, IntoOwned, Eq, PartialEq)] pub struct CustomUtf8Option<'a> { /// Option code pub code: u16, /// Option PEN identifier pub pen: u32, /// Option value pub value: Cow<'a, str>, } impl<'a> CustomUtf8Option<'a> { /// Parse a [`CustomUtf8Option`] from a slice pub fn from_slice(code: u16, mut src: &'a [u8]) -> Result { let pen = src.read_u32::().map_err(|_| PcapError::IncompleteBuffer)?; let opt = CustomUtf8Option { code, pen, value: Cow::Borrowed(std::str::from_utf8(src)?) }; Ok(opt) } } pub(crate) trait WriteOptTo { fn write_opt_to(&self, code: u16, writer: &mut W) -> IoResult; } impl<'a> WriteOptTo for Cow<'a, [u8]> { fn write_opt_to(&self, code: u16, writer: &mut W) -> IoResult { let len = self.len(); let pad_len = (4 - len % 4) % 4; writer.write_u16::(code)?; writer.write_u16::(len as u16)?; writer.write_all(self)?; writer.write_all(&[0_u8; 3][..pad_len])?; Ok(len + pad_len + 4) } } impl<'a> WriteOptTo for Cow<'a, str> { fn write_opt_to(&self, code: u16, writer: &mut W) -> IoResult { let len = self.as_bytes().len(); let pad_len = (4 - len % 4) % 4; writer.write_u16::(code)?; writer.write_u16::(len as u16)?; writer.write_all(self.as_bytes())?; writer.write_all(&[0_u8; 3][..pad_len])?; Ok(len + pad_len + 4) } } impl WriteOptTo for u8 { fn write_opt_to(&self, code: u16, writer: &mut W) -> IoResult { writer.write_u16::(code)?; writer.write_u16::(1)?; writer.write_u8(*self)?; writer.write_all(&[0_u8; 3])?; Ok(8) } } impl WriteOptTo for u16 { fn write_opt_to(&self, code: u16, writer: &mut W) -> IoResult { writer.write_u16::(code)?; writer.write_u16::(2)?; writer.write_u16::(*self)?; writer.write_all(&[0_u8; 2])?; Ok(8) } } impl WriteOptTo for u32 { fn write_opt_to(&self, code: u16, writer: &mut W) -> IoResult { writer.write_u16::(code)?; writer.write_u16::(4)?; writer.write_u32::(*self)?; Ok(8) } } impl WriteOptTo for u64 { fn write_opt_to(&self, code: u16, writer: &mut W) -> IoResult { writer.write_u16::(code)?; writer.write_u16::(8)?; writer.write_u64::(*self)?; Ok(12) } } impl<'a> WriteOptTo for CustomBinaryOption<'a> { fn write_opt_to(&self, code: u16, writer: &mut W) -> IoResult { let len = &self.value.len() + 4; let pad_len = (4 - len % 4) % 4; writer.write_u16::(code)?; writer.write_u16::(len as u16)?; writer.write_u32::(self.pen)?; writer.write_all(&self.value)?; writer.write_all(&[0_u8; 3][..pad_len])?; Ok(len + pad_len + 4) } } impl<'a> WriteOptTo for CustomUtf8Option<'a> { fn write_opt_to(&self, code: u16, writer: &mut W) -> IoResult { let len = &self.value.len() + 4; let pad_len = (4 - len % 4) % 4; writer.write_u16::(code)?; writer.write_u16::(len as u16)?; writer.write_u32::(self.pen)?; writer.write_all(self.value.as_bytes())?; writer.write_all(&[0_u8; 3][..pad_len])?; Ok(len + pad_len + 4) } } impl<'a> WriteOptTo for UnknownOption<'a> { fn write_opt_to(&self, code: u16, writer: &mut W) -> IoResult { let len = self.value.len(); let pad_len = (4 - len % 4) % 4; writer.write_u16::(code)?; writer.write_u16::(len as u16)?; writer.write_all(&self.value)?; writer.write_all(&[0_u8; 3][..pad_len])?; Ok(len + pad_len + 4) } } pcap-file-2.0.0/src/pcapng/blocks/packet.rs000064400000000000000000000127711046102023000166240ustar 00000000000000//! Packet Block. use std::borrow::Cow; use std::io::{Result as IoResult, Write}; use byteorder_slice::byteorder::WriteBytesExt; use byteorder_slice::result::ReadSlice; use byteorder_slice::ByteOrder; use derive_into_owned::IntoOwned; use super::block_common::{Block, PcapNgBlock}; use super::opt_common::{CustomBinaryOption, CustomUtf8Option, PcapNgOption, UnknownOption, WriteOptTo}; use crate::errors::PcapError; /// The Packet Block is obsolete, and MUST NOT be used in new files. /// Use the Enhanced Packet Block or Simple Packet Block instead. #[derive(Clone, Debug, IntoOwned, Eq, PartialEq)] pub struct PacketBlock<'a> { /// It specifies the interface this packet comes from. pub interface_id: u16, /// Local drop counter. /// /// It specifies the number of packets lost (by the interface and the operating system) /// between this packet and the preceding one. pub drop_count: u16, /// The timestamp is a single 64-bit unsigned integer that represents the number of units of time /// that have elapsed since 1970-01-01 00:00:00 UTC. pub timestamp: u64, /// Number of octets captured from the packet (i.e. the length of the Packet Data field). pub captured_len: u32, /// Actual length of the packet when it was transmitted on the network. pub original_len: u32, /// The data coming from the network, including link-layer headers. pub data: Cow<'a, [u8]>, /// Options pub options: Vec>, } impl<'a> PcapNgBlock<'a> for PacketBlock<'a> { fn from_slice(mut slice: &'a [u8]) -> Result<(&'a [u8], Self), PcapError> { if slice.len() < 20 { return Err(PcapError::InvalidField("EnhancedPacketBlock: block length length < 20")); } let interface_id = slice.read_u16::().unwrap(); let drop_count = slice.read_u16::().unwrap(); let timestamp = slice.read_u64::().unwrap(); let captured_len = slice.read_u32::().unwrap(); let original_len = slice.read_u32::().unwrap(); let pad_len = (4 - (captured_len as usize % 4)) % 4; let tot_len = captured_len as usize + pad_len; if slice.len() < tot_len { return Err(PcapError::InvalidField("EnhancedPacketBlock: captured_len + padding > block length")); } let data = &slice[..captured_len as usize]; slice = &slice[tot_len..]; let (slice, options) = PacketOption::opts_from_slice::(slice)?; let block = PacketBlock { interface_id, drop_count, timestamp, captured_len, original_len, data: Cow::Borrowed(data), options, }; Ok((slice, block)) } fn write_to(&self, writer: &mut W) -> IoResult { writer.write_u16::(self.interface_id)?; writer.write_u16::(self.drop_count)?; writer.write_u64::(self.timestamp)?; writer.write_u32::(self.captured_len)?; writer.write_u32::(self.original_len)?; writer.write_all(&self.data)?; let pad_len = (4 - (self.captured_len as usize % 4)) % 4; writer.write_all(&[0_u8; 3][..pad_len])?; let opt_len = PacketOption::write_opts_to::(&self.options, writer)?; Ok(20 + self.data.len() + pad_len + opt_len) } fn into_block(self) -> Block<'a> { Block::Packet(self) } } /// Packet Block option #[derive(Clone, Debug, IntoOwned, Eq, PartialEq)] pub enum PacketOption<'a> { /// Comment associated with the current block Comment(Cow<'a, str>), /// 32-bit flags word containing link-layer information. Flags(u32), /// Contains a hash of the packet. Hash(Cow<'a, [u8]>), /// Custom option containing binary octets in the Custom Data portion CustomBinary(CustomBinaryOption<'a>), /// Custom option containing a UTF-8 string in the Custom Data portion CustomUtf8(CustomUtf8Option<'a>), /// Unknown option Unknown(UnknownOption<'a>), } impl<'a> PcapNgOption<'a> for PacketOption<'a> { fn from_slice(code: u16, length: u16, mut slice: &'a [u8]) -> Result { let opt = match code { 1 => PacketOption::Comment(Cow::Borrowed(std::str::from_utf8(slice)?)), 2 => { if slice.len() != 4 { return Err(PcapError::InvalidField("PacketOption: Flags length != 4")); } PacketOption::Flags(slice.read_u32::().map_err(|_| PcapError::IncompleteBuffer)?) }, 3 => PacketOption::Hash(Cow::Borrowed(slice)), 2988 | 19372 => PacketOption::CustomUtf8(CustomUtf8Option::from_slice::(code, slice)?), 2989 | 19373 => PacketOption::CustomBinary(CustomBinaryOption::from_slice::(code, slice)?), _ => PacketOption::Unknown(UnknownOption::new(code, length, slice)), }; Ok(opt) } fn write_to(&self, writer: &mut W) -> IoResult { match self { PacketOption::Comment(a) => a.write_opt_to::(1, writer), PacketOption::Flags(a) => a.write_opt_to::(2, writer), PacketOption::Hash(a) => a.write_opt_to::(3, writer), PacketOption::CustomBinary(a) => a.write_opt_to::(a.code, writer), PacketOption::CustomUtf8(a) => a.write_opt_to::(a.code, writer), PacketOption::Unknown(a) => a.write_opt_to::(a.code, writer), } } } pcap-file-2.0.0/src/pcapng/blocks/section_header.rs000064400000000000000000000135751046102023000203340ustar 00000000000000//! Section Header Block. use std::borrow::Cow; use std::io::{Result as IoResult, Write}; use byteorder_slice::byteorder::WriteBytesExt; use byteorder_slice::result::ReadSlice; use byteorder_slice::{BigEndian, ByteOrder, LittleEndian}; use derive_into_owned::IntoOwned; use super::block_common::{Block, PcapNgBlock}; use super::opt_common::{CustomBinaryOption, CustomUtf8Option, PcapNgOption, UnknownOption, WriteOptTo}; use crate::errors::PcapError; use crate::Endianness; /// Section Header Block: it defines the most important characteristics of the capture file. #[derive(Clone, Debug, IntoOwned, Eq, PartialEq)] pub struct SectionHeaderBlock<'a> { /// Endianness of the section. pub endianness: Endianness, /// Major version of the format. /// Current value is 1. pub major_version: u16, /// Minor version of the format. /// Current value is 0. pub minor_version: u16, /// Length in bytes of the following section excluding this block. /// /// This block can be used to skip the section for faster navigation in /// large files. Length of -1i64 means that the length is unspecified. pub section_length: i64, /// Options pub options: Vec>, } impl<'a> PcapNgBlock<'a> for SectionHeaderBlock<'a> { fn from_slice(mut slice: &'a [u8]) -> Result<(&'a [u8], Self), PcapError> { if slice.len() < 16 { return Err(PcapError::InvalidField("SectionHeaderBlock: block length < 16")); } let magic = slice.read_u32::().unwrap(); let endianness = match magic { 0x1A2B3C4D => Endianness::Big, 0x4D3C2B1A => Endianness::Little, _ => return Err(PcapError::InvalidField("SectionHeaderBlock: invalid magic number")), }; let (rem, major_version, minor_version, section_length, options) = match endianness { Endianness::Big => parse_inner::(slice)?, Endianness::Little => parse_inner::(slice)?, }; let block = SectionHeaderBlock { endianness, major_version, minor_version, section_length, options }; return Ok((rem, block)); #[allow(clippy::type_complexity)] fn parse_inner(mut slice: &[u8]) -> Result<(&[u8], u16, u16, i64, Vec), PcapError> { let maj_ver = slice.read_u16::().unwrap(); let min_ver = slice.read_u16::().unwrap(); let sec_len = slice.read_i64::().unwrap(); let (rem, opts) = SectionHeaderOption::opts_from_slice::(slice)?; Ok((rem, maj_ver, min_ver, sec_len, opts)) } } fn write_to(&self, writer: &mut W) -> IoResult { match self.endianness { Endianness::Big => writer.write_u32::(0x1A2B3C4D)?, Endianness::Little => writer.write_u32::(0x1A2B3C4D)?, }; writer.write_u16::(self.major_version)?; writer.write_u16::(self.minor_version)?; writer.write_i64::(self.section_length)?; let opt_len = SectionHeaderOption::write_opts_to::(&self.options, writer)?; Ok(16 + opt_len) } fn into_block(self) -> Block<'a> { Block::SectionHeader(self) } } impl Default for SectionHeaderBlock<'static> { fn default() -> Self { Self { endianness: Endianness::Big, major_version: 1, minor_version: 0, section_length: -1, options: vec![], } } } /// Section Header Block options #[derive(Clone, Debug, IntoOwned, Eq, PartialEq)] pub enum SectionHeaderOption<'a> { /// Comment associated with the current block Comment(Cow<'a, str>), /// Description of the hardware used to create this section Hardware(Cow<'a, str>), /// Name of the operating system used to create this section OS(Cow<'a, str>), /// Name of the application used to create this section UserApplication(Cow<'a, str>), /// Custom option containing binary octets in the Custom Data portion CustomBinary(CustomBinaryOption<'a>), /// Custom option containing a UTF-8 string in the Custom Data portion CustomUtf8(CustomUtf8Option<'a>), /// Unknown option Unknown(UnknownOption<'a>), } impl<'a> PcapNgOption<'a> for SectionHeaderOption<'a> { fn from_slice(code: u16, length: u16, slice: &'a [u8]) -> Result { let opt = match code { 1 => SectionHeaderOption::Comment(Cow::Borrowed(std::str::from_utf8(slice)?)), 2 => SectionHeaderOption::Hardware(Cow::Borrowed(std::str::from_utf8(slice)?)), 3 => SectionHeaderOption::OS(Cow::Borrowed(std::str::from_utf8(slice)?)), 4 => SectionHeaderOption::UserApplication(Cow::Borrowed(std::str::from_utf8(slice)?)), 2988 | 19372 => SectionHeaderOption::CustomUtf8(CustomUtf8Option::from_slice::(code, slice)?), 2989 | 19373 => SectionHeaderOption::CustomBinary(CustomBinaryOption::from_slice::(code, slice)?), _ => SectionHeaderOption::Unknown(UnknownOption::new(code, length, slice)), }; Ok(opt) } fn write_to(&self, writer: &mut W) -> IoResult { match self { SectionHeaderOption::Comment(a) => a.write_opt_to::(1, writer), SectionHeaderOption::Hardware(a) => a.write_opt_to::(2, writer), SectionHeaderOption::OS(a) => a.write_opt_to::(3, writer), SectionHeaderOption::UserApplication(a) => a.write_opt_to::(4, writer), SectionHeaderOption::CustomBinary(a) => a.write_opt_to::(a.code, writer), SectionHeaderOption::CustomUtf8(a) => a.write_opt_to::(a.code, writer), SectionHeaderOption::Unknown(a) => a.write_opt_to::(a.code, writer), } } } pcap-file-2.0.0/src/pcapng/blocks/simple_packet.rs000064400000000000000000000031401046102023000201630ustar 00000000000000//! Simple Packet Block (SPB). use std::borrow::Cow; use std::io::{Result as IoResult, Write}; use byteorder_slice::byteorder::WriteBytesExt; use byteorder_slice::result::ReadSlice; use byteorder_slice::ByteOrder; use derive_into_owned::IntoOwned; use super::block_common::{Block, PcapNgBlock}; use crate::errors::PcapError; /// The Simple Packet Block (SPB) is a lightweight container for storing the packets coming from the network. /// /// Its presence is optional. #[derive(Clone, Debug, IntoOwned, Eq, PartialEq)] pub struct SimplePacketBlock<'a> { /// Actual length of the packet when it was transmitted on the network. pub original_len: u32, /// The data coming from the network, including link-layer headers. pub data: Cow<'a, [u8]>, } impl<'a> PcapNgBlock<'a> for SimplePacketBlock<'a> { fn from_slice(mut slice: &'a [u8]) -> Result<(&'a [u8], Self), PcapError> { if slice.len() < 4 { return Err(PcapError::InvalidField("SimplePacketBlock: block length < 4")); } let original_len = slice.read_u32::().unwrap(); let packet = SimplePacketBlock { original_len, data: Cow::Borrowed(slice) }; Ok((&[], packet)) } fn write_to(&self, writer: &mut W) -> IoResult { writer.write_u32::(self.original_len)?; writer.write_all(&self.data)?; let pad_len = (4 - (self.data.len() % 4)) % 4; writer.write_all(&[0_u8; 3][..pad_len])?; Ok(4 + self.data.len() + pad_len) } fn into_block(self) -> Block<'a> { Block::SimplePacket(self) } } pcap-file-2.0.0/src/pcapng/blocks/systemd_journal_export.rs000064400000000000000000000023231046102023000221700ustar 00000000000000//! Systemd Journal Export Block. use std::borrow::Cow; use std::io::{Result as IoResult, Write}; use byteorder_slice::ByteOrder; use derive_into_owned::IntoOwned; use super::block_common::{Block, PcapNgBlock}; use crate::errors::PcapError; /// The Systemd Journal Export Block is a lightweight containter for systemd Journal Export Format entry data. #[derive(Clone, Debug, IntoOwned, Eq, PartialEq)] pub struct SystemdJournalExportBlock<'a> { /// A journal entry as described in the Journal Export Format documentation. pub journal_entry: Cow<'a, [u8]>, } impl<'a> PcapNgBlock<'a> for SystemdJournalExportBlock<'a> { fn from_slice(slice: &'a [u8]) -> Result<(&'a [u8], Self), PcapError> { let packet = SystemdJournalExportBlock { journal_entry: Cow::Borrowed(slice) }; Ok((&[], packet)) } fn write_to(&self, writer: &mut W) -> IoResult { writer.write_all(&self.journal_entry)?; let pad_len = (4 - (self.journal_entry.len() % 4)) % 4; writer.write_all(&[0_u8; 3][..pad_len])?; Ok(self.journal_entry.len() + pad_len) } fn into_block(self) -> Block<'a> { Block::SystemdJournalExport(self) } } pcap-file-2.0.0/src/pcapng/blocks/unknown.rs000064400000000000000000000022221046102023000170420ustar 00000000000000//! Unknown Block. use std::borrow::Cow; use std::io::{Result as IoResult, Write}; use byteorder_slice::ByteOrder; use derive_into_owned::IntoOwned; use super::block_common::{Block, PcapNgBlock}; use crate::PcapError; /// Unknown block #[derive(Clone, Debug, IntoOwned, Eq, PartialEq)] pub struct UnknownBlock<'a> { /// Block type pub type_: u32, /// Block length pub length: u32, /// Block value pub value: Cow<'a, [u8]>, } impl<'a> UnknownBlock<'a> { /// Creates a new [`UnknownBlock`] pub fn new(type_: u32, length: u32, value: &'a [u8]) -> Self { UnknownBlock { type_, length, value: Cow::Borrowed(value) } } } impl<'a> PcapNgBlock<'a> for UnknownBlock<'a> { fn from_slice(_slice: &'a [u8]) -> Result<(&[u8], Self), PcapError> where Self: Sized, { unimplemented!("UnkknownBlock::::From_slice shouldn't be called") } fn write_to(&self, writer: &mut W) -> IoResult { writer.write_all(&self.value)?; Ok(self.value.len()) } fn into_block(self) -> Block<'a> { Block::Unknown(self) } } pcap-file-2.0.0/src/pcapng/mod.rs000064400000000000000000000003641046102023000146520ustar 00000000000000//! Contains the PcapNg parser, reader and writer pub mod blocks; pub use blocks::{Block, PcapNgBlock, RawBlock}; pub(crate) mod parser; pub use parser::*; pub(crate) mod reader; pub use reader::*; pub(crate) mod writer; pub use writer::*; pcap-file-2.0.0/src/pcapng/parser.rs000064400000000000000000000107671046102023000153770ustar 00000000000000use byteorder_slice::{BigEndian, ByteOrder, LittleEndian}; use super::blocks::block_common::{Block, RawBlock}; use super::blocks::enhanced_packet::EnhancedPacketBlock; use super::blocks::interface_description::InterfaceDescriptionBlock; use super::blocks::section_header::SectionHeaderBlock; use super::blocks::{INTERFACE_DESCRIPTION_BLOCK, SECTION_HEADER_BLOCK}; use crate::errors::PcapError; use crate::Endianness; /// Parses a PcapNg from a slice of bytes. /// /// You can match on [`PcapError::IncompleteBuffer`] to know if the parser need more data. /// /// # Example /// ```rust,no_run /// use std::fs::File; /// /// use pcap_file::pcapng::PcapNgParser; /// use pcap_file::PcapError; /// /// let pcap = std::fs::read("test.pcapng").expect("Error reading file"); /// let mut src = &pcap[..]; /// /// let (rem, mut pcapng_parser) = PcapNgParser::new(src).unwrap(); /// src = rem; /// /// loop { /// match pcapng_parser.next_block(src) { /// Ok((rem, block)) => { /// // Do something /// /// // Don't forget to update src /// src = rem; /// }, /// Err(PcapError::IncompleteBuffer) => { /// // Load more data into src /// }, /// Err(_) => { /// // Handle parsing error /// }, /// } /// } /// ``` pub struct PcapNgParser { section: SectionHeaderBlock<'static>, interfaces: Vec>, } impl PcapNgParser { /// Creates a new [`PcapNgParser`]. /// /// Parses the first block which must be a valid SectionHeaderBlock. pub fn new(src: &[u8]) -> Result<(&[u8], Self), PcapError> { // Always use BigEndian here because we can't know the SectionHeaderBlock endianness let (rem, section) = Block::from_slice::(src)?; let section = match section { Block::SectionHeader(section) => section.into_owned(), _ => return Err(PcapError::InvalidField("PcapNg: SectionHeader invalid or missing")), }; let parser = PcapNgParser { section, interfaces: vec![] }; Ok((rem, parser)) } /// Returns the remainder and the next [`Block`]. pub fn next_block<'a>(&mut self, src: &'a [u8]) -> Result<(&'a [u8], Block<'a>), PcapError> { // Read next Block match self.section.endianness { Endianness::Big => { let (rem, raw_block) = self.next_raw_block_inner::(src)?; let block = raw_block.try_into_block::()?; Ok((rem, block)) }, Endianness::Little => { let (rem, raw_block) = self.next_raw_block_inner::(src)?; let block = raw_block.try_into_block::()?; Ok((rem, block)) }, } } /// Returns the remainder and the next [`RawBlock`]. pub fn next_raw_block<'a>(&mut self, src: &'a [u8]) -> Result<(&'a [u8], RawBlock<'a>), PcapError> { // Read next Block match self.section.endianness { Endianness::Big => self.next_raw_block_inner::(src), Endianness::Little => self.next_raw_block_inner::(src), } } /// Inner function to parse the next raw block. fn next_raw_block_inner<'a, B: ByteOrder>(&mut self, src: &'a [u8]) -> Result<(&'a [u8], RawBlock<'a>), PcapError> { let (rem, raw_block) = RawBlock::from_slice::(src)?; match raw_block.type_ { SECTION_HEADER_BLOCK => { self.section = raw_block.clone().try_into_block::()?.into_owned().into_section_header().unwrap(); self.interfaces.clear(); }, INTERFACE_DESCRIPTION_BLOCK => { let interface = raw_block.clone().try_into_block::()?.into_owned().into_interface_description().unwrap(); self.interfaces.push(interface); }, _ => {}, } Ok((rem, raw_block)) } /// Returns the current [`SectionHeaderBlock`]. pub fn section(&self) -> &SectionHeaderBlock<'static> { &self.section } /// Returns all the current [`InterfaceDescriptionBlock`]. pub fn interfaces(&self) -> &[InterfaceDescriptionBlock<'static>] { &self.interfaces[..] } /// Returns the [`InterfaceDescriptionBlock`] corresponding to the given packet. pub fn packet_interface(&self, packet: &EnhancedPacketBlock) -> Option<&InterfaceDescriptionBlock> { self.interfaces.get(packet.interface_id as usize) } } pcap-file-2.0.0/src/pcapng/reader.rs000064400000000000000000000060201046102023000153300ustar 00000000000000use std::io::Read; use super::blocks::block_common::{Block, RawBlock}; use super::blocks::enhanced_packet::EnhancedPacketBlock; use super::blocks::interface_description::InterfaceDescriptionBlock; use super::blocks::section_header::SectionHeaderBlock; use super::PcapNgParser; use crate::errors::PcapError; use crate::read_buffer::ReadBuffer; /// Reads a PcapNg from a reader. /// /// # Example /// ```rust,no_run /// use std::fs::File; /// /// use pcap_file::pcapng::PcapNgReader; /// /// let file_in = File::open("test.pcapng").expect("Error opening file"); /// let mut pcapng_reader = PcapNgReader::new(file_in).unwrap(); /// /// // Read test.pcapng /// while let Some(block) = pcapng_reader.next_block() { /// //Check if there is no error /// let block = block.unwrap(); /// /// //Do something /// } /// ``` pub struct PcapNgReader { parser: PcapNgParser, reader: ReadBuffer, } impl PcapNgReader { /// Creates a new [`PcapNgReader`] from a reader. /// /// Parses the first block which must be a valid SectionHeaderBlock. pub fn new(reader: R) -> Result, PcapError> { let mut reader = ReadBuffer::new(reader); let parser = reader.parse_with(PcapNgParser::new)?; Ok(Self { parser, reader }) } /// Returns the next [`Block`]. pub fn next_block(&mut self) -> Option> { match self.reader.has_data_left() { Ok(has_data) => { if has_data { Some(self.reader.parse_with(|src| self.parser.next_block(src))) } else { None } }, Err(e) => Some(Err(PcapError::IoError(e))), } } /// Returns the next [`RawBlock`]. pub fn next_raw_block(&mut self) -> Option> { match self.reader.has_data_left() { Ok(has_data) => { if has_data { Some(self.reader.parse_with(|src| self.parser.next_raw_block(src))) } else { None } }, Err(e) => Some(Err(PcapError::IoError(e))), } } /// Returns the current [`SectionHeaderBlock`]. pub fn section(&self) -> &SectionHeaderBlock<'static> { self.parser.section() } /// Returns all the current [`InterfaceDescriptionBlock`]. pub fn interfaces(&self) -> &[InterfaceDescriptionBlock<'static>] { self.parser.interfaces() } /// Returns the [`InterfaceDescriptionBlock`] corresponding to the given packet pub fn packet_interface(&self, packet: &EnhancedPacketBlock) -> Option<&InterfaceDescriptionBlock> { self.interfaces().get(packet.interface_id as usize) } /// Consumes the [`Self`], returning the wrapped reader. pub fn into_inner(self) -> R { self.reader.into_inner() } /// Gets a reference to the wrapped reader. pub fn get_ref(&self) -> &R { self.reader.get_ref() } } pcap-file-2.0.0/src/pcapng/writer.rs000064400000000000000000000174461046102023000154200ustar 00000000000000use std::io::Write; use byteorder_slice::{BigEndian, ByteOrder, LittleEndian}; use super::blocks::block_common::{Block, PcapNgBlock}; use super::blocks::interface_description::InterfaceDescriptionBlock; use super::blocks::section_header::SectionHeaderBlock; use super::blocks::SECTION_HEADER_BLOCK; use super::RawBlock; use crate::{Endianness, PcapError, PcapResult}; /// Writes a PcapNg to a writer. /// /// # Examples /// ```rust,no_run /// use std::fs::File; /// /// use pcap_file::pcapng::{PcapNgReader, PcapNgWriter}; /// /// let file_in = File::open("test.pcapng").expect("Error opening file"); /// let mut pcapng_reader = PcapNgReader::new(file_in).unwrap(); /// /// let mut out = Vec::new(); /// let mut pcapng_writer = PcapNgWriter::new(out).unwrap(); /// /// // Read test.pcapng /// while let Some(block) = pcapng_reader.next_block() { /// // Check if there is no error /// let block = block.unwrap(); /// /// // Write back parsed Block /// pcapng_writer.write_block(&block).unwrap(); /// } /// ``` pub struct PcapNgWriter { section: SectionHeaderBlock<'static>, interfaces: Vec>, writer: W, } impl PcapNgWriter { /// Creates a new [`PcapNgWriter`] from an existing writer. /// /// Defaults to the native endianness of the CPU. /// /// Writes this global pcapng header to the file: /// ```rust, ignore /// Self { /// endianness: Endianness::Native, /// major_version: 1, /// minor_version: 0, /// section_length: -1, /// options: vec![] /// } /// ``` /// /// /// # Errors /// The writer can't be written to. pub fn new(writer: W) -> PcapResult { Self::with_endianness(writer, Endianness::native()) } /// Creates a new [`PcapNgWriter`] from an existing writer with the given endianness. pub fn with_endianness(writer: W, endianness: Endianness) -> PcapResult { let section = SectionHeaderBlock { endianness, ..Default::default() }; Self::with_section_header(writer, section) } /// Creates a new [`PcapNgWriter`] from an existing writer with the given section header. pub fn with_section_header(mut writer: W, section: SectionHeaderBlock<'static>) -> PcapResult { match section.endianness { Endianness::Big => section.clone().into_block().write_to::(&mut writer).map_err(PcapError::IoError)?, Endianness::Little => section.clone().into_block().write_to::(&mut writer).map_err(PcapError::IoError)?, }; Ok(Self { section, interfaces: vec![], writer }) } /// Writes a [`Block`]. /// /// # Example /// ```rust,no_run /// use std::borrow::Cow; /// use std::fs::File; /// use std::time::Duration; /// /// use pcap_file::pcapng::blocks::enhanced_packet::EnhancedPacketBlock; /// use pcap_file::pcapng::blocks::interface_description::InterfaceDescriptionBlock; /// use pcap_file::pcapng::{PcapNgBlock, PcapNgWriter}; /// use pcap_file::DataLink; /// /// let data = [0u8; 10]; /// /// let interface = InterfaceDescriptionBlock { /// linktype: DataLink::ETHERNET, /// snaplen: 0xFFFF, /// options: vec![], /// }; /// /// let packet = EnhancedPacketBlock { /// interface_id: 0, /// timestamp: Duration::from_secs(0), /// original_len: data.len() as u32, /// data: Cow::Borrowed(&data), /// options: vec![], /// }; /// /// let file = File::create("out.pcap").expect("Error creating file"); /// let mut pcap_ng_writer = PcapNgWriter::new(file).unwrap(); /// /// pcap_ng_writer.write_block(&interface.into_block()).unwrap(); /// pcap_ng_writer.write_block(&packet.into_block()).unwrap(); /// ``` pub fn write_block(&mut self, block: &Block) -> PcapResult { match block { Block::SectionHeader(a) => { self.section = a.clone().into_owned(); self.interfaces.clear(); }, Block::InterfaceDescription(a) => { self.interfaces.push(a.clone().into_owned()); }, Block::InterfaceStatistics(a) => { if a.interface_id as usize >= self.interfaces.len() { return Err(PcapError::InvalidInterfaceId(a.interface_id)); } }, Block::EnhancedPacket(a) => { if a.interface_id as usize >= self.interfaces.len() { return Err(PcapError::InvalidInterfaceId(a.interface_id)); } }, _ => (), } match self.section.endianness { Endianness::Big => block.write_to::(&mut self.writer).map_err(PcapError::IoError), Endianness::Little => block.write_to::(&mut self.writer).map_err(PcapError::IoError), } } /// Writes a [`PcapNgBlock`]. /// /// # Example /// ```rust,no_run /// use std::borrow::Cow; /// use std::fs::File; /// use std::time::Duration; /// /// use pcap_file::pcapng::blocks::enhanced_packet::EnhancedPacketBlock; /// use pcap_file::pcapng::blocks::interface_description::InterfaceDescriptionBlock; /// use pcap_file::pcapng::{PcapNgBlock, PcapNgWriter}; /// use pcap_file::DataLink; /// /// let data = [0u8; 10]; /// /// let interface = InterfaceDescriptionBlock { /// linktype: DataLink::ETHERNET, /// snaplen: 0xFFFF, /// options: vec![], /// }; /// /// let packet = EnhancedPacketBlock { /// interface_id: 0, /// timestamp: Duration::from_secs(0), /// original_len: data.len() as u32, /// data: Cow::Borrowed(&data), /// options: vec![], /// }; /// /// let file = File::create("out.pcap").expect("Error creating file"); /// let mut pcap_ng_writer = PcapNgWriter::new(file).unwrap(); /// /// pcap_ng_writer.write_pcapng_block(interface).unwrap(); /// pcap_ng_writer.write_pcapng_block(packet).unwrap(); /// ``` pub fn write_pcapng_block<'a, B: PcapNgBlock<'a>>(&mut self, block: B) -> PcapResult { self.write_block(&block.into_block()) } /// Writes a [`RawBlock`]. /// /// Doesn't check the validity of the written blocks. pub fn write_raw_block(&mut self, block: &RawBlock) -> PcapResult { return match self.section.endianness { Endianness::Big => inner::(&mut self.section, block, &mut self.writer), Endianness::Little => inner::(&mut self.section, block, &mut self.writer), }; fn inner(section: &mut SectionHeaderBlock, block: &RawBlock, writer: &mut W) -> PcapResult { if block.type_ == SECTION_HEADER_BLOCK { *section = block.clone().try_into_block::()?.into_owned().into_section_header().unwrap(); } block.write_to::(writer).map_err(PcapError::IoError) } } /// Consumes [`Self`], returning the wrapped writer. pub fn into_inner(self) -> W { self.writer } /// Gets a reference to the underlying writer. pub fn get_ref(&self) -> &W { &self.writer } /// Gets a mutable reference to the underlying writer. /// /// You should not be used unless you really know what you're doing pub fn get_mut(&mut self) -> &mut W { &mut self.writer } /// Returns the current [`SectionHeaderBlock`]. pub fn section(&self) -> &SectionHeaderBlock<'static> { &self.section } /// Returns all the current [`InterfaceDescriptionBlock`]. pub fn interfaces(&self) -> &[InterfaceDescriptionBlock<'static>] { &self.interfaces } } pcap-file-2.0.0/src/read_buffer.rs000064400000000000000000000112421046102023000150640ustar 00000000000000use std::io::{Error, ErrorKind, Read}; use crate::PcapError; /// Internal structure that bufferize its input and allow to parse element from its buffer. #[derive(Debug)] pub(crate) struct ReadBuffer { /// Reader from which we read the data from reader: R, /// Internal buffer buffer: Vec, /// Current start position of the buffer pos: usize, /// Current end position of the buffer len: usize, } impl ReadBuffer { /// Creates a new ReadBuffer with capacity of 8_000_000 pub fn new(reader: R) -> Self { Self::with_capacity(reader, 8_000_000) } /// Creates a new ReadBuffer with the given capacity pub fn with_capacity(reader: R, capacity: usize) -> Self { Self { reader, buffer: vec![0_u8; capacity], pos: 0, len: 0 } } /// Parse data from the internal buffer /// /// Safety /// /// The parser must NOT keep a reference to the buffer in input. pub fn parse_with<'a, 'b: 'a, 'c: 'a, F, O>(&'c mut self, mut parser: F) -> Result where F: FnMut(&'a [u8]) -> Result<(&'a [u8], O), PcapError>, F: 'b, O: 'a, { loop { let buf = &self.buffer[self.pos..self.len]; // Sound because 'b and 'c must outlive 'a so the buffer cannot be modified while someone has a ref on it let buf: &'a [u8] = unsafe { std::mem::transmute(buf) }; match parser(buf) { Ok((rem, value)) => { self.advance_with_slice(rem); return Ok(value); }, Err(PcapError::IncompleteBuffer) => { // The parsed data len should never be more than the buffer capacity if buf.len() == self.buffer.len() { return Err(PcapError::IoError(Error::from(ErrorKind::UnexpectedEof))); } let nb_read = self.fill_buf().map_err(PcapError::IoError)?; if nb_read == 0 { return Err(PcapError::IoError(Error::from(ErrorKind::UnexpectedEof))); } }, Err(e) => return Err(e), } } } /// Fill the inner buffer. /// Copy the remaining data inside buffer at its start and the fill the end part with data from the reader. fn fill_buf(&mut self) -> Result { // Copy the remaining data to the start of the buffer let rem_len = unsafe { let buf_ptr_mut = self.buffer.as_mut_ptr(); let rem_ptr_mut = buf_ptr_mut.add(self.pos); std::ptr::copy(rem_ptr_mut, buf_ptr_mut, self.len - self.pos); self.len - self.pos }; let nb_read = self.reader.read(&mut self.buffer[rem_len..])?; self.len = rem_len + nb_read; self.pos = 0; Ok(nb_read) } /// Advance the internal buffer position. fn advance(&mut self, nb_bytes: usize) { assert!(self.pos + nb_bytes <= self.len); self.pos += nb_bytes; } /// Advance the internal buffer position. fn advance_with_slice(&mut self, rem: &[u8]) { // Compute the length between the buffer and the slice let diff_len = (rem.as_ptr() as usize) .checked_sub(self.buffer().as_ptr() as usize) .expect("Rem is not a sub slice of self.buffer"); self.advance(diff_len) } /// Return the valid data of the internal buffer pub fn buffer(&self) -> &[u8] { &self.buffer[self.pos..self.len] } /// Return true there are some data that can be read pub fn has_data_left(&mut self) -> Result { // The buffer can be empty and the reader can still have data if self.buffer().is_empty() { let nb_read = self.fill_buf()?; if nb_read == 0 { return Ok(false); } } Ok(true) } /// Return the inner reader pub fn into_inner(self) -> R { self.reader } /// Return a reference over the inner reader pub fn get_ref(&self) -> &R { &self.reader } } #[cfg(test)] mod test { /* // Shouldn't compile #[test] fn parse_with_safety() { let a = &[0_u8; 10]; let b = &mut &a[..]; let input = vec![1_u8; 100]; let input_read = &mut &input[..]; let mut reader = super::ReadBuffer::new(input_read); unsafe { reader.parse_with(|buf| { *b = buf; Ok((buf, ())) }); } unsafe { reader.has_data_left(); } println!("{:?}", b); } */ }