Skip to main content

mtorrent_core/pe/
utils.rs

1use super::{Decryptor, PrefixedStream};
2use crate::pwp::PROTOCOL_STRING;
3use bytes::BufMut;
4use std::io;
5use std::mem::MaybeUninit;
6use tokio::io::{AsyncRead, AsyncReadExt, ReadBuf};
7
8/// Return value of [`detect_encryption()`].
9pub enum MaybeEncrypted<T> {
10    Plain(T),
11    Encrypted(T),
12}
13
14/// Determine if the stream is likely encrypted or not by checking if the first bytes match the
15/// BitTorrent protocol string. The stream is returned with the first bytes "put back" so that the
16/// caller can read them regardless of encryption status.
17pub async fn detect_encryption<S: AsyncRead + Unpin>(
18    mut stream: S,
19) -> io::Result<MaybeEncrypted<PrefixedStream<io::Cursor<[u8; PROTOCOL_STRING.len()]>, S>>> {
20    let mut buf = [0u8; PROTOCOL_STRING.len()];
21    stream.read_exact(&mut buf).await?;
22    let is_unecrypted = buf == PROTOCOL_STRING;
23
24    let stream = PrefixedStream::new(io::Cursor::new(buf), stream);
25    if is_unecrypted {
26        Ok(MaybeEncrypted::Plain(stream))
27    } else {
28        Ok(MaybeEncrypted::Encrypted(stream))
29    }
30}
31
32/// ```ignore
33/// sha1_of![b""];
34/// ```
35macro_rules! sha1_of {
36    ($($slices:expr),+) => {{
37        let mut hasher = sha1_smol::Sha1::new();
38        $(hasher.update($slices);)+
39        hasher.digest().bytes()
40    }};
41}
42pub(super) use sha1_of;
43
44pub(super) fn xor_arrays<const N: usize>(arr1: [u8; N], arr2: [u8; N]) -> [u8; N] {
45    let mut result = [0u8; N];
46    for i in 0..N {
47        result[i] = arr1[i] ^ arr2[i];
48    }
49    result
50}
51
52/// Read and discard encrypted data from `stream`.
53pub(super) async fn consume_encrypted<const MAX_LEN: usize>(
54    mut stream: impl AsyncReadExt + Unpin,
55    len: usize,
56    decryptor: &mut Decryptor,
57    what: &'static str,
58) -> io::Result<()> {
59    let mut buf = [MaybeUninit::<u8>::uninit(); MAX_LEN];
60    let mut rd = ReadBuf::uninit(&mut buf);
61    let mut rd = rd.take(len);
62
63    while 0 != stream.read_buf(&mut rd).await? {}
64
65    if rd.filled().len() != len {
66        return Err(io::Error::new(
67            io::ErrorKind::UnexpectedEof,
68            format!("stream exhausted before {what} fully read"),
69        ));
70    }
71
72    decryptor.decrypt(rd.filled_mut());
73    Ok(())
74}
75
76/// Read data from `source` until `pattern` is found, consuming and discarding the pattern and all
77/// data before it.
78pub(super) async fn consume_through<const N: usize>(
79    mut source: impl AsyncReadExt + Unpin,
80    pattern: &[u8; N],
81) -> io::Result<()> {
82    let mut storage = [MaybeUninit::<u8>::uninit(); N];
83    let mut buf = ReadBuf::uninit(&mut storage);
84
85    let mut overlap_ind = None;
86
87    loop {
88        let max_to_read = overlap_ind.unwrap_or(N);
89        let bytes_read = source.read_buf(&mut buf.take(max_to_read)).await?;
90        if 0 == bytes_read {
91            return Err(io::Error::new(
92                io::ErrorKind::UnexpectedEof,
93                "stream exhausted before pattern found",
94            ));
95        }
96        unsafe { buf.advance_mut(bytes_read) }
97
98        if let Some(last_n) = buf.filled().last_chunk::<N>() {
99            overlap_ind = overlap_start_index(pattern, last_n);
100
101            match overlap_ind {
102                Some(0) => break,
103                None => buf.clear(),
104                Some(n) => {
105                    buf.filled_mut().copy_within(n.., 0);
106                    buf.set_filled(N - n);
107                }
108            }
109        }
110    }
111
112    Ok(())
113}
114
115/// Returns index into `data` such that data[ret..] == pattern[..-ret]
116fn overlap_start_index<const N: usize>(pattern: &[u8; N], data: &[u8; N]) -> Option<usize> {
117    let mut data_ind = 0;
118    let mut pattern_ind = 0;
119    let mut ret = None;
120
121    while data_ind < N {
122        if data[data_ind] == pattern[pattern_ind] {
123            ret.get_or_insert(data_ind);
124            data_ind += 1;
125            pattern_ind += 1;
126        } else {
127            if let Some(old_ret) = ret.take() {
128                data_ind = old_ret;
129                pattern_ind = 0;
130            }
131            data_ind += 1;
132        }
133    }
134
135    ret
136}
137
138#[cfg(test)]
139mod tests {
140    use super::*;
141    use std::iter;
142
143    #[test]
144    fn test_overlap_start_index() {
145        // No overlap
146        assert_eq!(overlap_start_index(b"wxyz", b"abcd"), None);
147
148        // Full overlap: data == pattern → entire data is a prefix of pattern
149        assert_eq!(overlap_start_index(b"abcd", b"abcd"), Some(0));
150
151        // Partial overlap at middle: data[2..] = "cd" == pattern[..2]
152        assert_eq!(overlap_start_index(b"cdef", b"abcd"), Some(2));
153
154        // Single-byte overlap at end: data[3..] = "a" == pattern[..1]
155        assert_eq!(overlap_start_index(b"axyz", b"bcda"), Some(3));
156
157        // Prefers earliest (longest) overlap: both i=0 and i=2 are valid
158        assert_eq!(overlap_start_index(&[1, 2, 1, 2], &[1, 2, 1, 2]), Some(0));
159
160        // Repeated bytes: data[1..] = [1,1,1] == pattern[..3]
161        assert_eq!(overlap_start_index(&[1, 1, 1, 2], &[1, 1, 1, 1]), Some(1));
162
163        // Backtrack then find shorter: data[1..] = [1,2] == pattern[..2]
164        assert_eq!(overlap_start_index(&[1, 2, 3], &[1, 1, 2]), Some(1));
165
166        // Self-overlapping pattern: data[2..] = [1,2,1] == pattern[..3]
167        assert_eq!(overlap_start_index(&[1, 2, 1, 2, 3], &[1, 2, 1, 2, 1]), Some(2));
168    }
169
170    #[tokio::test]
171    async fn test_consume_through_pattern_at_start() {
172        let pattern: [u8; 4] = [7, 8, 9, 10];
173        let tail = [100u8, 101, 102, 103];
174
175        let mut input = Vec::new();
176        input.extend_from_slice(&pattern);
177        input.extend_from_slice(&tail);
178
179        let mut source = io::Cursor::new(input);
180        consume_through(&mut source, &pattern).await.unwrap();
181
182        let mut buf = Vec::new();
183        source.read_to_end(&mut buf).await.unwrap();
184        assert_eq!(buf.as_slice(), &tail);
185    }
186
187    #[tokio::test]
188    async fn test_consume_through_empty_tail() {
189        // Pattern is at the very end of the stream; nothing should remain after consuming.
190        let head = [0u8, 1, 2, 3, 4, 5];
191        let pattern: [u8; 4] = [7, 8, 9, 10];
192
193        let mut input = Vec::new();
194        input.extend_from_slice(&head);
195        input.extend_from_slice(&pattern);
196
197        let mut source = io::Cursor::new(input);
198        consume_through(&mut source, &pattern).await.unwrap();
199
200        let mut buf = Vec::new();
201        source.read_to_end(&mut buf).await.unwrap();
202        assert!(buf.is_empty());
203    }
204
205    #[tokio::test]
206    async fn test_consume_through_not_found() {
207        // Stream ends without containing the pattern → UnexpectedEof.
208        let pattern: [u8; 4] = [7, 8, 9, 10];
209        let data = [0u8, 1, 2, 3, 4, 5];
210
211        let mut source = io::Cursor::new(data);
212        let err = consume_through(&mut source, &pattern).await.unwrap_err();
213        assert_eq!(err.kind(), io::ErrorKind::UnexpectedEof);
214    }
215
216    #[tokio::test]
217    async fn test_consume_through_self_overlapping_pattern() {
218        // Pattern [1,2,1,2,3] has an internal overlap: the prefix [1,2] also appears
219        // at position 2 within the pattern.  The stream starts with a false match
220        // [1,2,1,2,1] before the real occurrence [1,2,1,2,3], exercising backtracking.
221        let pattern: [u8; 5] = [1, 2, 1, 2, 3];
222        let tail = [99u8, 100];
223
224        let mut input: Vec<u8> = vec![1, 2, 1, 2, 1, 2, 3]; // false start at 0, real match at 2
225        input.extend_from_slice(&tail);
226
227        let mut source = io::Cursor::new(input);
228        consume_through(&mut source, &pattern).await.unwrap();
229
230        let mut buf = Vec::new();
231        source.read_to_end(&mut buf).await.unwrap();
232        assert_eq!(buf.as_slice(), &tail);
233    }
234
235    #[tokio::test]
236    async fn test_consume_through_multiple_occurrences_stops_at_first() {
237        // The pattern appears twice; consume_through must stop at the first occurrence.
238        let pattern: [u8; 3] = [1, 2, 3];
239        let between = [50u8, 51, 52];
240
241        let mut input = Vec::new();
242        input.extend_from_slice(&[9u8, 8, 7]); // head before first occurrence
243        input.extend_from_slice(&pattern);
244        input.extend_from_slice(&between);
245        input.extend_from_slice(&pattern); // second occurrence
246
247        let mut source = io::Cursor::new(input);
248        consume_through(&mut source, &pattern).await.unwrap();
249
250        let mut buf = Vec::new();
251        source.read_to_end(&mut buf).await.unwrap();
252
253        let mut expected = Vec::new();
254        expected.extend_from_slice(&between);
255        expected.extend_from_slice(&pattern);
256        assert_eq!(buf, expected);
257    }
258
259    #[tokio::test]
260    async fn test_consume_through_buffer_exhaustion() {
261        let data: &[u8] = &[
262            144, 37, 224, 143, 67, 254, 129, 194, 32, 127, 151, 215, 163, 80, 106, 252, 181, 23,
263            132, 37, 53, 13, 156, 161, 189, 157, 209, 38, 142, 221, 192, 27, 229, 224, 50, 204, 91,
264            99, 94, 173, 25, 201, 161, 160, 251, 41, 58, 128, 156, 233, 160, 195, 234, 179, 140,
265            160, 14, 194, 161, 87, 203, 148, 114, 2, 24, 122, 18, 117, 196, 86, 153, 147, 35, 241,
266            182, 173, 212, 107, 80, 14, 49, 125, 91, 100, 10, 232, 36, 166, 250, 241, 82, 118, 6,
267            53, 188, 24, 41, 176, 109, 20, 99, 120, 191, 218, 114, 91, 161, 178, 27, 137, 184, 251,
268            52, 222, 116, 232, 153, 101, 173, 121, 229, 39, 247, 65, 1, 46, 216, 14, 1, 2, 3, 4, 5,
269            162, 244, 37, 212, 65, 33, 45, 215, 68, 110, 244, 216, 155, 107, 160, 199, 149, 175,
270            168, 75, 51, 195, 151, 235, 166, 68, 181, 163, 12, 153, 243, 211, 245, 148, 122, 106,
271            250, 195, 215, 122, 218, 43, 0, 204, 241, 186, 223, 201, 101, 188, 170, 244, 226, 195,
272            86, 254, 81, 157, 192, 141, 100, 12, 62, 179,
273        ];
274        let pattern: [u8; 5] = [1, 2, 3, 4, 5];
275
276        let mut source = io::Cursor::new(data);
277        consume_through(&mut source, &pattern).await.unwrap();
278
279        let expected_tail = [
280            162, 244, 37, 212, 65, 33, 45, 215, 68, 110, 244, 216, 155, 107, 160, 199, 149, 175,
281            168, 75, 51, 195, 151, 235, 166, 68, 181, 163, 12, 153, 243, 211, 245, 148, 122, 106,
282            250, 195, 215, 122, 218, 43, 0, 204, 241, 186, 223, 201, 101, 188, 170, 244, 226, 195,
283            86, 254, 81, 157, 192, 141, 100, 12, 62, 179,
284        ];
285        let mut buf = Vec::new();
286        source.read_to_end(&mut buf).await.unwrap();
287        assert_eq!(buf.as_slice(), &expected_tail);
288    }
289
290    #[tokio::test]
291    async fn test_consume_through_fuzz() {
292        for _ in 0..10_000 {
293            let pattern: [u8; 5] = [1, 2, 3, 4, 5];
294            let tail = b"tail data";
295
296            let input = {
297                let head_len = (rand::random::<u16>() % 512) as usize;
298                let mut tmp: Vec<u8> = iter::repeat_with(rand::random).take(head_len).collect();
299                tmp.extend_from_slice(&pattern);
300                tmp.extend_from_slice(tail);
301                tmp
302            };
303
304            let mut source = io::Cursor::new(input);
305            if let Err(e) = consume_through(&mut source, &pattern).await {
306                panic!("consume_through failed: {:?}\n{:?}", e, source.get_ref());
307            }
308
309            let mut buf = Vec::new();
310            source.read_to_end(&mut buf).await.unwrap();
311            assert_eq!(buf, tail);
312        }
313    }
314
315    #[tokio::test]
316    async fn test_detect_encryption() {
317        let unencrypted_stream = io::Cursor::new(PROTOCOL_STRING);
318        match detect_encryption(unencrypted_stream).await.unwrap() {
319            MaybeEncrypted::Plain(mut stream) => {
320                let mut buf = Vec::new();
321                stream.read_to_end(&mut buf).await.unwrap();
322                assert_eq!(buf, PROTOCOL_STRING);
323            }
324            MaybeEncrypted::Encrypted(_) => panic!("unencrypted stream misclassified as encrypted"),
325        }
326
327        let encrypted_stream = io::Cursor::new(b"not the protocol string");
328        match detect_encryption(encrypted_stream).await.unwrap() {
329            MaybeEncrypted::Plain(_) => panic!("encrypted stream misclassified as unencrypted"),
330            MaybeEncrypted::Encrypted(mut stream) => {
331                let mut buf = Vec::new();
332                stream.read_to_end(&mut buf).await.unwrap();
333                assert_eq!(buf, b"not the protocol string");
334            }
335        }
336    }
337}