1#![doc = include_str!("../README.md")]
2#![forbid(unsafe_code)]
3
4use std::mem;
10
11use bytes::{Buf, BufMut, Bytes, BytesMut};
12use tokio_util::codec::{Decoder, Encoder};
13
14use crate::{
15 constants::{
16 CHARSET, CHARSET_ACCEPTED, CHARSET_REJECTED, CHARSET_REQUEST, CHARSET_TTABLE_REJECTED, DO,
17 DONT, ENVIRON, IAC, LINEMODE, LINEMODE_FORWARD_MASK, LINEMODE_SLC, MODE, NAWS, NOP, SB, SE,
18 WILL, WONT,
19 },
20 env::{decode_env, encode_env_op},
21 error::TelnetError,
22 event::TelnetEvent,
23 linemode::ForwardMaskOption,
24 option::TelnetOption,
25 subnegotiation::{LineModeOption, SubnegotiationType},
26};
27
28pub mod constants;
30pub mod env;
32pub mod error;
34pub mod event;
36pub mod linemode;
38pub mod option;
40pub mod subnegotiation;
42
43type Result<T> = std::result::Result<T, TelnetError>;
44
45#[derive(Debug)]
50pub struct TelnetCodec {
51 pub sga: bool,
53 pub max_buffer_length: usize,
54 pub buffer: Vec<u8>,
55 pub message_mode: bool,
58 #[cfg(feature = "unicode")]
60 pub unicode: bool,
61}
62
63impl TelnetCodec {
64 #[must_use]
65 pub fn new(max_buffer_length: usize) -> Self {
66 TelnetCodec {
67 sga: false,
68 max_buffer_length,
69 buffer: Vec::new(),
70 message_mode: true,
71 #[cfg(feature = "unicode")]
72 unicode: false,
73 }
74 }
75}
76
77impl Decoder for TelnetCodec {
78 type Item = TelnetEvent;
79 type Error = TelnetError;
80
81 fn decode(&mut self, buffer: &mut BytesMut) -> Result<Option<Self::Item>> {
82 let mut byte_index = 0;
83
84 if self.sga && !self.buffer.is_empty() {
85 let buf = mem::take(&mut self.buffer);
86 let result = String::from_utf8_lossy(&buf[..]);
87
88 return Ok(Some(TelnetEvent::Message(result.to_string())));
89 }
90
91 if buffer.is_empty() {
92 return Ok(None);
93 }
94
95 if self.sga {
96 return Ok(decode_suppress_go_ahead(&mut byte_index, buffer));
97 }
98
99 Ok(decode_bytes(self, &mut byte_index, buffer))
100 }
101}
102
103impl Encoder<TelnetEvent> for TelnetCodec {
104 type Error = TelnetError;
105
106 fn encode(&mut self, event: TelnetEvent, buffer: &mut BytesMut) -> Result<()> {
107 match event {
108 TelnetEvent::Do(option) => encode_negotiate(DO, option, buffer),
109 TelnetEvent::Dont(option) => encode_negotiate(DONT, option, buffer),
110 TelnetEvent::Will(option) => encode_negotiate(WILL, option, buffer),
111 TelnetEvent::Wont(option) => encode_negotiate(WONT, option, buffer),
112 TelnetEvent::Subnegotiate(sb_type) => encode_sb(sb_type, buffer),
113 TelnetEvent::Message(msg) => encode_message(msg, buffer),
114 TelnetEvent::RawMessage(msg) => encode_raw_message(msg, buffer),
115 _ => {}
116 }
117
118 Ok(())
119 }
120}
121
122#[cfg(feature = "unicode")]
123fn decode_utf8(byte_index: usize, buffer: &mut BytesMut, start: u8) -> Option<TelnetEvent> {
124 let length = match start {
125 0xC2..=0xDF => 2,
126 0xE0..=0xEF => 3,
127 0xF0..=0xF4 => 4,
129 _ => 1,
130 };
131
132 if length == 1 {
133 buffer.advance(byte_index + 1);
134 Some(TelnetEvent::Unicode(start as char))
135 } else {
136 if let Ok(s) = std::str::from_utf8(&buffer[byte_index..byte_index + length]) {
137 if s.chars().count() != 1 {
138 buffer.advance(byte_index + length);
142 return Some(TelnetEvent::Nop);
143 }
144
145 let c = s.chars().next().unwrap();
147
148 buffer.advance(byte_index + length);
149 return Some(TelnetEvent::Unicode(c));
150 }
151
152 buffer.advance(byte_index + length);
156 Some(TelnetEvent::Nop)
157 }
158}
159
160fn decode_negotiate(byte_index: usize, buffer: &mut BytesMut, option: u8) -> Option<TelnetEvent> {
161 if byte_index + 2 >= buffer.len() {
162 return None;
163 }
164
165 let byte = buffer[byte_index + 2];
166 buffer.advance(byte_index + 3);
167 match option {
168 WILL => Some(TelnetEvent::Will(byte.into())),
169 WONT => Some(TelnetEvent::Wont(byte.into())),
170 DO => Some(TelnetEvent::Do(byte.into())),
171 DONT => Some(TelnetEvent::Dont(byte.into())),
172 _ => None,
173 }
174}
175
176fn decode_suppress_go_ahead(byte_index: &mut usize, buffer: &mut BytesMut) -> Option<TelnetEvent> {
177 match buffer[0] {
178 IAC => {
179 if 1 >= buffer.len() {
180 return None;
181 }
182
183 match buffer[*byte_index + 1] {
184 IAC => {
185 buffer.advance(2);
186 Some(TelnetEvent::Character(IAC))
187 }
188 _ => None,
189 }
190 }
191 _ => None,
192 }
193}
194
195fn decode_negotiate_about_window_size(subvec: &[u8]) -> Option<TelnetEvent> {
196 match subvec.len() {
197 4 => {
198 let result = SubnegotiationType::WindowSize(
199 (u16::from(subvec[0]) << 8) | u16::from(subvec[1]),
200 (u16::from(subvec[2]) << 8) | u16::from(subvec[3]),
201 );
202 Some(TelnetEvent::Subnegotiate(result))
203 }
204 _ => None,
205 }
206}
207
208fn decode_linemode(subvec: &[u8]) -> Option<TelnetEvent> {
209 if subvec.is_empty() {
210 return None;
211 }
212
213 let suboption = match subvec[0] {
214 WILL | WONT | DO | DONT => LineModeOption::ForwardMask(ForwardMaskOption::from(subvec[0])),
215 _ => LineModeOption::from(subvec[0]),
216 };
217
218 match suboption {
219 LineModeOption::SLC(_) => {
220 let slc_data = &subvec[1..];
221
222 let slc_triples = slc_data
224 .chunks_exact(3)
225 .map(|chunk| ((chunk[0], chunk[1]).into(), chunk[2] as char))
226 .collect();
227 Some(TelnetEvent::Subnegotiate(SubnegotiationType::LineMode(LineModeOption::SLC(
228 slc_triples,
229 ))))
230 }
231 LineModeOption::ForwardMask(_) => {
232 let data = &subvec[2..];
233 let option = match subvec[0] {
234 DO => ForwardMaskOption::Do(data.to_vec()),
235 byte => ForwardMaskOption::from(byte),
236 };
237
238 Some(TelnetEvent::Subnegotiate(SubnegotiationType::LineMode(
239 LineModeOption::ForwardMask(option),
240 )))
241 }
242 LineModeOption::Mode(_) => {
243 let mode = subvec[1];
244
245 Some(TelnetEvent::Subnegotiate(SubnegotiationType::LineMode(LineModeOption::Mode(
246 mode,
247 ))))
248 }
249 LineModeOption::Unknown(_, _) => {
250 let data = &subvec[1..];
251 Some(TelnetEvent::Subnegotiate(SubnegotiationType::LineMode(LineModeOption::Unknown(
252 subvec[0],
253 Bytes::from(data.to_vec()),
254 ))))
255 }
256 }
257}
258
259fn decode_charset(subvec: &[u8]) -> Option<TelnetEvent> {
260 if subvec.is_empty() {
261 return None;
262 }
263
264 match subvec[0] {
265 CHARSET_REQUEST => {
266 if subvec.len() == 1 {
267 return None;
268 }
269
270 let separator = subvec[1];
271 let charsets: Vec<_> =
272 subvec[2..].split(|&x| x == separator).map(|x| Bytes::from(x.to_vec())).collect();
273
274 if charsets.is_empty() {
275 return None;
276 }
277
278 let result = SubnegotiationType::CharsetRequest(charsets);
279 Some(TelnetEvent::Subnegotiate(result))
280 }
281 CHARSET_ACCEPTED => {
282 let result = SubnegotiationType::CharsetAccepted(Bytes::from(subvec[1..].to_vec()));
283 Some(TelnetEvent::Subnegotiate(result))
284 }
285 CHARSET_REJECTED => {
286 let result = SubnegotiationType::CharsetRejected;
287 Some(TelnetEvent::Subnegotiate(result))
288 }
289 CHARSET_TTABLE_REJECTED => {
290 let result = SubnegotiationType::CharsetTTableRejected;
291 Some(TelnetEvent::Subnegotiate(result))
292 }
293 _ => None,
294 }
295}
296
297fn decode_unknown(option: u8, subvec: Vec<u8>) -> TelnetEvent {
298 TelnetEvent::Subnegotiate(SubnegotiationType::Unknown(option.into(), Bytes::from(subvec)))
299}
300
301fn decode_next_byte(codec: &mut TelnetCodec, buffer_size: &mut usize, byte: u8) {
302 if buffer_size < &mut codec.max_buffer_length {
303 codec.buffer.push(byte);
304 *buffer_size += 1;
305 }
306}
307
308fn decode_subnegotiation_end(
309 invalid: bool,
310 buffer: &mut BytesMut,
311 subvec: Vec<u8>,
312 option: u8,
313) -> Option<TelnetEvent> {
314 if invalid {
315 None
316 } else {
317 let opt = match option {
318 NAWS => decode_negotiate_about_window_size(&subvec),
319 CHARSET => decode_charset(&subvec),
320 LINEMODE => decode_linemode(&subvec),
321 ENVIRON => decode_env(&subvec),
322 _ => Some(decode_unknown(option, subvec)),
323 };
324
325 if let Some(event) = &opt {
326 buffer.advance(event.len());
327 }
328
329 opt
330 }
331}
332
333fn decode_bytes(
334 codec: &mut TelnetCodec,
335 byte_index: &mut usize,
336 buffer: &mut BytesMut,
337) -> Option<TelnetEvent> {
338 let mut codec_buffer_size = codec.buffer.len();
339
340 loop {
341 if *byte_index >= buffer.len() {
342 return None;
343 }
344
345 match buffer[*byte_index] {
347 IAC => {
348 if *byte_index + 1 >= buffer.len() {
349 return None;
350 }
351
352 match buffer[*byte_index + 1] {
354 IAC => {
355 if codec.buffer.len() < codec.max_buffer_length {
356 codec.buffer.push(IAC);
357 codec_buffer_size += 1;
358 }
359
360 *byte_index += 1;
361 }
362 DO => return decode_negotiate(*byte_index, buffer, DO),
363 DONT => return decode_negotiate(*byte_index, buffer, DONT),
364 WILL => return decode_negotiate(*byte_index, buffer, WILL),
365 WONT => return decode_negotiate(*byte_index, buffer, WONT),
366 SB => {
367 if *byte_index + 2 >= buffer.len() {
368 buffer.advance(*byte_index + 2);
369 return None;
370 }
371
372 let start = *byte_index;
373 let opt = buffer[*byte_index + 2];
374
375 *byte_index += 3;
376
377 let mut subvec: Vec<u8> = Vec::new();
378 let mut invalid = false;
379
380 loop {
381 if *byte_index > buffer.len() {
382 buffer.advance(start);
383 return None;
384 }
385
386 match buffer[*byte_index] {
389 IAC => {
390 if *byte_index + 1 > buffer.len() {
391 return None;
392 }
393
394 match buffer[*byte_index + 1] {
398 SE => {
399 return decode_subnegotiation_end(
400 invalid, buffer, subvec, opt,
401 )
402 }
403 IAC => subvec.push(IAC),
404 _ => invalid = true,
405 }
406
407 *byte_index += 1;
408 }
409 _ => subvec.push(buffer[*byte_index]),
410 }
411
412 *byte_index += 1;
413 }
414 }
415 NOP => *byte_index += 1,
416 _ => {}
417 }
418 }
419 b'\n' => {
420 let mut codec_buffer = mem::take(&mut codec.buffer);
421 if codec_buffer.ends_with(&[b'\r']) {
422 codec_buffer.pop();
423 buffer.advance(*byte_index + 1);
424
425 let result = String::from_utf8_lossy(&codec_buffer[..]);
426 return Some(TelnetEvent::Message(result.to_string()));
427 }
428
429 decode_next_byte(codec, &mut codec_buffer_size, buffer[*byte_index]);
430 }
431 #[cfg(not(feature = "unicode"))]
432 c if !codec.message_mode => {
433 let mut codec_buffer = mem::take(&mut codec.buffer);
434 codec_buffer.pop();
435 buffer.advance(*byte_index + 1);
436 return Some(TelnetEvent::Character(c));
437 }
438
439 #[cfg(feature = "unicode")]
440 c if !codec.message_mode => {
441 if !codec.unicode {
445 let mut codec_buffer = mem::take(&mut codec.buffer);
446 codec_buffer.pop();
447 buffer.advance(*byte_index + 1);
448 return Some(TelnetEvent::Character(c));
449 }
450
451 return decode_utf8(*byte_index, buffer, c);
452 }
453 _ => decode_next_byte(codec, &mut codec_buffer_size, buffer[*byte_index]),
454 };
455
456 *byte_index += 1;
457 }
458}
459
460fn encode_negotiate(opt: u8, subopt: TelnetOption, buf: &mut BytesMut) {
461 buf.reserve(3);
462 buf.put_u8(IAC);
463
464 match opt {
465 DO => buf.put_u8(DO),
466 DONT => buf.put_u8(DONT),
467 WILL => buf.put_u8(WILL),
468 WONT => buf.put_u8(WONT),
469 _ => unreachable!(),
470 }
471
472 buf.put_u8(subopt.into());
473}
474
475fn encode_sb(sb: SubnegotiationType, buffer: &mut BytesMut) {
476 match sb {
477 SubnegotiationType::WindowSize(width, height) => {
478 buffer.reserve(9);
479 buffer.extend([IAC, SB, NAWS]);
480 buffer.put_u16(width);
481 buffer.put_u16(height);
482 buffer.extend([IAC, SE]);
483 }
484 SubnegotiationType::CharsetRequest(charsets) => {
485 let charset_lens = charsets.iter().map(|c| c.len()).sum::<usize>();
486 let spaces = charsets.len().saturating_sub(1);
487
488 buffer.reserve(7 + charset_lens + spaces);
489 let sep = b' ';
490 buffer.extend([IAC, SB, CHARSET, CHARSET_REQUEST, sep]);
491
492 for (i, charset) in charsets.iter().enumerate() {
493 buffer.extend(charset);
494 if i < charsets.len() - 1 {
495 buffer.put_u8(sep);
496 }
497 }
498
499 buffer.extend([IAC, SE]);
500 }
501 SubnegotiationType::CharsetAccepted(charset) => {
502 buffer.reserve(6 + charset.len());
503 buffer.extend([IAC, SB, CHARSET, CHARSET_ACCEPTED]);
504 buffer.extend(charset);
505 buffer.extend([IAC, SE]);
506 }
507 SubnegotiationType::CharsetRejected => {
508 buffer.reserve(6);
509 buffer.extend([IAC, SB, CHARSET, CHARSET_REJECTED, IAC, SE]);
510 }
511 SubnegotiationType::CharsetTTableRejected => {
512 buffer.reserve(6);
513 buffer.extend([IAC, SB, CHARSET, CHARSET_TTABLE_REJECTED, IAC, SE]);
514 }
515 SubnegotiationType::Environment(op) => {
516 buffer.extend([IAC, SB, ENVIRON]);
517 encode_env_op(op, buffer);
518 buffer.extend([IAC, SE]);
519 }
520 SubnegotiationType::Unknown(option, bytes) => {
521 let mut bytes_buffer_size = bytes.len() + 5;
522
523 for byte in &bytes {
524 if *byte == IAC {
525 bytes_buffer_size += 1;
526 }
527 }
528
529 buffer.reserve(bytes_buffer_size);
530
531 buffer.extend([IAC, SB, option.into()]);
533
534 for byte in &bytes {
536 if *byte == IAC {
537 buffer.extend([IAC, IAC]);
538 } else {
539 buffer.put_u8(*byte);
540 }
541 }
542
543 buffer.extend([IAC, SE]);
545 }
546 SubnegotiationType::LineMode(mode) => match mode {
547 LineModeOption::Mode(value) => {
548 buffer.reserve(7);
549 buffer.extend([IAC, SB, LINEMODE, MODE, value, IAC, SE]);
550 }
551 LineModeOption::SLC(values) => {
552 buffer.reserve(6 + values.len() * 3);
559 buffer.extend([IAC, SB, LINEMODE, LINEMODE_SLC]);
560
561 for &(dispatch, char) in &values {
562 let (first, second) = dispatch.into();
563 buffer.extend([first, second, char as u8]);
564 }
565
566 buffer.extend([IAC, SE]);
567 }
568 LineModeOption::ForwardMask(ForwardMaskOption::Do(data)) => {
569 buffer.reserve(7 + 16);
571
572 buffer.extend([IAC, SB, LINEMODE, DO, LINEMODE_FORWARD_MASK]);
573
574 let iter = data.into_iter().take(16);
575 let zeros = std::iter::repeat(0).take(16 - iter.len());
576
577 buffer.extend(iter.chain(zeros));
578 buffer.extend([IAC, SE]);
579 }
580 LineModeOption::ForwardMask(option) => {
581 buffer.reserve(7);
582 buffer.extend([IAC, SB, LINEMODE, option.into(), LINEMODE_FORWARD_MASK, IAC, SE]);
583 }
584 LineModeOption::Unknown(option, data) => {
585 buffer.reserve(7 + data.len());
586 buffer.extend([IAC, SB, LINEMODE, option]);
587 buffer.extend(data);
588 buffer.extend([IAC, SE]);
589 }
590 },
591 }
592}
593
594fn encode_raw_message(message: String, buffer: &mut BytesMut) {
595 let bytes = Bytes::from(message);
596 let mut bytes_buffer_size = bytes.len();
597
598 for byte in &bytes {
599 if *byte == IAC {
600 bytes_buffer_size += 1;
601 }
602 }
603
604 buffer.reserve(bytes_buffer_size);
605
606 for byte in &bytes {
607 if *byte == IAC {
608 buffer.extend([IAC, IAC]);
609 }
610 buffer.put_u8(*byte);
611 }
612}
613
614fn encode_message(message: String, buffer: &mut BytesMut) {
615 encode_raw_message(message, buffer);
616
617 if !buffer.ends_with(b"\r\n") {
618 buffer.reserve(2);
619 buffer.extend([b'\r', b'\n']);
620 }
621}
622
623#[cfg(test)]
624mod tests {
625 use super::*;
626
627 fn setup() -> (TelnetCodec, BytesMut) {
628 let codec = TelnetCodec::new(16);
629 let buffer = BytesMut::new();
630 (codec, buffer)
631 }
632
633 mod test_decode {
634 use super::*;
635
636 #[test]
637 fn test_sga_true() {
638 let (mut codec, mut buffer) = setup();
639 codec.sga = true;
640
641 assert!(codec.decode(&mut buffer).unwrap().is_none());
644
645 codec.buffer.extend([b'h', b'i', b'y', b'a', b' ', 0xf0, 0x9f, 0x98, 0x81]);
648 assert_eq!(
649 codec.decode(&mut buffer).unwrap().unwrap(),
650 TelnetEvent::Message("hiya ๐".to_string())
651 );
652 assert!(codec.buffer.is_empty());
653
654 buffer.extend([IAC]);
657 assert!(codec.decode(&mut buffer).unwrap().is_none());
658 assert!(codec.buffer.is_empty());
659 assert_eq!(buffer.as_ref(), &[IAC]);
660 buffer.extend([IAC]); assert_eq!(codec.decode(&mut buffer).unwrap().unwrap(), TelnetEvent::Character(IAC));
662 assert!(codec.buffer.is_empty());
663 assert!(buffer.is_empty());
664
665 buffer.extend([IAC, WILL]);
667 assert!(codec.decode(&mut buffer).unwrap().is_none());
668 assert!(codec.buffer.is_empty());
669 assert_eq!(buffer.as_ref(), &[IAC, WILL]);
670
671 buffer.extend([WILL, IAC]);
673 assert!(codec.decode(&mut buffer).unwrap().is_none());
674 assert!(codec.buffer.is_empty());
675 assert_eq!(buffer.as_ref(), &[IAC, WILL, WILL, IAC]); }
677
678 mod test_sga_false {
679 use super::*;
680
681 #[test]
682 fn test_buffer_starts_with_newline() {
683 let (mut codec, mut buffer) = setup();
684
685 codec.buffer.extend([b'c', b'o', b'o', b'l', b'!', b'\r']);
686 buffer.extend([b'\n', b'y', b'e', b's']);
687
688 assert_eq!(
691 codec.decode(&mut buffer).unwrap().unwrap(),
692 TelnetEvent::Message("cool!".to_string())
693 );
694 assert!(codec.buffer.is_empty());
695 assert_eq!(buffer.as_ref(), &[b'y', b'e', b's']);
696
697 assert_eq!(codec.decode(&mut buffer).unwrap(), None);
701 assert_eq!(&codec.buffer, &[b'y', b'e', b's']);
702 assert_eq!(buffer.as_ref(), &[b'y', b'e', b's']);
703 }
704
705 #[test]
706 fn test_overflow() {
707 let (mut codec, mut buffer) = setup();
708
709 buffer.extend([b'a'; 10]);
710 buffer.extend([b'z'; 10]);
711
712 assert!(codec.decode(&mut buffer).unwrap().is_none());
713
714 assert_eq!(&codec.buffer[..=9], &[b'a'; 10]);
715 assert_eq!(&codec.buffer[10..], &[b'z'; 6]);
716
717 assert_eq!(&buffer[..=9], &[b'a'; 10]);
718 assert_eq!(&buffer[10..], &[b'z'; 10]);
719 }
720
721 mod test_iac {
722 use super::*;
723 use crate::constants::ECHO;
724
725 #[test]
726 fn test_double_iac() {
727 let (mut codec, mut buffer) = setup();
728
729 buffer.extend([IAC, IAC]);
732 assert_eq!(codec.decode(&mut buffer).unwrap(), None);
733 assert_eq!(&codec.buffer, &[IAC]);
734 assert_eq!(buffer.as_ref(), &[IAC, IAC]);
735 }
736
737 #[test]
738 fn test_do() {
739 let (mut codec, mut buffer) = setup();
740
741 buffer.extend([IAC, DO, ECHO]);
742 assert_eq!(
743 codec.decode(&mut buffer).unwrap().unwrap(),
744 TelnetEvent::Do(TelnetOption::Echo)
745 );
746 assert!(codec.buffer.is_empty());
747 assert!(buffer.is_empty());
748 }
749
750 #[test]
751 fn test_dont() {
752 let (mut codec, mut buffer) = setup();
753
754 buffer.extend([IAC, DONT, ECHO]);
755 assert_eq!(
756 codec.decode(&mut buffer).unwrap().unwrap(),
757 TelnetEvent::Dont(TelnetOption::Echo)
758 );
759 assert!(codec.buffer.is_empty());
760 assert!(buffer.is_empty());
761 }
762
763 #[test]
764 fn test_will() {
765 let (mut codec, mut buffer) = setup();
766
767 buffer.extend([IAC, WILL, ECHO]);
768 assert_eq!(
769 codec.decode(&mut buffer).unwrap().unwrap(),
770 TelnetEvent::Will(TelnetOption::Echo)
771 );
772 assert!(codec.buffer.is_empty());
773 assert!(buffer.is_empty());
774 }
775
776 #[test]
777 fn test_wont() {
778 let (mut codec, mut buffer) = setup();
779
780 buffer.extend([IAC, WONT, ECHO]);
781 assert_eq!(
782 codec.decode(&mut buffer).unwrap().unwrap(),
783 TelnetEvent::Wont(TelnetOption::Echo)
784 );
785 assert!(codec.buffer.is_empty());
786 assert!(buffer.is_empty());
787 }
788
789 #[test]
790 fn test_nop() {
791 let (mut codec, mut buffer) = setup();
792
793 buffer.extend([IAC, NOP]);
794 assert_eq!(codec.decode(&mut buffer).unwrap(), None);
795 assert!(codec.buffer.is_empty());
796 assert_eq!(buffer.as_ref(), &[IAC, NOP]);
797 }
798
799 #[test]
800 fn test_sb_naws() {
801 let (mut codec, mut buffer) = setup();
802
803 buffer.extend([IAC, SB, NAWS, 0x00, 0x50, 0x00, 0x50, IAC, SE]);
804 assert_eq!(
805 codec.decode(&mut buffer).unwrap().unwrap(),
806 TelnetEvent::Subnegotiate(SubnegotiationType::WindowSize(80, 80))
807 );
808 assert!(codec.buffer.is_empty());
809 assert!(buffer.is_empty());
810 }
811
812 #[test]
813 fn test_sb_charset_request() {
814 let (mut codec, mut buffer) = setup();
815
816 buffer.extend([IAC, SB, CHARSET, CHARSET_REQUEST, b' ']);
817 buffer.extend("UTF-8".bytes());
818 buffer.put_u8(b' ');
819 buffer.extend("US-ASCII".bytes());
820 buffer.extend([IAC, SE]);
821
822 assert_eq!(
823 codec.decode(&mut buffer).unwrap().unwrap(),
824 TelnetEvent::Subnegotiate(SubnegotiationType::CharsetRequest(vec![
825 Bytes::from("UTF-8"),
826 Bytes::from("US-ASCII")
827 ]))
828 );
829 assert!(codec.buffer.is_empty());
830 assert!(buffer.is_empty());
831 }
832
833 #[test]
834 fn test_sb_charset_accepted() {
835 let (mut codec, mut buffer) = setup();
836
837 buffer.extend([IAC, SB, CHARSET, CHARSET_ACCEPTED]);
838 buffer.extend("UTF-8".bytes());
839 buffer.extend([IAC, SE]);
840
841 assert_eq!(
842 codec.decode(&mut buffer).unwrap().unwrap(),
843 TelnetEvent::Subnegotiate(SubnegotiationType::CharsetAccepted(
844 Bytes::from("UTF-8")
845 ))
846 );
847 assert!(codec.buffer.is_empty());
848 assert!(buffer.is_empty());
849 }
850
851 #[test]
852 fn test_sb_charset_rejected() {
853 let (mut codec, mut buffer) = setup();
854
855 buffer.extend([IAC, SB, CHARSET, CHARSET_REJECTED, IAC, SE]);
856
857 assert_eq!(
858 codec.decode(&mut buffer).unwrap().unwrap(),
859 TelnetEvent::Subnegotiate(SubnegotiationType::CharsetRejected)
860 );
861 assert!(codec.buffer.is_empty());
862 assert!(buffer.is_empty());
863 }
864
865 #[test]
866 fn test_sb_charset_ttable_rejected() {
867 let (mut codec, mut buffer) = setup();
868
869 buffer.extend([IAC, SB, CHARSET, CHARSET_TTABLE_REJECTED, IAC, SE]);
870
871 assert_eq!(
872 codec.decode(&mut buffer).unwrap().unwrap(),
873 TelnetEvent::Subnegotiate(SubnegotiationType::CharsetTTableRejected)
874 );
875 assert!(codec.buffer.is_empty());
876 assert!(buffer.is_empty());
877 }
878 }
879 }
880 }
881
882 mod test_encode {
883 use super::*;
884 use crate::{
885 constants::{ECHO, LINEMODE_EDIT, SLC_ABORT, SLC_BRK, SLC_SYNCH},
886 linemode::{Dispatch, SlcFunction},
887 };
888
889 #[test]
890 fn test_message() {
891 let (mut codec, mut buffer) = setup();
892 codec.encode(TelnetEvent::Message("hiya ๐".to_string()), &mut buffer).unwrap();
893 assert_eq!(buffer.as_ref(), b"hiya \xF0\x9F\x98\x81\r\n");
894
895 let (mut codec, mut buffer) = setup();
896 let msg = "this message is larger than the max buffer length".to_string();
897 assert!(msg.len() > codec.max_buffer_length);
898 codec.encode(TelnetEvent::Message(msg), &mut buffer).unwrap();
899 assert_eq!(buffer.as_ref(), b"this message is larger than the max buffer length\r\n");
900 }
901
902 #[test]
903 #[cfg(feature = "unicode")]
904 fn test_unicode() {
905 let (mut codec, mut buffer) = setup();
906 codec.message_mode = false;
907 codec.unicode = true;
908 codec.sga = false;
909
910 buffer.extend(b"\xC3\xA4");
911
912 let result = codec.decode(&mut buffer);
913
914 assert!(matches!(result, Ok(Some(TelnetEvent::Unicode('รค')))));
915 }
916
917 #[test]
918 fn test_raw_message() {
919 let (mut codec, mut buffer) = setup();
920 codec.encode(TelnetEvent::RawMessage("hiya ๐".to_string()), &mut buffer).unwrap();
921 assert_eq!(buffer.as_ref(), b"hiya \xF0\x9F\x98\x81");
922 }
923
924 #[test]
925 fn test_do() {
926 let (mut codec, mut buffer) = setup();
927 codec.encode(TelnetEvent::Do(TelnetOption::Echo), &mut buffer).unwrap();
928 assert_eq!(buffer.as_ref(), &[IAC, DO, ECHO]);
929 }
930
931 #[test]
932 fn test_dont() {
933 let (mut codec, mut buffer) = setup();
934 codec.encode(TelnetEvent::Dont(TelnetOption::Echo), &mut buffer).unwrap();
935 assert_eq!(buffer.as_ref(), &[IAC, DONT, ECHO]);
936 }
937
938 #[test]
939 fn test_will() {
940 let (mut codec, mut buffer) = setup();
941 codec.encode(TelnetEvent::Will(TelnetOption::Echo), &mut buffer).unwrap();
942 assert_eq!(buffer.as_ref(), &[IAC, WILL, ECHO]);
943 }
944
945 #[test]
946 fn test_wont() {
947 let (mut codec, mut buffer) = setup();
948 codec.encode(TelnetEvent::Wont(TelnetOption::Echo), &mut buffer).unwrap();
949 assert_eq!(buffer.as_ref(), &[IAC, WONT, ECHO]);
950 }
951
952 #[test]
953 fn test_sb_naws() {
954 let (mut codec, mut buffer) = setup();
955 codec
956 .encode(
957 TelnetEvent::Subnegotiate(SubnegotiationType::WindowSize(80, 80)),
958 &mut buffer,
959 )
960 .unwrap();
961 assert_eq!(buffer.as_ref(), &[IAC, SB, NAWS, 0x00, 0x50, 0x00, 0x50, IAC, SE]);
962 }
963
964 #[test]
965 fn test_sb_charset_request() {
966 let (mut codec, mut buffer) = setup();
967 codec
968 .encode(
969 TelnetEvent::Subnegotiate(SubnegotiationType::CharsetRequest(vec![
970 Bytes::from("UTF-8"),
971 Bytes::from("US-ASCII"),
972 ])),
973 &mut buffer,
974 )
975 .unwrap();
976 assert_eq!(&buffer.as_ref()[0..=4], &[IAC, SB, CHARSET, CHARSET_REQUEST, b' ']);
977 assert_eq!(&buffer.as_ref()[5..], b"UTF-8 US-ASCII\xFF\xF0" as &[u8]);
978 }
979
980 #[test]
981 fn test_sb_charset_accepted() {
982 let (mut codec, mut buffer) = setup();
983 codec
984 .encode(
985 TelnetEvent::Subnegotiate(SubnegotiationType::CharsetAccepted(Bytes::from(
986 "UTF-8",
987 ))),
988 &mut buffer,
989 )
990 .unwrap();
991 assert_eq!(&buffer.as_ref()[0..=3], &[IAC, SB, CHARSET, CHARSET_ACCEPTED]);
992 assert_eq!(&buffer.as_ref()[4..], b"UTF-8\xFF\xF0" as &[u8]);
993 }
994
995 #[test]
996 fn test_sb_charset_rejected() {
997 let (mut codec, mut buffer) = setup();
998 codec
999 .encode(TelnetEvent::Subnegotiate(SubnegotiationType::CharsetRejected), &mut buffer)
1000 .unwrap();
1001 assert_eq!(buffer.as_ref(), &[IAC, SB, CHARSET, CHARSET_REJECTED, IAC, SE]);
1002 }
1003
1004 #[test]
1005 fn test_sb_charset_ttable_rejected() {
1006 let (mut codec, mut buffer) = setup();
1007 codec
1008 .encode(
1009 TelnetEvent::Subnegotiate(SubnegotiationType::CharsetTTableRejected),
1010 &mut buffer,
1011 )
1012 .unwrap();
1013 assert_eq!(buffer.as_ref(), &[IAC, SB, CHARSET, CHARSET_TTABLE_REJECTED, IAC, SE]);
1014 }
1015
1016 #[test]
1017 fn test_sb_linemode_mode_encode() {
1018 let (mut codec, mut buffer) = setup();
1019 codec
1020 .encode(
1021 TelnetEvent::Subnegotiate(SubnegotiationType::LineMode(LineModeOption::Mode(
1022 LINEMODE_EDIT,
1023 ))),
1024 &mut buffer,
1025 )
1026 .unwrap();
1027
1028 assert_eq!(buffer.as_ref(), &[IAC, SB, LINEMODE, MODE, LINEMODE_EDIT, IAC, SE]);
1029 }
1030
1031 #[test]
1032 fn test_sb_linemode_mode_decode() {
1033 let (mut codec, mut buffer) = setup();
1034 buffer.extend([IAC, SB, LINEMODE, MODE, LINEMODE_EDIT, IAC, SE]);
1035 let event = codec.decode(&mut buffer).unwrap().unwrap();
1036 match event {
1037 TelnetEvent::Subnegotiate(SubnegotiationType::LineMode(LineModeOption::Mode(
1038 mode,
1039 ))) => {
1040 assert_eq!(mode, LINEMODE_EDIT);
1041 }
1042 _ => panic!("Bad decode!"),
1043 };
1044 }
1045
1046 #[test]
1047 fn test_sb_linemode_slc_encode() {
1048 let (mut codec, mut buffer) = setup();
1049 let triples = [
1050 (Dispatch::from((SLC_ABORT, 0)), '0'),
1051 (Dispatch::from((SLC_SYNCH, 0)), '1'),
1052 (Dispatch::from((SLC_BRK, 0)), '2'),
1053 ];
1054
1055 codec
1056 .encode(
1057 TelnetEvent::Subnegotiate(SubnegotiationType::LineMode(LineModeOption::SLC(
1058 triples.to_vec(),
1059 ))),
1060 &mut buffer,
1061 )
1062 .unwrap();
1063
1064 assert_eq!(
1065 buffer.as_ref(),
1066 &[
1067 IAC,
1068 SB,
1069 LINEMODE,
1070 LINEMODE_SLC,
1071 SLC_ABORT,
1072 0,
1073 b'0',
1074 SLC_SYNCH,
1075 0,
1076 b'1',
1077 SLC_BRK,
1078 0,
1079 b'2',
1080 IAC,
1081 SE
1082 ]
1083 )
1084 }
1085
1086 #[test]
1087 fn test_sb_linemode_unk_decode() {
1088 let (mut codec, mut buffer) = setup();
1089
1090 buffer.extend([IAC, SB, LINEMODE, 123, 1, 2, 3, 4, 5, 6, IAC, SE]);
1091
1092 let event = codec.decode(&mut buffer).unwrap().unwrap();
1093
1094 match event {
1095 TelnetEvent::Subnegotiate(SubnegotiationType::LineMode(
1096 LineModeOption::Unknown(123, data),
1097 )) => {
1098 assert_eq!(data.as_ref(), &[1, 2, 3, 4, 5, 6]);
1099 }
1100 _ => panic!("Bad decode!"),
1101 }
1102 }
1103
1104 #[test]
1105 fn test_sb_linemode_unk_encode() {
1106 let (mut codec, mut buffer) = setup();
1107
1108 codec
1109 .encode(
1110 TelnetEvent::Subnegotiate(SubnegotiationType::LineMode(
1111 LineModeOption::Unknown(123, [1, 2, 3, 4, 5, 6].to_vec().into()),
1112 )),
1113 &mut buffer,
1114 )
1115 .unwrap();
1116
1117 assert_eq!(buffer.as_ref(), &[IAC, SB, LINEMODE, 123, 1, 2, 3, 4, 5, 6, IAC, SE]);
1118 }
1119
1120 #[test]
1121 fn test_sb_linemode_slc_decode() {
1122 let (mut codec, mut buffer) = setup();
1123
1124 buffer.extend([
1125 IAC,
1126 SB,
1127 LINEMODE,
1128 LINEMODE_SLC,
1129 SLC_ABORT,
1130 0,
1131 b'0',
1132 SLC_SYNCH,
1133 0,
1134 b'1',
1135 SLC_BRK,
1136 0,
1137 b'2',
1138 IAC,
1139 SE,
1140 ]);
1141
1142 let event = codec.decode(&mut buffer).unwrap().unwrap();
1143
1144 match event {
1145 TelnetEvent::Subnegotiate(SubnegotiationType::LineMode(LineModeOption::SLC(
1146 triples,
1147 ))) => {
1148 assert_eq!(triples.len(), 3);
1149
1150 const CHARS: [char; 3] = ['0', '1', '2'];
1151 const FUNCS: [u8; 3] = [SLC_ABORT, SLC_SYNCH, SLC_BRK];
1152
1153 for (index, &(dispatch, char)) in triples.iter().enumerate() {
1154 assert_eq!(dispatch.function, SlcFunction::from(FUNCS[index]));
1155 assert_eq!(char, CHARS[index]);
1156 }
1157 }
1158 _ => panic!("Bad decode!"),
1159 }
1160 }
1161
1162 #[test]
1163 fn test_sb_linemode_fmask_decode() {
1164 let (mut codec, mut buffer) = setup();
1165 buffer.extend([
1166 IAC,
1167 SB,
1168 LINEMODE,
1169 DO,
1170 LINEMODE_FORWARD_MASK,
1171 0,
1172 0,
1173 0,
1174 0,
1175 0,
1176 0,
1177 0,
1178 0,
1179 0,
1180 0,
1181 0,
1182 0,
1183 0,
1184 0,
1185 0,
1186 123,
1187 IAC,
1188 SE,
1189 ]);
1190
1191 let event = codec.decode(&mut buffer).unwrap().unwrap();
1192
1193 match event {
1194 TelnetEvent::Subnegotiate(SubnegotiationType::LineMode(
1195 LineModeOption::ForwardMask(ForwardMaskOption::Do(data)),
1196 )) => {
1197 assert_eq!(data.len(), 16);
1198 assert_eq!(data[15], 123)
1199 }
1200 _ => panic!("Bad decode!"),
1201 }
1202 }
1203
1204 #[test]
1205 fn test_sb_linemode_fmask_encode() {
1206 let (mut codec, mut buffer) = setup();
1207 codec
1208 .encode(
1209 TelnetEvent::Subnegotiate(SubnegotiationType::LineMode(
1210 LineModeOption::ForwardMask(ForwardMaskOption::Do(Vec::with_capacity(16))),
1211 )),
1212 &mut buffer,
1213 )
1214 .unwrap();
1215
1216 assert_eq!(
1217 buffer.as_ref(),
1218 &[
1219 IAC,
1220 SB,
1221 LINEMODE,
1222 DO,
1223 LINEMODE_FORWARD_MASK,
1224 0,
1225 0,
1226 0,
1227 0,
1228 0,
1229 0,
1230 0,
1231 0,
1232 0,
1233 0,
1234 0,
1235 0,
1236 0,
1237 0,
1238 0,
1239 0,
1240 IAC,
1241 SE
1242 ]
1243 )
1244 }
1245 }
1246}