rfc2217_rs/
subnegotiation.rs

1use crate::codes;
2
3pub const MAX_DATA_SIZE: usize = 256;
4pub const NONDATA_SIZE: usize = 6;
5pub const MAX_SIZE: usize = MAX_DATA_SIZE + NONDATA_SIZE;
6
7// RFC2217 subnegotiation options, defined here: https://www.rfc-editor.org/rfc/rfc2217.html
8#[derive(Debug, PartialEq, Eq, Clone, Copy)]
9pub enum Subnegotiation {
10    SetSignature {
11        data: [u8; MAX_DATA_SIZE],
12        size: u8,
13    },
14    SetBaudRate(u32),
15    SetDataSize(u8),
16    SetParity(u8),
17    SetStopSize(u8),
18    SetControl(u8),
19    NotifyLineState(u8),
20    NotifyModemState(u8),
21    FlowControlSuspend,
22    FlowControlResume,
23    SetLinestateMask(u8),
24    SetModemStateMask(u8),
25    PurgeData(u8),
26    Unsupported {
27        base_option_code: u8,
28        option_code: u8,
29        data: [u8; MAX_DATA_SIZE],
30        data_cnt: u8,
31    },
32}
33
34// The codes for client to server and server to client ComPort options differ by 100,
35// this indicates which one the serializer should pick
36#[derive(Clone, Copy)]
37enum OptionKind {
38    ClientToServer,
39    ServerToClient,
40}
41
42impl Subnegotiation {
43    pub fn serialize_client(&self, buf: &mut [u8]) -> usize {
44        self.serialize(buf, OptionKind::ClientToServer)
45    }
46
47    pub fn serialize_server(&self, buf: &mut [u8]) -> usize {
48        self.serialize(buf, OptionKind::ServerToClient)
49    }
50
51    fn serialize(&self, buf: &mut [u8], option_kind: OptionKind) -> usize {
52        let start = |option_code: u8| -> [u8; 4] {
53            [
54                codes::IAC,
55                codes::SB,
56                codes::COM_PORT_OPTION,
57                match option_kind {
58                    OptionKind::ClientToServer => option_code,
59                    OptionKind::ServerToClient => option_code + 100,
60                },
61            ]
62        };
63
64        let end = [codes::IAC, codes::SE];
65
66        let mut subnegotiate = |option_code: u8, data: &[u8]| -> usize {
67            buf[..4].copy_from_slice(&start(option_code));
68            buf[4..4 + data.len()].copy_from_slice(data);
69            buf[4 + data.len()..NONDATA_SIZE + data.len()].copy_from_slice(&end);
70            NONDATA_SIZE + data.len()
71        };
72
73        match *self {
74            Self::SetSignature { data, size } => {
75                buf[..4].copy_from_slice(&start(0));
76                let data_slice = &data[..size as usize];
77                let mut i = 4;
78                for byte in data_slice {
79                    buf[i] = *byte;
80                    i += 1;
81                    // Make sure to escape IAC bytes in the signature
82                    if *byte == codes::IAC {
83                        buf[i] = *byte;
84                        i += 1;
85                    }
86                }
87                buf[i..i + 2].copy_from_slice(&end);
88                i + 2
89            }
90
91            Self::SetBaudRate(baud) => subnegotiate(1, &u32::to_be_bytes(baud)),
92            Self::SetDataSize(data_size) => subnegotiate(2, &[data_size]),
93            Self::SetParity(parity) => subnegotiate(3, &[parity]),
94            Self::SetStopSize(stopsize) => subnegotiate(4, &[stopsize]),
95            Self::SetControl(control) => subnegotiate(5, &[control]),
96            Self::NotifyLineState(linestate) => subnegotiate(6, &[linestate]),
97            Self::NotifyModemState(modemstate) => subnegotiate(7, &[modemstate]),
98            Self::FlowControlSuspend => subnegotiate(8, &[]),
99            Self::FlowControlResume => subnegotiate(9, &[]),
100            Self::SetLinestateMask(linestate_mask) => subnegotiate(10, &[linestate_mask]),
101            Self::SetModemStateMask(modemstate_mask) => subnegotiate(11, &[modemstate_mask]),
102            Self::PurgeData(purge_data) => subnegotiate(12, &[purge_data]),
103            Self::Unsupported {
104                base_option_code,
105                option_code,
106                data,
107                data_cnt,
108            } => {
109                buf[..4].copy_from_slice(&[codes::IAC, codes::SB, base_option_code, option_code]);
110                buf[4..4 + data_cnt as usize].copy_from_slice(&data[..data_cnt as usize]);
111                buf[4 + data_cnt as usize..NONDATA_SIZE + data_cnt as usize].copy_from_slice(&end);
112                NONDATA_SIZE + data_cnt as usize
113            }
114        }
115    }
116
117    pub fn deserialize(buf: &[u8]) -> Self {
118        assert!(
119            buf[0] == codes::IAC
120                && buf[1] == codes::SB
121                && buf[buf.len() - 2] == codes::IAC
122                && buf[buf.len() - 1] == codes::SE
123        );
124
125        let base_option_code = buf[2];
126        let option_code = buf[3];
127        let data_len = buf.len() - NONDATA_SIZE;
128        let data = &buf[4..4 + data_len];
129
130        match base_option_code {
131            codes::COM_PORT_OPTION => match option_code {
132                0 | 100 => {
133                    let mut data_no_escapes = [0; MAX_DATA_SIZE];
134                    let mut i = 0;
135                    let mut iac_occured = false;
136                    for &byte in data {
137                        if byte == codes::IAC {
138                            if iac_occured {
139                                iac_occured = false;
140                                continue;
141                            } else {
142                                iac_occured = true;
143                            }
144                        }
145                        data_no_escapes[i] = byte;
146                        i += 1;
147                    }
148                    Self::SetSignature {
149                        data: data_no_escapes,
150                        size: i as u8,
151                    }
152                }
153                1 | 101 => {
154                    let baud_rate = u32::from_be_bytes([data[0], data[1], data[2], data[3]]);
155                    Self::SetBaudRate(baud_rate)
156                }
157                2 | 102 => Self::SetDataSize(data[0]),
158                3 | 103 => Self::SetParity(data[0]),
159                4 | 104 => Self::SetStopSize(data[0]),
160                5 | 105 => Self::SetControl(data[0]),
161                6 | 106 => Self::NotifyLineState(data[0]),
162                7 | 107 => Self::NotifyModemState(data[0]),
163                8 | 108 => Self::FlowControlSuspend,
164                9 | 109 => Self::FlowControlResume,
165                10 | 110 => Self::SetLinestateMask(data[0]),
166                11 | 111 => Self::SetModemStateMask(data[0]),
167                12 | 112 => Self::PurgeData(data[0]),
168                _ => panic!("Option code is not a Com Port option code"),
169            },
170            _ => {
171                let mut data_arr = [0; MAX_DATA_SIZE];
172                data_arr.copy_from_slice(data);
173                Self::Unsupported {
174                    base_option_code: base_option_code,
175                    option_code: option_code,
176                    data: data_arr,
177                    data_cnt: data_len as u8,
178                }
179            }
180        }
181    }
182}