chiark / gitweb /
change mimeswap error handling
[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 #[derive(Error,Debug,Copy,Clone)]
10 pub enum PacketError {
11   #[error("MTU exceeded ({len} > {mtu})")] MTU { len: usize, mtu: u32 },
12   #[error("Invalid SLIP escape sequence")] SLIP,
13 }
14
15 #[throws(PacketError)]
16 pub fn check_checkmtu_mimeswap<const TO_MIME: bool>
17   (mtu: u32, data: &mut [u8])
18 {
19 //  eprintln!("before: {:?}", DumpHex(data));
20
21   for mut packet in data.split_mut(|&c| c == SLIP_END) {
22     if packet.len() > mtu.sat() {
23       throw!(PacketError::MTU { len: packet.len(), mtu })
24     }
25
26     while let Some((i, was_mime)) = packet.iter().enumerate().find_map(
27       |(i,&c)| match c {
28         SLIP_MIME_ESC => Some((i,true)),
29         SLIP_ESC      => Some((i,false)),
30         _ => None,
31       }
32     ) {
33       packet[i] = if was_mime { SLIP_ESC } else { SLIP_MIME_ESC };
34       if was_mime != TO_MIME {
35         match packet.get(i+1) {
36           Some(&SLIP_ESC_END) |
37           Some(&SLIP_ESC_ESC) => Ok(()),
38           _ => throw!(PacketError::SLIP),
39         }?;
40         packet = &mut packet[i+2 ..];
41       } else {
42         packet = &mut packet[i+1 ..];
43       }
44     }
45   }
46
47 //  eprintln!(" after: {:?}", DumpHex(data));
48 }
49
50 pub type Frame = Vec<u8>;
51 pub type FramesData = Vec<Vec<u8>>;
52
53 #[derive(Default)]
54 pub struct Frames {
55   frames: FramesData,
56   total_len: usize,
57 }
58
59 impl Debug for Frames {
60   #[throws(fmt::Error)]
61   fn fmt(&self, f: &mut fmt::Formatter) {
62     write!(f, "Frames{{n={},len={}}}", &self.frames.len(), &self.total_len)?;
63   }
64 }
65
66 impl Frames {
67   #[throws(Frame)]
68   pub fn add(&mut self, max: u32, frame: Frame) {
69     if frame.len() == 0 { return }
70     let new_total = self.total_len + frame.len() + 1;
71     if new_total > max.sat() { throw!(frame) }
72     self.total_len = new_total;
73     self.frames.push(frame);
74   }
75 }
76
77 impl From<Frames> for FramesData {
78   fn from(frames: Frames) -> FramesData { frames.frames }
79 }
80
81 pub fn ip_packet_addrs(packet: &[u8]) -> Option<(IpAddr, IpAddr)> {
82   Some(match packet.get(0)? & 0xf0 {
83     4 if packet.len() >= 20 => (
84       Ipv4Addr::from(*<&[u8;4]>::try_from(&packet[12..16]).unwrap()).into(),
85       Ipv4Addr::from(*<&[u8;4]>::try_from(&packet[16..20]).unwrap()).into(),
86     ),
87
88     6 if packet.len() >= 40 => (
89       Ipv6Addr::from(*<&[u8;16]>::try_from(&packet[ 8..24]).unwrap()).into(),
90       Ipv6Addr::from(*<&[u8;16]>::try_from(&packet[24..40]).unwrap()).into(),
91     ),
92
93     _ => None?,
94   })
95 }
96
97 #[derive(Copy,Clone,Eq,PartialEq,Ord,PartialOrd,Hash)]
98 pub struct DumpHex<'b>(pub &'b [u8]);
99 impl Debug for DumpHex<'_> {
100   #[throws(fmt::Error)]
101   fn fmt(&self, f: &mut fmt::Formatter) {
102     for v in self.0 { write!(f, "{:02x}", v)?; }
103   }
104 }
105
106 #[test]
107 fn mime_slip_to_mime() {
108   fn chk(i: &[u8], exp: Result<&[u8], &str>) {
109     let mut p = i.to_owned();
110     match (exp, check_checkmtu_mimeswap::<true>(10, p.as_mut())) {
111       (Ok(exp), Ok(())) => assert_eq!( DumpHex(exp), DumpHex(&p) ),
112       (Err(exp), Err(got)) => assert!( got.to_string().contains(exp) ),
113       x => panic!("? {:?}", x),
114     }
115   }
116
117   chk( &[ SLIP_END, SLIP_ESC, SLIP_ESC_END, b'-',     b'X' ],
118     Ok(&[ SLIP_END, b'-',     SLIP_ESC_END, SLIP_ESC, b'X' ]) );
119
120   chk( &[ SLIP_END, SLIP_ESC, b'y' ], Err("SLIP escape") );
121
122   chk( &[ SLIP_END, b'-',     b'y' ],
123     Ok(&[ SLIP_END, SLIP_ESC, b'y' ]) );
124
125   chk( &[b'x'; 20], Err("MTU"));
126 }