chiark / gitweb /
fix slip mimeification
[hippotat.git] / src / slip.rs
1 // Copyright 2021 Ian Jackson and contributors to Hippotat
2 // SPDX-License-Identifier: AGPL-3.0-or-later
3 // There is NO WARRANTY.
4
5 use crate::prelude::*;
6
7 pub static SLIP_END_SLICE: &[u8] = &[SLIP_END];
8
9 #[throws(AE)]
10 pub fn check_checkmtu_mimeswap<const TO_MIME: bool>
11   (mtu: u32, data: &mut [u8])
12 {
13 //  eprintln!("before: {:?}", DumpHex(data));
14
15   for mut packet in data.split_mut(|&c| c == SLIP_END) {
16     if packet.len() > mtu.sat() {
17       throw!(anyhow!("MTU exceeded ({} > {})", packet.len(), mtu));
18     }
19
20     while let Some((i, was_mime)) = packet.iter().enumerate().find_map(
21       |(i,&c)| match c {
22         SLIP_MIME_ESC => Some((i,true)),
23         SLIP_ESC      => Some((i,false)),
24         _ => None,
25       }
26     ) {
27       packet[i] = if was_mime { SLIP_ESC } else { SLIP_MIME_ESC };
28       if was_mime != TO_MIME {
29         match packet.get(i+1) {
30           Some(&SLIP_ESC_END) |
31           Some(&SLIP_ESC_ESC) => Ok(()),
32           _ => Err(anyhow!("SLIP escape not followed by ESC_END or ESC_ESC")),
33         }?;
34         packet = &mut packet[i+2 ..];
35       } else {
36         packet = &mut packet[i+1 ..];
37       }
38     }
39   }
40
41 //  eprintln!(" after: {:?}", DumpHex(data));
42 }
43
44 pub type Frame = Vec<u8>;
45 pub type FramesData = Vec<Vec<u8>>;
46
47 #[derive(Default)]
48 pub struct Frames {
49   frames: FramesData,
50   total_len: usize,
51 }
52
53 impl Debug for Frames {
54   #[throws(fmt::Error)]
55   fn fmt(&self, f: &mut fmt::Formatter) {
56     write!(f, "Frames{{n={},len={}}}", &self.frames.len(), &self.total_len)?;
57   }
58 }
59
60 impl Frames {
61   #[throws(Frame)]
62   pub fn add(&mut self, max: u32, frame: Frame) {
63     if frame.len() == 0 { return }
64     let new_total = self.total_len + frame.len() + 1;
65     if new_total > max.sat() { throw!(frame) }
66     self.total_len = new_total;
67     self.frames.push(frame);
68   }
69 }
70
71 impl From<Frames> for FramesData {
72   fn from(frames: Frames) -> FramesData { frames.frames }
73 }
74
75 #[derive(Copy,Clone,Eq,PartialEq,Ord,PartialOrd,Hash)]
76 pub struct DumpHex<'b>(pub &'b [u8]);
77 impl Debug for DumpHex<'_> {
78   #[throws(fmt::Error)]
79   fn fmt(&self, f: &mut fmt::Formatter) {
80     for v in self.0 { write!(f, "{:02x}", v)?; }
81   }
82 }
83
84 #[test]
85 fn mime_slip_to_mime() {
86   fn chk(i: &[u8], exp: Result<&[u8], &str>) {
87     let mut p = i.to_owned();
88     match (exp, check_checkmtu_mimeswap::<true>(10, p.as_mut())) {
89       (Ok(exp), Ok(())) => assert_eq!( DumpHex(exp), DumpHex(&p) ),
90       (Err(exp), Err(got)) => assert!( got.to_string().contains(exp) ),
91       x => panic!("? {:?}", x),
92     }
93   }
94
95   chk( &[ SLIP_END, SLIP_ESC, SLIP_ESC_END, b'-',     b'X' ],
96     Ok(&[ SLIP_END, b'-',     SLIP_ESC_END, SLIP_ESC, b'X' ]) );
97
98   chk( &[ SLIP_END, SLIP_ESC, b'y' ], Err("SLIP escape") );
99
100   chk( &[ SLIP_END, b'-',     b'y' ],
101     Ok(&[ SLIP_END, SLIP_ESC, b'y' ]) );
102
103   chk( &[b'x'; 20], Err("MTU"));
104 }