pcap_rs/
lib.rs

1#![cfg_attr(feature = "core", feature(no_std))]
2
3//! This crate provides simple functions to parse pcap files.
4//!
5//! This implementation is based on the documentation available on wireshark's
6//! [wiki](https://wiki.wireshark.org/Development/LibpcapFileFormat).
7
8#[cfg(feature = "core")]
9extern crate collections;
10
11#[cfg(not(feature = "core"))]
12pub mod iter;
13
14use nom::{
15    call, do_parse, i32, map, named_args, named_attr,
16    number::{streaming::be_u32, Endianness},
17    switch, take, u16, u32, verify,
18};
19
20/// Enumerates all frame format supported by pcap.
21#[allow(non_camel_case_types)]
22#[derive(Debug, PartialEq, Copy, Clone)]
23pub enum LinkType {
24    NULL,
25    ETHERNET,
26    AX25,
27    IEEE802_5,
28    ARCNET_BSD,
29    SLIP,
30    PPP,
31    FDDI,
32    PPP_HDLC,
33    PPP_ETHER,
34    ATM_RFC1483,
35    RAW,
36    C_HDLC,
37    IEEE802_11,
38    FRELAY,
39    LOOP,
40    LINUX_SLL,
41    LTALK,
42    PFLOG,
43    IEEE802_11_PRISM,
44    IP_OVER_FC,
45    SUNATM,
46    IEEE802_11_RADIOTAP,
47    ARCNET_LINUX,
48    APPLE_IP_OVER_IEEE1394,
49    MTP2_WITH_PHDR,
50    MTP2,
51    MTP3,
52    SCCP,
53    DOCSIS,
54    LINUX_IRDA,
55    USER0,
56    USER1,
57    USER2,
58    USER3,
59    USER4,
60    USER5,
61    USER6,
62    USER7,
63    USER8,
64    USER9,
65    USER10,
66    USER11,
67    USER12,
68    USER13,
69    USER14,
70    USER15,
71    IEEE802_11_AVS,
72    BACNET_MS_TP,
73    PPP_PPPD,
74    GPRS_LLC,
75    GPF_T,
76    GPF_F,
77    LINUX_LAPD,
78    BLUETOOTH_HCI_H4,
79    USB_LINUX,
80    PPI,
81    IEEE802_15_4,
82    SITA,
83    ERF,
84    BLUETOOTH_HCI_H4_WITH_PHDR,
85    AX25_KISS,
86    LAPD,
87    PPP_WITH_DIR,
88    C_HDLC_WITH_DIR,
89    FRELAY_WITH_DIR,
90    IPMB_LINUX,
91    IEEE802_15_4_NONASK_PHY,
92    USB_LINUX_MMAPPED,
93    FC_2,
94    FC_2_WITH_FRAME_DELIMS,
95    IPNET,
96    CAN_SOCKETCAN,
97    IPV4,
98    IPV6,
99    IEEE802_15_4_NOFCS,
100    DBUS,
101    DVB_CI,
102    MUX27010,
103    STANAG_5066_D_PDU,
104    NFLOG,
105    NETANALYZER,
106    NETANALYZER_TRANSPARENT,
107    IPOIB,
108    MPEG_2_TS,
109    NG40,
110    NFC_LLCP,
111    INFINIBAND,
112    SCTP,
113    USBPCAP,
114    RTAC_SERIAL,
115    BLUETOOTH_LE_LL,
116    NETLINK,
117    BLUETOOTH_LINUX_MONITOR,
118    BLUETOOTH_BREDR_BB,
119    BLUETOOTH_LE_LL_WITH_PHDR,
120    PROFIBUS_DL,
121    PKTAP,
122    EPON,
123    IPMI_HPM_2,
124    ZWAVE_R1_R2,
125    ZWAVE_R3,
126    WATTSTOPPER_DLM,
127    ISO_14443,
128    RDS,
129    USB_DARWIN,
130    SDLC,
131    UNKNOWN,
132}
133
134impl From<u32> for LinkType {
135    fn from(n: u32) -> LinkType {
136        match n {
137            0 => LinkType::NULL,
138            1 => LinkType::ETHERNET,
139            3 => LinkType::AX25,
140            6 => LinkType::IEEE802_5,
141            7 => LinkType::ARCNET_BSD,
142            8 => LinkType::SLIP,
143            9 => LinkType::PPP,
144            10 => LinkType::FDDI,
145            50 => LinkType::PPP_HDLC,
146            51 => LinkType::PPP_ETHER,
147            100 => LinkType::ATM_RFC1483,
148            101 => LinkType::RAW,
149            104 => LinkType::C_HDLC,
150            105 => LinkType::IEEE802_11,
151            107 => LinkType::FRELAY,
152            108 => LinkType::LOOP,
153            113 => LinkType::LINUX_SLL,
154            114 => LinkType::LTALK,
155            117 => LinkType::PFLOG,
156            119 => LinkType::IEEE802_11_PRISM,
157            122 => LinkType::IP_OVER_FC,
158            123 => LinkType::SUNATM,
159            127 => LinkType::IEEE802_11_RADIOTAP,
160            129 => LinkType::ARCNET_LINUX,
161            138 => LinkType::APPLE_IP_OVER_IEEE1394,
162            139 => LinkType::MTP2_WITH_PHDR,
163            140 => LinkType::MTP2,
164            141 => LinkType::MTP3,
165            142 => LinkType::SCCP,
166            143 => LinkType::DOCSIS,
167            144 => LinkType::LINUX_IRDA,
168            147 => LinkType::USER0,
169            148 => LinkType::USER1,
170            149 => LinkType::USER2,
171            150 => LinkType::USER3,
172            151 => LinkType::USER4,
173            152 => LinkType::USER5,
174            153 => LinkType::USER6,
175            154 => LinkType::USER7,
176            155 => LinkType::USER8,
177            156 => LinkType::USER9,
178            157 => LinkType::USER10,
179            158 => LinkType::USER11,
180            159 => LinkType::USER12,
181            160 => LinkType::USER13,
182            161 => LinkType::USER14,
183            162 => LinkType::USER15,
184            163 => LinkType::IEEE802_11_AVS,
185            165 => LinkType::BACNET_MS_TP,
186            166 => LinkType::PPP_PPPD,
187            169 => LinkType::GPRS_LLC,
188            170 => LinkType::GPF_T,
189            171 => LinkType::GPF_F,
190            177 => LinkType::LINUX_LAPD,
191            187 => LinkType::BLUETOOTH_HCI_H4,
192            189 => LinkType::USB_LINUX,
193            192 => LinkType::PPI,
194            195 => LinkType::IEEE802_15_4,
195            196 => LinkType::SITA,
196            197 => LinkType::ERF,
197            201 => LinkType::BLUETOOTH_HCI_H4_WITH_PHDR,
198            202 => LinkType::AX25_KISS,
199            203 => LinkType::LAPD,
200            204 => LinkType::PPP_WITH_DIR,
201            205 => LinkType::C_HDLC_WITH_DIR,
202            206 => LinkType::FRELAY_WITH_DIR,
203            209 => LinkType::IPMB_LINUX,
204            215 => LinkType::IEEE802_15_4_NONASK_PHY,
205            220 => LinkType::USB_LINUX_MMAPPED,
206            224 => LinkType::FC_2,
207            225 => LinkType::FC_2_WITH_FRAME_DELIMS,
208            226 => LinkType::IPNET,
209            227 => LinkType::CAN_SOCKETCAN,
210            228 => LinkType::IPV4,
211            229 => LinkType::IPV6,
212            230 => LinkType::IEEE802_15_4_NOFCS,
213            231 => LinkType::DBUS,
214            235 => LinkType::DVB_CI,
215            236 => LinkType::MUX27010,
216            237 => LinkType::STANAG_5066_D_PDU,
217            239 => LinkType::NFLOG,
218            240 => LinkType::NETANALYZER,
219            241 => LinkType::NETANALYZER_TRANSPARENT,
220            242 => LinkType::IPOIB,
221            243 => LinkType::MPEG_2_TS,
222            244 => LinkType::NG40,
223            245 => LinkType::NFC_LLCP,
224            247 => LinkType::INFINIBAND,
225            248 => LinkType::SCTP,
226            249 => LinkType::USBPCAP,
227            250 => LinkType::RTAC_SERIAL,
228            251 => LinkType::BLUETOOTH_LE_LL,
229            253 => LinkType::NETLINK,
230            254 => LinkType::BLUETOOTH_LINUX_MONITOR,
231            255 => LinkType::BLUETOOTH_BREDR_BB,
232            256 => LinkType::BLUETOOTH_LE_LL_WITH_PHDR,
233            257 => LinkType::PROFIBUS_DL,
234            258 => LinkType::PKTAP,
235            259 => LinkType::EPON,
236            260 => LinkType::IPMI_HPM_2,
237            261 => LinkType::ZWAVE_R1_R2,
238            262 => LinkType::ZWAVE_R3,
239            263 => LinkType::WATTSTOPPER_DLM,
240            264 => LinkType::ISO_14443,
241            265 => LinkType::RDS,
242            266 => LinkType::USB_DARWIN,
243            268 => LinkType::SDLC,
244            _ => LinkType::UNKNOWN,
245        }
246    }
247}
248
249/// Header of the pcap file.
250///
251/// The type of frame contained in the records is described by the field `network`.
252#[derive(PartialEq, Debug)]
253pub struct Header {
254    /// Major version of this file format.
255    pub major: u16,
256    /// Minor version of this file format.
257    pub minor: u16,
258    /// The correction time in seconds between GMT (UTC) and the local timezone of the following
259    /// packet header timestamps.
260    ///
261    /// **Examples**: If the timestamps are in GMT (UTC), `this_zone` is simply 0. If the timestamps are
262    /// in Central European time (Amsterdam, Berlin, ...) which is GMT + 1:00, `this_zone` must be
263    /// -3600. In practice, time stamps are always in GMT, so `this_zone` is always 0.
264    pub this_zone: i32,
265    /// In theory, the accuracy of time stamps in the capture; in practice, all tools set it to 0.
266    pub sigfigs: u32,
267    /// Maximum length of a captured packet.
268    /// If the initial packet is bigger than this value it will be truncated.
269    pub snaplen: u32,
270    pub network: LinkType,
271    pub nano_sec: bool,
272    pub endianness: Endianness,
273}
274
275/// A single Record in the file.
276#[derive(PartialEq, Debug)]
277pub struct Record {
278    pub ts_sec: u32,
279    pub ts_nanosec: u32,
280    pub orig_len: u32,
281    pub data: Vec<u8>,
282}
283
284named_args!(parse_header_e(e: Endianness, nsec: bool)<Header>,
285    do_parse!(
286        major: u16!(e) >>
287        minor: u16!(e) >>
288        this_zone: i32!(e) >>
289        sigfigs: u32!(e) >>
290        snaplen: u32!(e) >>
291        network: verify!(
292            map!(u32!(e), LinkType::from),
293            |val:&LinkType| { *val != LinkType::UNKNOWN }
294        ) >>
295        (Header {
296            major: major,
297            minor: minor,
298            this_zone: this_zone,
299            sigfigs: sigfigs,
300            snaplen: snaplen,
301            network: network,
302            nano_sec: nsec,
303            endianness: e
304        })
305    )
306);
307
308named_attr!(
309/// Parses a header.
310///
311/// This is always the first item of the file. This amongst other information extracts the
312/// endianness and whether the time is expressed in micro or nano-second from the `magic number`™ .
313#[doc=""],
314pub parse_header<Header>, switch!(be_u32,
315    0xa1b2c3d4 => call!(parse_header_e, Endianness::Big, false)    | // straight sec
316    0xd4c3b2a1 => call!(parse_header_e, Endianness::Little, false) | // reverse  sec
317    0xa1b23c4d => call!(parse_header_e, Endianness::Big, true)     | // straight usec
318    0x4d3cb2a1 => call!(parse_header_e, Endianness::Little, true)    // reverse  usec
319));
320
321named_args!(pub parse_record(e: Endianness, nano_sec: bool)<Record>, do_parse!(
322    ts_sec: u32!(e) >>
323    ts_subsec: u32!(e) >>
324    incl_len: u32!(e) >>
325    orig_len: u32!(e) >>
326    data: take!(incl_len) >>
327
328    (Record {
329        ts_sec: ts_sec,
330        ts_nanosec: if nano_sec {ts_subsec} else {ts_subsec*1000},
331        orig_len: orig_len,
332        data: Vec::from(data)
333    })
334));
335
336#[cfg(test)]
337mod tests {
338    use super::*;
339
340    #[test]
341    fn parse_header_be_usec() {
342        let i = b"\xa1\xb2\xc3\xd4\x00\x02\x00\x04\xFF\xFF\xFF\xFF\
343                  \x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x0a";
344        let h = Header {
345            major: 2,
346            minor: 4,
347            this_zone: -1,
348            sigfigs: 1,
349            snaplen: 1,
350            network: LinkType::ETHERNET,
351            nano_sec: false,
352            endianness: Endianness::Big,
353        };
354        assert_eq!(parse_header(&i[..]), Ok((&[10][..], h)));
355    }
356    #[test]
357    fn parse_header_le_usec() {
358        let i = b"\xd4\xc3\xb2\xa1\x02\x00\x04\x00\xFF\xFF\xFF\xFF\
359                  \x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x0a";
360        let h = Header {
361            major: 2,
362            minor: 4,
363            this_zone: -1,
364            sigfigs: 1,
365            snaplen: 1,
366            network: LinkType::ETHERNET,
367            nano_sec: false,
368            endianness: Endianness::Little,
369        };
370        assert_eq!(parse_header(&i[..]), Ok((&[10][..], h)));
371    }
372
373    #[test]
374    fn parse_header_be_nsec() {
375        let i = b"\xa1\xb2\x3c\x4d\x00\x02\x00\x04\xFF\xFF\xFF\xFF\
376                  \x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x0a";
377        let h = Header {
378            major: 2,
379            minor: 4,
380            this_zone: -1,
381            sigfigs: 1,
382            snaplen: 1,
383            network: LinkType::ETHERNET,
384            nano_sec: true,
385            endianness: Endianness::Big,
386        };
387        assert_eq!(parse_header(&i[..]), Ok((&[10][..], h)));
388    }
389    #[test]
390    fn parse_header_le_nsec() {
391        let i = b"\x4d\x3c\xb2\xa1\x02\x00\x04\x00\xFF\xFF\xFF\xFF\
392                  \x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x0a";
393        let h = Header {
394            major: 2,
395            minor: 4,
396            this_zone: -1,
397            sigfigs: 1,
398            snaplen: 1,
399            network: LinkType::ETHERNET,
400            nano_sec: true,
401            endianness: Endianness::Little,
402        };
403        assert_eq!(parse_header(&i[..]), Ok((&[10][..], h)));
404    }
405
406    #[test]
407    fn parse_record_be_empty() {
408        let i = b"\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\
409                  \x00\x00\x00\x00\x0a";
410        let r = Record {
411            ts_sec: 1,
412            ts_nanosec: 0,
413            orig_len: 0,
414            data: Vec::new(),
415        };
416        assert_eq!(
417            parse_record(&i[..], Endianness::Big, false),
418            Ok((&[10][..], r))
419        );
420    }
421    #[test]
422    fn parse_record_be_some_orig_data_zero_incl() {
423        let i = b"\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x00\
424                  \x00\x00\x00\x03\x0a";
425        let r = Record {
426            ts_sec: 1,
427            ts_nanosec: 2,
428            orig_len: 3,
429            data: Vec::new(),
430        };
431        assert_eq!(
432            parse_record(&i[..], Endianness::Big, true),
433            Ok((&[10][..], r))
434        );
435    }
436    #[test]
437    fn parse_record_be_some_orig_data_parially_incl() {
438        let i = b"\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x02\
439                  \x00\x00\x00\x03\x0a\x0b\x80";
440        let r = Record {
441            ts_sec: 1,
442            ts_nanosec: 2,
443            orig_len: 3,
444            data: vec![10, 11],
445        };
446        assert_eq!(
447            parse_record(&i[..], Endianness::Big, true),
448            Ok((&[128][..], r))
449        );
450    }
451    #[test]
452    fn parse_record_be_all_data_incl() {
453        let i = b"\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\
454                  \x00\x00\x00\x03\x0a\x0b\x0c\x80";
455        let r = Record {
456            ts_sec: 1,
457            ts_nanosec: 2,
458            orig_len: 3,
459            data: vec![10, 11, 12],
460        };
461        assert_eq!(
462            parse_record(&i[..], Endianness::Big, true),
463            Ok((&[128][..], r))
464        );
465    }
466
467    #[test]
468    fn parse_record_le_empty() {
469        let i = b"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
470                  \x00\x00\x00\x00\x0a";
471        let r = Record {
472            ts_sec: 1,
473            ts_nanosec: 0,
474            orig_len: 0,
475            data: Vec::new(),
476        };
477        assert_eq!(
478            parse_record(&i[..], Endianness::Little, true),
479            Ok((&[10][..], r))
480        );
481    }
482    #[test]
483    fn parse_record_le_some_orig_data_zero_incl() {
484        let i = b"\x01\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\
485                  \x03\x00\x00\x00\x0a";
486        let r = Record {
487            ts_sec: 1,
488            ts_nanosec: 2,
489            orig_len: 3,
490            data: Vec::new(),
491        };
492        assert_eq!(
493            parse_record(&i[..], Endianness::Little, true),
494            Ok((&[10][..], r))
495        );
496    }
497    #[test]
498    fn parse_record_le_some_orig_data_parially_incl() {
499        let i = b"\x01\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\
500                  \x03\x00\x00\x00\x0a\x0b\x80";
501        let r = Record {
502            ts_sec: 1,
503            ts_nanosec: 2,
504            orig_len: 3,
505            data: vec![10, 11],
506        };
507        assert_eq!(
508            parse_record(&i[..], Endianness::Little, true),
509            Ok((&[128][..], r))
510        );
511    }
512    #[test]
513    fn parse_record_le_all_data_incl() {
514        let i = b"\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\
515                  \x03\x00\x00\x00\x0a\x0b\x0c\x80";
516        let r = Record {
517            ts_sec: 1,
518            ts_nanosec: 2,
519            orig_len: 3,
520            data: vec![10, 11, 12],
521        };
522        assert_eq!(
523            parse_record(&i[..], Endianness::Little, true),
524            Ok((&[128][..], r))
525        );
526    }
527    #[test]
528    fn parse_record_le_all_data_incl_usec() {
529        let i = b"\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\
530                  \x03\x00\x00\x00\x0a\x0b\x0c\x80";
531        let r = Record {
532            ts_sec: 1,
533            ts_nanosec: 2000,
534            orig_len: 3,
535            data: vec![10, 11, 12],
536        };
537        assert_eq!(
538            parse_record(&i[..], Endianness::Little, false),
539            Ok((&[128][..], r))
540        );
541    }
542
543    #[test]
544    fn parse_all_link_type() {
545        for j in 0..270u32 {
546            let linktype = LinkType::from(j);
547            if let LinkType::UNKNOWN = linktype {
548                continue;
549            }
550
551            let mut i = b"\xa1\xb2\xc3\xd4\x00\x02\x00\x04\xFF\xFF\xFF\xFF\
552                      \x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00"
553                .to_vec();
554            i[22] = (j >> 8) as u8;
555            i[23] = j as u8;
556
557            let h = Header {
558                major: 2,
559                minor: 4,
560                this_zone: -1,
561                sigfigs: 1,
562                snaplen: 1,
563                network: linktype,
564                nano_sec: false,
565                endianness: Endianness::Big,
566            };
567            assert_eq!(parse_header(&i[..]), Ok((&[][..], h)));
568        }
569    }
570}