Skip to main content

proto_packet/io/wire/
transfer.rs

1use crate::io::WireType;
2use crate::io::WireType::*;
3use enc::read_single_byte;
4use enc::var_int::VarInt128;
5use std::io::{Read, Write};
6
7impl WireType {
8    //! Transfer
9
10    /// Transfers the encoded value for this wire type from `r` to `w`.
11    ///
12    /// This reads the value bytes from `r` and writes them to `w`. The field header has already
13    /// been consumed — this transfers only the value payload.
14    pub fn transfer<R, W>(self, r: &mut R, w: &mut W) -> Result<(), enc::Error>
15    where
16        R: Read,
17        W: Write,
18    {
19        match self {
20            Fixed1Byte => Self::transfer_fixed(r, w, 1),
21            Fixed2Byte => Self::transfer_fixed(r, w, 2),
22            Fixed4Byte => Self::transfer_fixed(r, w, 4),
23            Fixed8Byte => Self::transfer_fixed(r, w, 8),
24            Fixed16Byte => Self::transfer_fixed(r, w, 16),
25            VarInt => Self::transfer_varint(r, w),
26            LengthPrefixed => Self::transfer_length_prefixed(r, w),
27            List => Self::transfer_length_prefixed(r, w),
28        }
29    }
30
31    /// Transfers `n` fixed bytes from `r` to `w`.
32    fn transfer_fixed<R, W>(r: &mut R, w: &mut W, n: usize) -> Result<(), enc::Error>
33    where
34        R: Read,
35        W: Write,
36    {
37        let mut buf: [u8; 16] = [0u8; 16];
38        r.read_exact(&mut buf[..n])?;
39        w.write_all(&buf[..n])?;
40        Ok(())
41    }
42
43    /// Transfers a varint from `r` to `w`.
44    fn transfer_varint<R, W>(r: &mut R, w: &mut W) -> Result<(), enc::Error>
45    where
46        R: Read,
47        W: Write,
48    {
49        let mut buf: [u8; VarInt128::MAX_ENCODED_LEN] = [0u8; VarInt128::MAX_ENCODED_LEN];
50        let mut len: usize = 0;
51        while len < VarInt128::MAX_ENCODED_LEN {
52            let b: u8 = read_single_byte(r)?;
53            buf[len] = b;
54            len += 1;
55            if b & 0x80 == 0 {
56                w.write_all(&buf[..len])?;
57                return Ok(());
58            }
59        }
60        Err(enc::Error::InvalidEncodedData { reason: None }) // todo -- better error here
61    }
62
63    /// Transfers a length-prefixed or list value from `r` to `w`.
64    ///
65    /// The varint length prefix is read into a small stack buffer and forwarded directly (skipping
66    /// the decode+re-encode round trip), while still being validated by the canonical varint
67    /// decoder. The payload bytes are then forwarded blindly — they are not validated since they
68    /// may contain any nested packet structure.
69    fn transfer_length_prefixed<R, W>(r: &mut R, w: &mut W) -> Result<(), enc::Error>
70    where
71        R: Read,
72        W: Write,
73    {
74        use enc::DecodeFromReadPrefix;
75        use enc::var_int::VarIntSize;
76
77        // Read varint bytes into a stack buffer until the high-bit-clear terminator.
78        let mut prefix_buf: [u8; VarIntSize::MAX_ENCODED_LEN] = [0u8; VarIntSize::MAX_ENCODED_LEN];
79        let mut prefix_len: usize = 0;
80        loop {
81            if prefix_len >= VarIntSize::MAX_ENCODED_LEN {
82                return Err(enc::Error::InvalidEncodedData { reason: None });
83            }
84            let b: u8 = read_single_byte(r)?;
85            prefix_buf[prefix_len] = b;
86            prefix_len += 1;
87            if b & 0x80 == 0 {
88                break;
89            }
90        }
91
92        // Decode the buffered prefix to validate it and extract the payload length.
93        let mut prefix_rest: &[u8] = &prefix_buf[1..prefix_len];
94        let len: usize =
95            VarIntSize::decode_from_read_prefix_with_first_byte(&mut prefix_rest, prefix_buf[0])?
96                .value();
97
98        // Forward the prefix bytes verbatim.
99        w.write_all(&prefix_buf[..prefix_len])?;
100
101        // Transfer the payload.
102        let mut remaining: usize = len;
103        let mut buf: [u8; 1024] = [0u8; 1024]; // todo -- why 1024?
104        while remaining > 0 {
105            let chunk: usize = remaining.min(buf.len());
106            r.read_exact(&mut buf[..chunk])?;
107            w.write_all(&buf[..chunk])?;
108            remaining -= chunk;
109        }
110        Ok(())
111    }
112}
113
114#[cfg(test)]
115mod tests {
116    use crate::io::WireType;
117    use crate::io::WireType::*;
118    use std::io::Cursor;
119
120    #[test]
121    #[allow(clippy::type_complexity)]
122    fn transfer() {
123        // 1500 byte payload to exercise the multi-chunk path in `transfer_length_prefixed`
124        // (chunk size = 1024). varint(1500) = [0xDC, 0x0B].
125        let mut large_lp: Vec<u8> = vec![0xDC, 0x0B];
126        large_lp.extend(std::iter::repeat_n(0xAB, 1500));
127
128        // 20 bytes all with the continuation bit set — exceeds VarInt128::MAX_ENCODED_LEN (19).
129        let overlong_varint: [u8; 20] = [0xFF; 20];
130
131        // 16 bytes all with the continuation bit set — exceeds the usize varint max for any
132        // supported pointer width (max is 10 on 64-bit, 5 on 32-bit).
133        let overlong_lp_prefix: [u8; 16] = [0xFF; 16];
134
135        // Each case: (wire type, input bytes, Some(expected output) for Ok, None for Err).
136        let cases: &[(WireType, &[u8], Option<&[u8]>)] = &[
137            // Fixed widths — output equals input.
138            (Fixed1Byte, &[0x42], Some(&[0x42])),
139            (Fixed2Byte, &[1, 2], Some(&[1, 2])),
140            (Fixed4Byte, &[1, 2, 3, 4], Some(&[1, 2, 3, 4])),
141            (
142                Fixed8Byte,
143                &[1, 2, 3, 4, 5, 6, 7, 8],
144                Some(&[1, 2, 3, 4, 5, 6, 7, 8]),
145            ),
146            (
147                Fixed16Byte,
148                &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
149                Some(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]),
150            ),
151            // VarInt: 1, 2, 3, and 5 byte forms.
152            (VarInt, &[0x42], Some(&[0x42])),
153            (VarInt, &[0xAC, 0x02], Some(&[0xAC, 0x02])),
154            (VarInt, &[0xFF, 0xFF, 0x03], Some(&[0xFF, 0xFF, 0x03])),
155            (
156                VarInt,
157                &[0xFF, 0xFF, 0xFF, 0xFF, 0x0F],
158                Some(&[0xFF, 0xFF, 0xFF, 0xFF, 0x0F]),
159            ),
160            // VarInt: overlong — should reject.
161            (VarInt, &overlong_varint, None),
162            // LengthPrefixed: empty body, small body, large body that crosses the chunk boundary.
163            (LengthPrefixed, &[0x00], Some(&[0x00])),
164            (
165                LengthPrefixed,
166                &[0x03, b'a', b'b', b'c'],
167                Some(&[0x03, b'a', b'b', b'c']),
168            ),
169            (
170                LengthPrefixed,
171                large_lp.as_slice(),
172                Some(large_lp.as_slice()),
173            ),
174            // LengthPrefixed: overlong prefix — should reject.
175            (LengthPrefixed, &overlong_lp_prefix, None),
176            // List uses the same path as LengthPrefixed.
177            (List, &[0x00], Some(&[0x00])),
178            (List, &[0x03, 1, 2, 3], Some(&[0x03, 1, 2, 3])),
179        ];
180
181        for (wire, input, expected) in cases {
182            let mut r: Cursor<&[u8]> = Cursor::new(*input);
183            let mut w: Vec<u8> = Vec::new();
184            let result: Result<(), enc::Error> = wire.transfer(&mut r, &mut w);
185            match (result, expected) {
186                (Ok(()), Some(exp)) => {
187                    assert_eq!(
188                        w.as_slice(),
189                        *exp,
190                        "wire={wire:?} input_len={}",
191                        input.len()
192                    );
193                }
194                (Err(_), None) => {}
195                (Ok(()), None) => {
196                    panic!(
197                        "wire={wire:?} input_len={}: expected error, got Ok",
198                        input.len()
199                    )
200                }
201                (Err(e), Some(_)) => {
202                    panic!(
203                        "wire={wire:?} input_len={}: expected Ok, got error: {e:?}",
204                        input.len()
205                    )
206                }
207            }
208        }
209    }
210}