mysqlbinlog_network/mysql_binlog/
packet_helpers.rs

1use std::io::{self, Cursor, Read};
2
3use crate::mysql_binlog::errors::DecimalParseError;
4
5use bigdecimal::BigDecimal;
6use byteorder::{BigEndian, ByteOrder, LittleEndian, ReadBytesExt};
7
8// This module contains miscellaneous shitty functions for reading various
9// MySQL data types out of a packet (or, well, a Read).
10//
11// It's all garbage all the way down.
12
13pub(crate) fn read_variable_length_integer<R: Read>(r: &mut R) -> io::Result<i64> {
14    let first = r.read_u8()?;
15    if first < 0xfb {
16        Ok(i64::from(first as i8))
17    } else if first == 0xfc {
18        Ok(i64::from(r.read_i16::<LittleEndian>()?))
19    } else if first == 0xfd {
20        // why are there three byte integers fucking mysql
21        let mut buf = [0u8; 4];
22        r.read_exact(&mut buf[0..3])?;
23        // TODO: sign-extend to fill that top byte
24        Ok(i64::from(LittleEndian::read_i32(&buf)))
25    } else if first == 0xfe {
26        r.read_i64::<LittleEndian>()
27    } else {
28        unreachable!();
29    }
30}
31
32pub(crate) fn read_known_length_integer_be<R: Read>(r: &mut R, bytes: usize) -> io::Result<i64> {
33    Ok(match bytes {
34        1 => i64::from(r.read_i8()?),
35        2 => i64::from(r.read_i16::<BigEndian>()?),
36        3 => {
37            let mut buf = [0u8; 3];
38            r.read_exact(&mut buf)?;
39            let is_neg = buf[0] & 0x80 != 0;
40            buf[0] &= 0x7f;
41            let num: i64 = (i64::from(buf[0]) << 16) | (i64::from(buf[1]) << 8) | i64::from(buf[2]);
42            if is_neg {
43                -1 * num
44            } else {
45                num
46            }
47        }
48        4 => i64::from(r.read_i32::<BigEndian>()?),
49        _ => unimplemented!(),
50    })
51}
52
53pub(crate) fn read_uint24<R: Read>(r: &mut R) -> io::Result<u32> {
54    let mut buf = [0u8; 4];
55    r.read_exact(&mut buf[0..3])?;
56    Ok(LittleEndian::read_u32(&buf))
57}
58
59pub(crate) fn read_int24<R: Read>(r: &mut R) -> io::Result<i32> {
60    let mut buf = [0u8; 4];
61    r.read_exact(&mut buf[0..3])?;
62    Ok(LittleEndian::read_i32(&buf))
63}
64
65pub(crate) fn read_one_byte_length_prefixed_bytes<R: Read>(r: &mut R) -> io::Result<Vec<u8>> {
66    let length = r.read_u8()?;
67    read_nbytes(r, length)
68}
69
70pub(crate) fn read_two_byte_length_prefixed_bytes<R: Read>(r: &mut R) -> io::Result<Vec<u8>> {
71    let length = r.read_u16::<LittleEndian>()? as usize;
72    read_nbytes(r, length)
73}
74
75pub(crate) fn read_var_byte_length_prefixed_bytes<R: Read>(
76    r: &mut R,
77    pl: u8,
78) -> io::Result<Vec<u8>> {
79    let len = match pl {
80        1 => r.read_u8()? as usize,
81        2 => r.read_u16::<LittleEndian>()? as usize,
82        3 => {
83            let mut buf = [0u8; 4];
84            r.read_exact(&mut buf[0..3])?;
85            byteorder::LittleEndian::read_u32(&buf) as usize
86        }
87        4 => r.read_u32::<LittleEndian>()? as usize,
88        8 => r.read_u64::<LittleEndian>()? as usize,
89        l => unreachable!(format!("got unexpected length {0:?}", l)),
90    };
91    read_nbytes(r, len)
92}
93
94pub(crate) fn read_one_byte_length_prefixed_string<R: Read>(r: &mut R) -> io::Result<String> {
95    let buf = read_one_byte_length_prefixed_bytes(r)?;
96    Ok(String::from_utf8_lossy(&buf).into_owned())
97}
98
99pub(crate) fn read_two_byte_length_prefixed_string<R: Read>(r: &mut R) -> io::Result<String> {
100    let buf = read_two_byte_length_prefixed_bytes(r)?;
101    Ok(String::from_utf8_lossy(&buf).into_owned())
102}
103
104pub(crate) fn read_nbytes<R: Read, S: Into<usize>>(
105    r: &mut R,
106    desired_bytes: S,
107) -> io::Result<Vec<u8>> {
108    let mut into = vec![0u8; desired_bytes.into()];
109    r.read_exact(&mut into)?;
110    Ok(into)
111}
112
113pub(crate) fn read_variable_length_bytes<R: Read>(r: &mut R) -> io::Result<Vec<u8>> {
114    let mut byte = 0x80;
115    let mut length = 0usize;
116    let mut shbits = 0u32;
117    while byte & 0x80 != 0 {
118        byte = r.read_u8()?;
119        length |= ((byte & 0x7f) as usize) << shbits;
120        shbits += 7;
121        if shbits >= 57 {
122            panic!("illegal shift, shbits={}", shbits);
123        }
124    }
125    read_nbytes(r, length)
126}
127
128pub(crate) fn read_variable_length_string<R: Read>(r: &mut R) -> io::Result<String> {
129    let buf = read_variable_length_bytes(r)?;
130    Ok(String::from_utf8_lossy(&buf).into_owned())
131}
132
133const DECIMAL_DIGITS_PER_INTEGER: u8 = 9;
134
135pub(crate) fn read_new_decimal<R: Read>(
136    r: &mut R,
137    precision: u8,
138    decimal: u8,
139) -> Result<BigDecimal, DecimalParseError> {
140    // like every other binlog parser's implementation, this code
141    // is a transliteration of https://github.com/jeremycole/mysql_binlog/blob/master/lib/mysql_binlog/binlog_field_parser.rb#L233
142    // because this format is bananas
143    let compressed_byte_map = [0usize, 1, 1, 2, 2, 3, 3, 4, 4, 4];
144    let integral = precision - decimal;
145    let uncompressed_integers: usize = (integral / DECIMAL_DIGITS_PER_INTEGER).into();
146    let uncompressed_decimals: usize = (decimal / DECIMAL_DIGITS_PER_INTEGER).into();
147    let compressed_integers: usize =
148        integral as usize - (uncompressed_integers * DECIMAL_DIGITS_PER_INTEGER as usize);
149    let compressed_decimals: usize =
150        decimal as usize - (uncompressed_decimals * DECIMAL_DIGITS_PER_INTEGER as usize);
151
152    let bytes_to_read: usize = uncompressed_integers * 4
153        + compressed_byte_map[compressed_integers]
154        + uncompressed_decimals * 4
155        + compressed_byte_map[compressed_decimals];
156
157    let mut buf = read_nbytes(r, bytes_to_read)?;
158
159    let mut components = Vec::new();
160
161    let is_negative = (buf[0] & 0x80) == 0;
162    buf[0] ^= 0x80;
163    if is_negative {
164        components.push("-".to_owned());
165    }
166    let mut r = Cursor::new(buf);
167    // if there's a compressed integral part, read it
168    if compressed_integers != 0 {
169        let to_read = compressed_byte_map[compressed_integers];
170        components.push(read_known_length_integer_be(&mut r, to_read)?.to_string())
171    }
172    for _ in 0..uncompressed_integers {
173        components.push(format!("{:09}", r.read_u32::<BigEndian>()?));
174    }
175    components.push(".".to_owned());
176    for _ in 0..uncompressed_decimals {
177        components.push(format!("{:09}", r.read_u32::<LittleEndian>()?));
178    }
179    if compressed_decimals != 0 {
180        components.push(
181            read_known_length_integer_be(&mut r, compressed_byte_map[compressed_decimals])?
182                .to_string(),
183        )
184    }
185    let decimal = components.join("").parse::<BigDecimal>()?;
186    Ok(decimal)
187}
188
189pub(crate) fn read_datetime_subsecond_part<R: Read>(r: &mut R, pack_length: u8) -> io::Result<u32> {
190    Ok(match pack_length {
191        0 => 0u32,
192        1 | 2 => read_known_length_integer_be(r, 1)? as u32,
193        3 | 4 => read_known_length_integer_be(r, 2)? as u32,
194        5 | 6 => read_known_length_integer_be(r, 3)? as u32,
195        _ => 0u32,
196    })
197}
198pub(crate) fn decode_bit<R: Read>(r: &mut R, nbits: u16, length: u8) -> io::Result<i64> {
199    if nbits > 1 {
200        match length {
201            1..=8 => {
202                let mut data = vec![0u8; length as usize];
203                r.read_exact(&mut data)?;
204                return Ok(bf_fixed_length_int(&data) as i64);
205            }
206            _ => return Err(io::Error::from(io::ErrorKind::Other)),
207        }
208    } else {
209        if length != 1 {
210            return Err(io::Error::from(io::ErrorKind::Other));
211        }
212    }
213    Ok(r.read_u8()? as i64)
214}
215pub(crate) fn little_decode_bit<R: Read>(r: &mut R, nbits: u16, length: u16) -> io::Result<i64> {
216    if nbits > 1 {
217        match length {
218            1..=8 => {
219                let mut data = vec![0u8; length as usize];
220                r.read_exact(&mut data)?;
221                return Ok(fixed_length_int(&data) as i64);
222            }
223            _ => return Err(io::Error::from(io::ErrorKind::Other)),
224        }
225    } else {
226        if length != 1 {
227            return Err(io::Error::from(io::ErrorKind::Other));
228        }
229    }
230    Ok(r.read_u8()? as i64)
231}
232
233fn fixed_length_int(buf: &[u8]) -> u64 {
234    let mut num = 0u64;
235    for i in 0..buf.len() {
236        num |= (buf[i] as u64) << (i as u32) * 8;
237    }
238    num
239}
240
241fn bf_fixed_length_int(buf: &[u8]) -> u64 {
242    let mut num = 0u64;
243    for i in 0..buf.len() {
244        num |= (buf[i] as u64) << ((buf.len() - i - 1) as u32) * 8;
245    }
246    num
247}
248
249#[cfg(test)]
250mod tests {
251    use std::io::Cursor;
252
253    use bigdecimal::BigDecimal;
254
255    use super::read_new_decimal;
256    use super::read_var_byte_length_prefixed_bytes;
257
258    #[test]
259    fn test_read_new_decimal() {
260        let mut uut = Cursor::new(vec![0x80, 0x00, 0x00, 0x00, 0x01]);
261        let one = "1.00".parse::<BigDecimal>().unwrap();
262        assert_eq!(
263            read_new_decimal(&mut uut, 10, 0).expect("should parse"),
264            one
265        );
266        let mut uut = Cursor::new(vec![0x80, 0x00, 0x01, 0x00, 0x00]);
267        let zero_point_one = "0.100".parse::<BigDecimal>().unwrap();
268        assert_eq!(
269            read_new_decimal(&mut uut, 5, 5).expect("should parse"),
270            zero_point_one
271        );
272        let mut uut = Cursor::new(vec![128, 0, 5, 0, 212, 49]);
273        let expected = "5.54321".parse::<BigDecimal>().unwrap();
274        assert_eq!(
275            read_new_decimal(&mut uut, 10, 5).expect("should parse"),
276            expected
277        );
278    }
279
280    #[test]
281    fn test_read_var_byte_length_prefixed_bytes() {
282        for (byte_length, input, expected_output) in &[
283            (1, vec![0x01, 0x09], vec![0x09]),
284            (2, vec![0x01, 0x00, 0x0a], vec![0x0a]),
285            (3, vec![0x01, 0x00, 0x00, 0x0b], vec![0x0b]),
286            (
287                4,
288                vec![0x02, 0x00, 0x00, 0x00, 0x0c, 0x0d],
289                vec![0x0c, 0x0d],
290            ),
291            (
292                8,
293                vec![
294                    0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0xe, 0xa,
295                ],
296                vec![0xd, 0xe, 0xa],
297            ),
298        ] {
299            let mut uut = Cursor::new(input);
300            assert_eq!(
301                &read_var_byte_length_prefixed_bytes(&mut uut, *byte_length).expect("should be ok"),
302                expected_output
303            );
304        }
305    }
306}