libmudtelnet/
lib.rs

1#![cfg_attr(not(feature = "std"), no_std)]
2#![warn(clippy::pedantic)]
3#![allow(
4  clippy::module_name_repetitions,
5  clippy::fn_params_excessive_bools,
6  clippy::struct_excessive_bools,
7  deprecated
8)]
9
10#[cfg(not(feature = "std"))]
11extern crate alloc;
12#[cfg(feature = "std")]
13extern crate std as alloc;
14
15use alloc::{format, vec, vec::Vec};
16
17use bytes::{BufMut, Bytes, BytesMut};
18
19pub use bytes;
20pub mod compatibility;
21pub mod events;
22pub mod telnet;
23
24use compatibility::{CompatibilityEntry, CompatibilityTable};
25use events::{TelnetEvents, TelnetNegotiation, TelnetSubnegotiation};
26use telnet::op_command::{DO, DONT, EOR, GA, IAC, NOP, SB, SE, WILL, WONT};
27
28enum EventType {
29  None(Bytes),
30  Iac(Bytes),
31  SubNegotiation(Bytes, Option<Bytes>),
32  Neg(Bytes),
33}
34
35#[deprecated(
36  since = "0.2.1",
37  note = "Use `Bytes::copy_from_slice` directly instead."
38)]
39#[macro_export]
40/// Macro for calling `Bytes::copy_from_slice()`
41macro_rules! vbytes {
42  ($slice:expr) => {
43    Bytes::copy_from_slice($slice)
44  };
45}
46
47/// A telnet parser that handles the main parts of the protocol.
48pub struct Parser {
49  pub options: CompatibilityTable,
50  buffer: BytesMut,
51}
52
53impl Default for Parser {
54  fn default() -> Self {
55    Parser::with_capacity(128)
56  }
57}
58
59impl Parser {
60  /// Create a default, empty Parser with an internal buffer capacity of 128 bytes.
61  #[must_use]
62  pub fn new() -> Self {
63    Self::default()
64  }
65
66  /// Create an empty parser, setting the initial internal buffer capcity.
67  #[must_use]
68  pub fn with_capacity(size: usize) -> Self {
69    Self::with_support_and_capacity(size, CompatibilityTable::default())
70  }
71
72  /// Create a parser, directly supplying a `CompatibilityTable`.
73  ///
74  /// Uses the default initial buffer capacity of 128 bytes.
75  #[must_use]
76  pub fn with_support(table: CompatibilityTable) -> Self {
77    Self::with_support_and_capacity(128, table)
78  }
79
80  /// Create an parser, setting the initial internal buffer capacity and directly supplying a `CompatibilityTable`.
81  // TODO(@cpu): 'table' should be first arg to match name.
82  #[must_use]
83  pub fn with_support_and_capacity(size: usize, table: CompatibilityTable) -> Self {
84    Self {
85      options: table,
86      buffer: BytesMut::with_capacity(size),
87    }
88  }
89
90  /// Receive bytes into the internal buffer.
91  ///
92  /// # Arguments
93  ///
94  /// * `data` - The bytes to be received. This should be sourced from the remote side of a connection.
95  ///
96  /// # Returns
97  ///
98  /// `Vec<TelnetEvents>` - Any events parsed from the internal buffer with the new bytes.
99  ///
100  pub fn receive(&mut self, data: &[u8]) -> Vec<TelnetEvents> {
101    self.buffer.put(data);
102    self.process()
103  }
104
105  /// Get whether the remote end supports and is using linemode.
106  pub fn linemode_enabled(&mut self) -> bool {
107    matches!(
108      self.options.get_option(telnet::op_option::LINEMODE),
109      CompatibilityEntry {
110        remote: true,
111        remote_state: true,
112        ..
113      }
114    )
115  }
116
117  /// Escape IAC bytes in data that is to be transmitted and treated as a non-IAC sequence.
118  ///
119  /// # Example
120  /// `[255, 1, 6, 2]` -> `[255, 255, 1, 6, 2]`
121  pub fn escape_iac<T>(data: T) -> Bytes
122  where
123    Bytes: From<T>,
124  {
125    let data = Bytes::from(data);
126    let mut res = BytesMut::with_capacity(data.len());
127    for byte in data {
128      res.put_u8(byte);
129      if byte == IAC {
130        res.put_u8(IAC);
131      }
132    }
133    res.freeze()
134  }
135
136  /// Reverse escaped IAC bytes for non-IAC sequences and data.
137  ///
138  /// # Example
139  /// `[255, 255, 1, 6, 2]` -> `[255, 1, 6, 2]`
140  pub fn unescape_iac<T>(data: T) -> Bytes
141  where
142    Bytes: From<T>,
143  {
144    #[derive(Debug, Clone, Copy)]
145    enum States {
146      Normal,
147      Iac,
148    }
149
150    let data = Bytes::from(data);
151    let mut res = BytesMut::with_capacity(data.len());
152
153    let mut state = States::Normal;
154    let mut out_val;
155    for val in data {
156      (state, out_val) = match (state, val) {
157        (States::Normal, IAC) => (States::Iac, Some(val)),
158        (States::Iac, IAC) => (States::Normal, None),
159        (States::Normal | States::Iac, _) => (States::Normal, Some(val)),
160      };
161      if let Some(val) = out_val {
162        res.put_u8(val);
163      }
164    }
165
166    res.freeze()
167  }
168
169  /// Negotiate an option.
170  ///
171  /// # Arguments
172  ///
173  /// `command` - A `u8` representing the telnet command code to be negotiated with. Example: WILL (251), WONT (252), DO (253), DONT (254)
174  ///
175  /// `option` - A `u8` representing the telnet option code that is being negotiated.
176  ///
177  /// # Returns
178  ///
179  /// `TelnetEvents::DataSend` - A `DataSend` event to be processed.
180  ///
181  /// # Usage
182  ///
183  /// This and other methods meant for sending data to the remote end will generate a `TelnetEvents::Send(DataEvent)` event.
184  ///
185  /// These Send events contain a buffer that should be sent directly to the remote end, as it will have already been encoded properly.
186  pub fn negotiate(&mut self, command: u8, option: u8) -> TelnetEvents {
187    TelnetEvents::build_send(TelnetNegotiation::new(command, option).to_bytes())
188  }
189
190  /// Indicate to the other side that you are able and wanting to utilize an option.
191  ///
192  /// # Arguments
193  ///
194  /// `option` - A `u8` representing the telnet option code that you want to enable locally.
195  ///
196  /// # Returns
197  ///
198  /// `Option<TelnetEvents::DataSend>` - The `DataSend` event to be processed, or None if not supported.
199  ///
200  /// # Notes
201  ///
202  /// This method will do nothing if the option is not "supported" locally via the `CompatibilityTable`.
203  pub fn _will(&mut self, option: u8) -> Option<TelnetEvents> {
204    match self.options.get_option(option) {
205      mut opt @ CompatibilityEntry {
206        local: true,
207        local_state: false,
208        ..
209      } => {
210        opt.local_state = true;
211        self.options.set_option(option, opt);
212        Some(self.negotiate(WILL, option))
213      }
214      _ => None,
215    }
216  }
217
218  /// Indicate to the other side that you are not wanting to utilize an option.
219  ///
220  /// # Arguments
221  ///
222  /// `option` - A `u8` representing the telnet option code that you want to disable locally.
223  ///
224  /// # Returns
225  ///
226  /// `Option<TelnetEvents::DataSend>` - A `DataSend` event to be processed, or None if the option is already disabled.
227  ///
228  pub fn _wont(&mut self, option: u8) -> Option<TelnetEvents> {
229    match self.options.get_option(option) {
230      mut opt @ CompatibilityEntry {
231        local_state: true, ..
232      } => {
233        opt.local_state = false;
234        self.options.set_option(option, opt);
235        Some(self.negotiate(WONT, option))
236      }
237      _ => None,
238    }
239  }
240
241  /// Indicate to the other side that you would like them to utilize an option.
242  ///
243  /// # Arguments
244  ///
245  /// `option` - A `u8` representing the telnet option code that you want to enable remotely.
246  ///
247  /// # Returns
248  ///
249  /// `Option<TelnetEvents::DataSend>` - A `DataSend` event to be processed, or None if the option is not supported or already enabled.
250  ///
251  /// # Notes
252  ///
253  /// This method will do nothing if the option is not "supported" remotely via the `CompatibilityTable`.
254  pub fn _do(&mut self, option: u8) -> Option<TelnetEvents> {
255    match self.options.get_option(option) {
256      CompatibilityEntry {
257        remote: true,
258        remote_state: false,
259        ..
260      } => Some(self.negotiate(DO, option)),
261      _ => None,
262    }
263  }
264
265  /// Indicate to the other side that you would like them to stop utilizing an option.
266  ///
267  /// # Arguments
268  ///
269  /// `option` - A `u8` representing the telnet option code that you want to disable remotely.
270  ///
271  /// # Returns
272  ///
273  /// `Option<TelnetEvents::DataSend>` - A `DataSend` event to be processed, or None if the option is already disabled.
274  ///
275  pub fn _dont(&mut self, option: u8) -> Option<TelnetEvents> {
276    match self.options.get_option(option) {
277      CompatibilityEntry {
278        remote_state: true, ..
279      } => Some(self.negotiate(DONT, option)),
280      _ => None,
281    }
282  }
283
284  /// Send a subnegotiation for a locally supported option.
285  ///
286  /// # Arguments
287  ///
288  /// `option` - A `u8` representing the telnet option code for the negotiation.
289  ///
290  /// `data` - A `Bytes` containing the data to be sent in the subnegotiation. This data will have all IAC (255) byte values escaped.
291  ///
292  /// # Returns
293  ///
294  /// `Option<TelnetEvents::DataSend>` - A `DataSend` event to be processed, or None if the option is not supported or is currently disabled.
295  ///
296  /// # Notes
297  ///
298  /// This method will do nothing if the option is not "supported" locally via the `CompatibilityTable`.
299  pub fn subnegotiation<T>(&mut self, option: u8, data: T) -> Option<TelnetEvents>
300  where
301    Bytes: From<T>,
302  {
303    match self.options.get_option(option) {
304      CompatibilityEntry {
305        local: true,
306        local_state: true,
307        ..
308      } => Some(TelnetEvents::build_send(
309        TelnetSubnegotiation::new(option, Bytes::from(data)).to_bytes(),
310      )),
311      _ => None,
312    }
313  }
314
315  /// Send a subnegotiation for a locally supported option, using a string instead of raw byte values.
316  ///
317  /// # Arguments
318  ///
319  /// `option` - A `u8` representing the telnet option code for the negotiation.
320  ///
321  /// `text` - A `&str` representing the text to be sent in the subnegotation. This data will have all IAC (255) byte values escaped.
322  ///
323  /// # Returns
324  ///
325  /// `Option<TelnetEvents::DataSend>` - A `DataSend` event to be processed, or None if the option is not supported or is currently disabled.
326  ///
327  /// # Notes
328  ///
329  /// This method will do nothing if the option is not "supported" locally via the `CompatibilityTable`.
330  pub fn subnegotiation_text(&mut self, option: u8, text: &str) -> Option<TelnetEvents> {
331    self.subnegotiation(option, Bytes::copy_from_slice(text.as_bytes()))
332  }
333
334  /// Directly send a string, with appended `\r\n`, to the remote end, along with an `IAC (255) GOAHEAD (249)` sequence.
335  ///
336  /// # Returns
337  ///
338  /// `TelnetEvents::DataSend` - A `DataSend` event to be processed.
339  ///
340  /// # Notes
341  ///
342  /// The string will have IAC (255) bytes escaped before being sent.
343  pub fn send_text(&mut self, text: &str) -> TelnetEvents {
344    TelnetEvents::build_send(Parser::escape_iac(format!("{text}\r\n")))
345  }
346
347  /// Extract sub-buffers from the current buffer
348  fn extract_event_data(&mut self) -> Vec<EventType> {
349    #[derive(Copy, Clone)]
350    enum State {
351      Normal,
352      Iac,
353      Neg,
354      Sub,
355      SubOpt { opt: u8 },
356      SubIac { opt: u8 },
357    }
358
359    let mut events = Vec::with_capacity(4);
360    let mut iter_state = State::Normal;
361    let mut cmd_begin = 0;
362
363    // Empty self.buffer into an immutable Bytes we can process.
364    // We'll create views of this buffer to pass to the events using 'buf.slice'.
365    // Splitting is O(1) and doesn't copy the data. Freezing is zero-cost. Taking a slice is O(1).
366    let buf = self.buffer.split().freeze();
367    for (index, &val) in buf.iter().enumerate() {
368      (iter_state, cmd_begin) = match (&iter_state, val) {
369        (State::Normal, IAC) => {
370          if cmd_begin != index {
371            events.push(EventType::None(buf.slice(cmd_begin..index)));
372          }
373          (State::Iac, index)
374        }
375        (State::Iac, IAC) => (State::Normal, cmd_begin), // Double IAC, ignore,
376        (State::Iac, GA | EOR | NOP) => {
377          events.push(EventType::Iac(buf.slice(cmd_begin..=index)));
378          (State::Normal, index + 1)
379        }
380        (State::Iac, SB) => (State::Sub, cmd_begin),
381        (State::Iac, _) => (State::Neg, cmd_begin), // WILL | WONT | DO | DONT | IS | SEND
382        (State::Neg, _) => {
383          events.push(EventType::Neg(buf.slice(cmd_begin..=index)));
384          (State::Normal, index + 1)
385        }
386        (State::Sub, opt) => (State::SubOpt { opt }, cmd_begin),
387        (State::SubOpt { opt } | State::SubIac { opt }, IAC) => {
388          (State::SubIac { opt: *opt }, cmd_begin)
389        }
390        (State::SubIac { opt }, SE)
391          if *opt == telnet::op_option::MCCP2 || *opt == telnet::op_option::MCCP3 =>
392        {
393          // MCCP2/MCCP3 MUST DECOMPRESS DATA AFTER THIS!
394          events.push(EventType::SubNegotiation(
395            buf.slice(cmd_begin..=index),
396            Some(buf.slice(index + 1..)),
397          ));
398          cmd_begin = buf.len();
399          break;
400        }
401        (State::SubIac { .. }, SE) => {
402          events.push(EventType::SubNegotiation(
403            buf.slice(cmd_begin..=index),
404            None,
405          ));
406          (State::Normal, index + 1)
407        }
408        (State::SubIac { opt }, _) => (State::SubOpt { opt: *opt }, cmd_begin),
409        (cur_state, _) => (*cur_state, cmd_begin),
410      };
411    }
412
413    if cmd_begin < buf.len() {
414      match iter_state {
415        State::Sub | State::SubOpt { .. } | State::SubIac { .. } => {
416          events.push(EventType::SubNegotiation(buf.slice(cmd_begin..), None));
417        }
418        _ => events.push(EventType::None(buf.slice(cmd_begin..))),
419      }
420    }
421
422    events
423  }
424
425  /// The internal parser method that takes the current buffer and generates the corresponding events.
426  fn process(&mut self) -> Vec<TelnetEvents> {
427    let mut event_list = Vec::with_capacity(2);
428    let events = self.extract_event_data();
429    for event in events {
430      match event {
431        EventType::None(buffer) | EventType::Iac(buffer) | EventType::Neg(buffer) => {
432          match (buffer.first(), buffer.get(1), buffer.get(2)) {
433            (Some(&IAC), Some(command), None) if *command != SE => {
434              // IAC command
435              event_list.push(TelnetEvents::build_iac(*command));
436            }
437            (Some(&IAC), Some(command), Some(opt)) => {
438              // Negotiation command
439              event_list.extend(self.process_negotiation(*command, *opt));
440            }
441            (Some(c), _, _) if *c != IAC => {
442              // Not an iac sequence, it's data!
443              event_list.push(TelnetEvents::build_receive(buffer));
444            }
445            _ => {}
446          }
447        }
448        EventType::SubNegotiation(buffer, remaining) => {
449          let len = buffer.len();
450          if buffer[len - 2] == IAC && buffer[len - 1] == SE {
451            // Valid ending
452            let opt = self.options.get_option(buffer[2]);
453            if opt.local && opt.local_state && len - 2 >= 3 {
454              event_list.push(TelnetEvents::build_subnegotiation(
455                buffer[2],
456                vbytes!(&buffer[3..len - 2]),
457              ));
458              if let Some(rbuf) = remaining {
459                event_list.push(TelnetEvents::DecompressImmediate(rbuf));
460              }
461            }
462          } else {
463            // Missing the rest
464            self.buffer.put(&buffer[..]);
465          }
466        }
467      }
468    }
469    event_list
470  }
471
472  fn process_negotiation(&mut self, command: u8, opt: u8) -> Vec<TelnetEvents> {
473    let event = TelnetNegotiation::new(command, opt);
474    match (command, self.options.get_option(opt)) {
475      (
476        WILL,
477        mut entry @ CompatibilityEntry {
478          remote: true,
479          remote_state: false,
480          ..
481        },
482      ) => {
483        entry.remote_state = true;
484        self.options.set_option(opt, entry);
485        vec![
486          TelnetEvents::build_send(vbytes!(&[IAC, DO, opt])),
487          TelnetEvents::Negotiation(event),
488        ]
489      }
490      (WILL, CompatibilityEntry { remote: false, .. }) => {
491        vec![TelnetEvents::build_send(vbytes!(&[IAC, DONT, opt]))]
492      }
493      (
494        WONT,
495        mut entry @ CompatibilityEntry {
496          remote_state: true, ..
497        },
498      ) => {
499        entry.remote_state = false;
500        self.options.set_option(opt, entry);
501        vec![
502          TelnetEvents::build_send(vbytes!(&[IAC, DONT, opt])),
503          TelnetEvents::Negotiation(event),
504        ]
505      }
506      (
507        DO,
508        mut entry @ CompatibilityEntry {
509          local: true,
510          local_state: false,
511          ..
512        },
513      ) => {
514        entry.local_state = true;
515        entry.remote_state = true;
516        self.options.set_option(opt, entry);
517        vec![
518          TelnetEvents::build_send(vbytes!(&[IAC, WILL, opt])),
519          TelnetEvents::Negotiation(event),
520        ]
521      }
522      (
523        DO,
524        CompatibilityEntry {
525          local_state: false, ..
526        }
527        | CompatibilityEntry { local: false, .. },
528      ) => {
529        vec![TelnetEvents::build_send(vbytes!(&[IAC, WONT, opt]))]
530      }
531      (
532        DONT,
533        mut entry @ CompatibilityEntry {
534          local_state: true, ..
535        },
536      ) => {
537        entry.local_state = false;
538        self.options.set_option(opt, entry);
539        vec![
540          TelnetEvents::build_send(vbytes!(&[IAC, WONT, opt])),
541          TelnetEvents::Negotiation(event),
542        ]
543      }
544      (DONT | WONT, CompatibilityEntry { .. }) => {
545        vec![TelnetEvents::Negotiation(event)]
546      }
547      _ => Vec::default(),
548    }
549  }
550}