1pub const IAC: u8 = 255;
9pub const DONT: u8 = 254;
10pub const DO: u8 = 253;
11pub const WONT: u8 = 252;
12pub const WILL: u8 = 251;
13pub const SB: u8 = 250;
14pub const SE: u8 = 240;
15
16pub mod opt {
17 pub const BINARY: u8 = 0;
18 pub const ECHO: u8 = 1;
19 pub const SGA: u8 = 3;
20 pub const TTYPE: u8 = 24;
21 pub const NAWS: u8 = 31;
22}
23
24const TTYPE_IS: u8 = 0;
25const TTYPE_SEND: u8 = 1;
26const MAX_SB: usize = 1024;
27
28#[derive(Clone, Copy, Debug, PartialEq, Eq)]
29enum State {
30 Data,
31 Iac,
32 Negotiate(u8),
34 Sb,
35 SbIac,
36 Cr,
38}
39
40pub struct Telnet {
41 state: State,
42 local: [bool; 256],
44 remote: [bool; 256],
46 sb: Vec<u8>,
47 pub term_type: String,
48 cols: u16,
49 rows: u16,
50}
51
52impl Telnet {
53 pub fn new(term_type: &str, cols: u16, rows: u16) -> Self {
54 Telnet {
55 state: State::Data,
56 local: [false; 256],
57 remote: [false; 256],
58 sb: Vec::new(),
59 term_type: term_type.to_string(),
60 cols,
61 rows,
62 }
63 }
64
65 pub fn remote_echo(&self) -> bool {
67 self.remote[opt::ECHO as usize]
68 }
69
70 pub fn receive(&mut self, input: &[u8], data: &mut Vec<u8>, reply: &mut Vec<u8>) {
73 for &b in input {
74 self.state = match self.state {
75 State::Data | State::Cr => {
76 let after_cr = self.state == State::Cr;
77 match b {
78 IAC => State::Iac,
79 0 if after_cr => State::Data,
80 b'\r' => {
81 data.push(b);
82 State::Cr
83 }
84 _ => {
85 data.push(b);
86 State::Data
87 }
88 }
89 }
90 State::Iac => match b {
91 IAC => {
92 data.push(IAC);
93 State::Data
94 }
95 WILL | WONT | DO | DONT => State::Negotiate(b),
96 SB => {
97 self.sb.clear();
98 State::Sb
99 }
100 _ => State::Data,
102 },
103 State::Negotiate(cmd) => {
104 self.negotiate(cmd, b, reply);
105 State::Data
106 }
107 State::Sb => {
108 if b == IAC {
109 State::SbIac
110 } else {
111 if self.sb.len() < MAX_SB {
112 self.sb.push(b);
113 }
114 State::Sb
115 }
116 }
117 State::SbIac => match b {
118 SE => {
119 self.subnegotiation(reply);
120 State::Data
121 }
122 IAC => {
123 if self.sb.len() < MAX_SB {
124 self.sb.push(IAC);
125 }
126 State::Sb
127 }
128 _ => State::Sb,
129 },
130 };
131 }
132 }
133
134 fn supports_local(o: u8) -> bool {
135 matches!(o, opt::BINARY | opt::SGA | opt::TTYPE | opt::NAWS)
136 }
137
138 fn supports_remote(o: u8) -> bool {
139 matches!(o, opt::BINARY | opt::SGA | opt::ECHO)
140 }
141
142 fn negotiate(&mut self, cmd: u8, o: u8, reply: &mut Vec<u8>) {
143 let i = o as usize;
144 match cmd {
145 WILL => {
146 if !Self::supports_remote(o) {
147 reply.extend_from_slice(&[IAC, DONT, o]);
148 } else if !self.remote[i] {
149 self.remote[i] = true;
150 reply.extend_from_slice(&[IAC, DO, o]);
151 }
152 }
153 WONT if self.remote[i] => {
154 self.remote[i] = false;
155 reply.extend_from_slice(&[IAC, DONT, o]);
156 }
157 DO => {
158 if !Self::supports_local(o) {
159 reply.extend_from_slice(&[IAC, WONT, o]);
160 } else if !self.local[i] {
161 self.local[i] = true;
162 reply.extend_from_slice(&[IAC, WILL, o]);
163 if o == opt::NAWS {
164 self.naws(reply);
165 }
166 } else if o == opt::NAWS {
167 self.naws(reply);
169 }
170 }
171 DONT if self.local[i] => {
172 self.local[i] = false;
173 reply.extend_from_slice(&[IAC, WONT, o]);
174 }
175 _ => {}
176 }
177 }
178
179 fn subnegotiation(&mut self, reply: &mut Vec<u8>) {
180 if self.sb.first() == Some(&opt::TTYPE)
181 && self.sb.get(1) == Some(&TTYPE_SEND)
182 && self.local[opt::TTYPE as usize]
183 {
184 reply.extend_from_slice(&[IAC, SB, opt::TTYPE, TTYPE_IS]);
185 push_escaped(reply, self.term_type.as_bytes());
186 reply.extend_from_slice(&[IAC, SE]);
187 }
188 }
189
190 fn naws(&self, reply: &mut Vec<u8>) {
191 reply.extend_from_slice(&[IAC, SB, opt::NAWS]);
192 push_escaped(reply, &self.cols.to_be_bytes());
193 push_escaped(reply, &self.rows.to_be_bytes());
194 reply.extend_from_slice(&[IAC, SE]);
195 }
196
197 pub fn set_window_size(&mut self, cols: u16, rows: u16) -> Vec<u8> {
199 self.cols = cols;
200 self.rows = rows;
201 let mut out = Vec::new();
202 if self.local[opt::NAWS as usize] {
203 self.naws(&mut out);
204 }
205 out
206 }
207
208 pub fn encode(&self, data: &[u8]) -> Vec<u8> {
211 let binary = self.local[opt::BINARY as usize];
212 let mut out = Vec::with_capacity(data.len() + 4);
213 for (i, &b) in data.iter().enumerate() {
214 out.push(b);
215 if b == IAC {
216 out.push(IAC);
217 } else if b == b'\r' && !binary && data.get(i + 1) != Some(&b'\n') {
218 out.push(0);
219 }
220 }
221 out
222 }
223}
224
225fn push_escaped(out: &mut Vec<u8>, bytes: &[u8]) {
226 for &b in bytes {
227 out.push(b);
228 if b == IAC {
229 out.push(IAC);
230 }
231 }
232}
233
234#[cfg(test)]
235mod tests {
236 use super::*;
237
238 fn run(t: &mut Telnet, input: &[u8]) -> (Vec<u8>, Vec<u8>) {
239 let (mut d, mut r) = (Vec::new(), Vec::new());
240 t.receive(input, &mut d, &mut r);
241 (d, r)
242 }
243
244 #[test]
245 fn data_and_escaped_iac() {
246 let mut t = Telnet::new("ANSI", 80, 25);
247 let (d, r) = run(&mut t, &[b'a', IAC, IAC, b'b']);
248 assert_eq!(d, [b'a', 255, b'b']);
249 assert!(r.is_empty());
250 }
251
252 #[test]
253 fn negotiation_is_answered_once() {
254 let mut t = Telnet::new("ANSI", 80, 25);
255 let (_, r) = run(&mut t, &[IAC, WILL, opt::ECHO, IAC, WILL, opt::ECHO]);
256 assert_eq!(r, [IAC, DO, opt::ECHO]);
257 assert!(t.remote_echo());
258 let (_, r) = run(&mut t, &[IAC, DO, 42]);
259 assert_eq!(r, [IAC, WONT, 42]);
260 }
261
262 #[test]
263 fn naws_and_ttype() {
264 let mut t = Telnet::new("ANSI", 80, 25);
265 let (_, r) = run(&mut t, &[IAC, DO, opt::NAWS]);
266 assert_eq!(r, [IAC, WILL, opt::NAWS, IAC, SB, opt::NAWS, 0, 80, 0, 25, IAC, SE]);
267 let (_, r) = run(&mut t, &[IAC, DO, opt::TTYPE, IAC, SB, opt::TTYPE, 1, IAC, SE]);
268 let mut want = vec![IAC, WILL, opt::TTYPE, IAC, SB, opt::TTYPE, 0];
269 want.extend_from_slice(b"ANSI");
270 want.extend_from_slice(&[IAC, SE]);
271 assert_eq!(r, want);
272 }
273
274 #[test]
275 fn split_across_reads() {
276 let mut t = Telnet::new("ANSI", 80, 25);
277 let (d, _) = run(&mut t, &[b'x', IAC]);
278 assert_eq!(d, b"x");
279 let (d, r) = run(&mut t, &[WILL, opt::SGA, b'y']);
280 assert_eq!(d, b"y");
281 assert_eq!(r, [IAC, DO, opt::SGA]);
282 }
283
284 #[test]
285 fn encode_cr_and_iac() {
286 let t = Telnet::new("ANSI", 80, 25);
287 assert_eq!(t.encode(&[b'\r', 255]), [b'\r', 0, 255, 255]);
288 assert_eq!(t.encode(b"\r\n"), b"\r\n");
289 }
290
291 #[test]
292 fn cr_nul_from_server_is_stripped() {
293 let mut t = Telnet::new("ANSI", 80, 25);
294 let (d, _) = run(&mut t, b"a\r\0b");
295 assert_eq!(d, b"a\rb");
296 }
297}