1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
use crate::Error;
use nom::*;

const MAGIC_NUMBER: u32 = 0xA1B2C3D4u32;
#[cfg(target_endian = "little")]
pub const NATIVE_ENDIAN: Endianness = Endianness::Little;
#[cfg(target_endian = "big")]
pub const NATIVE_ENDIAN: Endianness = Endianness::Big;

///
/// Global header associated with libpcap capture files
///
#[allow(unused)]
#[derive(Clone, Copy, Debug)]
pub struct GlobalHeader {
    pub endianness: Endianness,
    pub version_major: u16,
    pub version_minor: u16,
    pub zone: i32,
    pub sig_figs: i32,
    pub snap_length: u32,
    pub network: u32,
}

impl Default for GlobalHeader {
    fn default() -> Self {
        Self {
            endianness: NATIVE_ENDIAN,
            version_major: 2,
            version_minor: 4,
            zone: 0,
            sig_figs: 0,
            snap_length: 1500,
            network: 1,
        }
    }
}

impl GlobalHeader {
    pub fn parse<'a>(input: &'a [u8]) -> Result<(&'a [u8], GlobalHeader), Error> {
        do_parse!(
            input,
            endianness: map!(u32!(NATIVE_ENDIAN), |e| {
                let res = if e == MAGIC_NUMBER {
                    NATIVE_ENDIAN
                } else if NATIVE_ENDIAN == Endianness::Little {
                    Endianness::Big
                } else {
                    Endianness::Little
                };
                #[cfg(feature = "log-errors")]
                debug!("Using endianness {:?} read {:02x} compared to magic number {:02x}, setting endianness to {:?}", NATIVE_ENDIAN, e, MAGIC_NUMBER, res);
                res
            }) >> version_major: u16!(endianness)
                >> version_minor: u16!(endianness)
                >> zone: i32!(endianness)
                >> sig_figs: i32!(endianness)
                >> snap_length: u32!(endianness)
                >> network: u32!(endianness)
                >> (GlobalHeader {
                    endianness: endianness,
                    version_major: version_major,
                    version_minor: version_minor,
                    zone: zone,
                    sig_figs: sig_figs,
                    snap_length: snap_length,
                    network: network
                })
        ).map_err(Error::from)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[cfg(target_endian = "little")]
    const RAW_DATA: &'static [u8] = &[
        0xD4u8, 0xC3u8, 0xB2u8, 0xA1u8, //magic number
        0x04u8, 0x00u8, //version major, 4
        0x02u8, 0x00u8, //version minor, 2
        0x00u8, 0x00u8, 0x00u8, 0x00u8, //zone, 0
        0x04u8, 0x00u8, 0x00u8, 0x00u8, //sig figs, 4
        0x13u8, 0x06u8, 0x00u8, 0x00u8, //snap length, 1555
        0x02u8, 0x00u8, 0x00u8, 0x00u8, //network, 2
    ];
    #[cfg(target_endian = "little")]
    const RAW_DATA_REVERSED: &'static [u8] = &[
        0x1Au8, 0x2Bu8, 0x3Cu8, 0x4Du8, //magic number
        0x00u8, 0x04u8, //version major, 4
        0x00u8, 0x02u8, //version minor, 2
        0x00u8, 0x00u8, 0x00u8, 0x00u8, //zone, 0
        0x00u8, 0x00u8, 0x00u8, 0x04u8, //sig figs, 4
        0x00u8, 0x00u8, 0x06u8, 0x13u8, //snap length, 1555
        0x00u8, 0x00u8, 0x00u8, 0x02u8, //network, 2
    ];
    #[cfg(target_endian = "big")]
    const RAW_DATA: &'static [u8] = &[
        0x1Au8, 0x2Bu8, 0x3Cu8, 0x4Du8, //magic number
        0x00u8, 0x04u8, //version major, 4
        0x00u8, 0x02u8, //version minor, 2
        0x00u8, 0x00u8, 0x00u8, 0x00u8, //zone, 0
        0x00u8, 0x00u8, 0x00u8, 0x04u8, //sig figs, 4
        0x00u8, 0x00u8, 0x06u8, 0x13u8, //snap length, 1555
        0x00u8, 0x00u8, 0x00u8, 0x02u8, //network, 2
    ];
    #[cfg(target_endian = "big")]
    const RAW_DATA_REVERSED: &'static [u8] = &[
        0xD4u8, 0xC3u8, 0xB2u8, 0xA1u8, //magic number
        0x04u8, 0x00u8, //version major, 4
        0x02u8, 0x00u8, //version minor, 2
        0x00u8, 0x00u8, 0x00u8, 0x00u8, //zone, 0
        0x04u8, 0x00u8, 0x00u8, 0x00u8, //sig figs, 4
        0x13u8, 0x06u8, 0x00u8, 0x00u8, //snap length, 1555
        0x02u8, 0x00u8, 0x00u8, 0x00u8, //network, 2
    ];

    #[test]
    fn global_header_native_endian() {
        let _ = env_logger::try_init();

        let (rem, gh) = GlobalHeader::parse(RAW_DATA).expect("Failed to parse header");

        assert!(rem.is_empty());
        assert_eq!(gh.version_major, 4);
        assert_eq!(gh.version_minor, 2);
        assert_eq!(gh.endianness, NATIVE_ENDIAN);
        assert_eq!(gh.snap_length, 1555);
    }

    #[test]
    fn global_header_not_native_endian() {
        let (rem, gh) = GlobalHeader::parse(RAW_DATA_REVERSED).expect("Failed to parse header");

        let expected_endianness = match NATIVE_ENDIAN {
            Endianness::Little => Endianness::Big,
            Endianness::Big => Endianness::Little,
        };

        assert!(rem.is_empty());
        assert_eq!(gh.version_major, 4);
        assert_eq!(gh.version_minor, 2);
        assert_eq!(gh.endianness, expected_endianness);
        assert_eq!(gh.snap_length, 1555);
    }
}