chiark / gitweb /
wip rx
[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,Eq,PartialEq)]
10 pub enum PacketError {
11   #[error("empty packet")]                 Empty,
12   #[error("MTU exceeded ({len} > {mtu})")] MTU { len: usize, mtu: u32 },
13   #[error("Invalid SLIP escape sequence")] SLIP,
14   #[error("unexpected src addr {0:?}")]    Src(IpAddr),
15   #[error("unexpected dst addr {0:?}")]    Dst(IpAddr),
16   #[error("bad, IPv{vsn}, len={len}")]     Bad { len: usize, vsn: u8 },
17 }
18
19 pub fn check<AC, EH, OUT, const TO_MIME: bool>(
20   mtu: u32,
21   data: &[u8],
22   out: &mut OUT,
23   mut addr_chk: AC,
24   mut error_handler: EH
25 ) where OUT: Extend<Box<[u8]>>,
26         AC: FnMut(&[u8]) -> Result<(), PacketError>,
27         EH: FnMut(PacketError),
28 {
29 //  eprintln!("before: {:?}", DumpHex(data));
30   for packet in data.split(|&c| c == SLIP_END) {
31     match (||{
32       if packet.len() == 0 {
33         throw!(PacketError::Empty)
34       }
35       if packet.len() > mtu.sat() {
36         throw!(PacketError::MTU { len: packet.len(), mtu });
37       }
38
39       let mut packet: Box<[u8]> = packet.to_owned().into();
40       let mut walk: &mut [u8] = &mut packet;
41       let mut header = [0u8; HEADER_FOR_ADDR];
42       let mut wheader = &mut header[..];
43
44       while let Some((i, was_mime)) = walk.iter().enumerate().find_map(
45         |(i,&c)| match c {
46           SLIP_MIME_ESC => Some((i,true)),
47           SLIP_ESC      => Some((i,false)),
48           _ => None,
49         }
50       ) {
51         let _ = wheader.write(&walk[0..i]);
52         walk[i] = if was_mime { SLIP_ESC } else { SLIP_MIME_ESC };
53         if was_mime != TO_MIME {
54           let c = match walk.get(i+1) {
55             Some(&SLIP_ESC_END) => SLIP_ESC,
56             Some(&SLIP_ESC_ESC) => SLIP_END,
57             _ => throw!(PacketError::SLIP),
58           };
59           let _ = wheader.write(&[c]);
60           walk = &mut walk[i+2 ..];
61         } else {
62           let _ = wheader.write(&[SLIP_MIME_ESC]);
63           walk = &mut walk[i+1 ..];
64         }
65       }
66       let _ = wheader.write(walk);
67
68       addr_chk(&header)?;
69
70       Ok(packet)
71     })() {
72       Err(e) => error_handler(e),
73       Ok(packet) => out.extend(iter::once(packet)),
74     }
75   }
76 //  eprintln!(" after: {:?}", DumpHex(data));
77 }
78
79 pub type Frame = Vec<u8>;
80 pub type FramesData = Vec<Vec<u8>>;
81 //pub type Frame = Box<[u8]>;
82 //pub type FramesData = Vec<Frame>;
83 //  `From<Box<[u8]>>` is not implemented for `Bytes`
84
85
86 #[derive(Default)]
87 pub struct Frames {
88   frames: FramesData,
89   total_len: usize,
90   tried_full: bool,
91 }
92
93 impl Debug for Frames {
94   #[throws(fmt::Error)]
95   fn fmt(&self, f: &mut fmt::Formatter) {
96     write!(f, "Frames{{n={},len={}}}", &self.frames.len(), &self.total_len)?;
97   }
98 }
99
100 impl Frames {
101   #[throws(Frame)]
102   pub fn add(&mut self, max: u32, frame: Frame) {
103     if frame.len() == 0 { return }
104     let new_total = self.total_len + frame.len() + 1;
105     if new_total > max.sat() { self.tried_full = true; throw!(frame); }
106     self.total_len = new_total;
107     self.frames.push(frame);
108   }
109
110   #[inline] pub fn tried_full(&self) -> bool { self.tried_full }
111   #[inline] pub fn is_empty(&self) -> bool { self.frames.is_empty() }
112 }
113
114 impl From<Frames> for FramesData {
115   fn from(frames: Frames) -> FramesData { frames.frames }
116 }
117
118 const HEADER_FOR_ADDR: usize = 40;
119
120 #[throws(PacketError)]
121 pub fn ip_packet_addr<const DST: bool>(packet: &[u8]) -> IpAddr {
122   let vsn = (packet.get(0).ok_or_else(|| PE::Empty)? & 0xf0) >> 4;
123   match vsn {
124     4 if packet.len() >= 20 => {
125       let slice = &packet[if DST { 16 } else { 12 }..][0..4];
126       Ipv4Addr::from(*<&[u8;4]>::try_from(slice).unwrap()).into()
127     },
128
129     6 if packet.len() >= 40 => {
130       let slice = &packet[if DST { 24 } else { 8 }..][0..16];
131       Ipv6Addr::from(*<&[u8;16]>::try_from(slice).unwrap()).into()
132     },
133
134     _ => throw!(PE::Bad{ vsn, len: packet.len() }),
135   }
136 }
137
138 #[derive(Copy,Clone,Eq,PartialEq,Ord,PartialOrd,Hash)]
139 pub struct DumpHex<'b>(pub &'b [u8]);
140 impl Debug for DumpHex<'_> {
141   #[throws(fmt::Error)]
142   fn fmt(&self, f: &mut fmt::Formatter) {
143     for v in self.0 { write!(f, "{:02x}", v)?; }
144   }
145 }
146
147 #[test]
148 fn mime_slip_to_mime() {
149   use PacketError as PE;
150   const MTU: u32 = 10;
151
152   fn chk(i: &[u8], exp_p: &[&[u8]], exp_e: &[PacketError]) {
153     let mut got_e = vec![];
154     let got_p = check::<_,_,true>(MTU, i, |_|Ok(()), |e| got_e.push(e));
155     assert_eq!( got_p.iter().map(|b| DumpHex(b)).collect_vec(),
156                 exp_p.iter().map(|b| DumpHex(b)).collect_vec() );
157     assert_eq!( got_e,
158                 exp_e );
159   }
160
161   chk( &[ SLIP_END, SLIP_ESC, SLIP_ESC_END, b'-',     b'X' ],
162     &[           &[ b'-',     SLIP_ESC_END, SLIP_ESC, b'X' ] ],
163     &[ PE::Empty ]);
164
165   chk( &[ SLIP_END, SLIP_ESC, b'y' ], &[],
166     &[ PE::Empty,   PE::SLIP ]);
167
168   chk( &[ SLIP_END, b'-',     b'y' ],
169     &[           &[ SLIP_ESC, b'y' ] ],
170     &[ PE::Empty ]);
171
172   chk( &[b'x'; 20],
173     &[             ],
174     &[ PE::MTU { len: 20, mtu: MTU } ]);
175 }