diff options
| author | Ruben Beltran del Rio <git@r.bdr.sh> | 2025-08-25 12:36:37 +0200 |
|---|---|---|
| committer | Ruben Beltran del Rio <git@r.bdr.sh> | 2025-08-25 12:36:37 +0200 |
| commit | e5de70f2c1dcac767bd34a6b90ac1797a7acb433 (patch) | |
| tree | 25a8c8f339fb5b496b460391ee06a9effeb64855 /src/telnet.rs | |
| parent | 6b909d95ec07848136a6f337db28318c1cd46c60 (diff) | |
| parent | d7bc21d19e168f3a99f54e5ba4866ed49f1187f1 (diff) | |
Merge branch 'rust'
Diffstat (limited to 'src/telnet.rs')
| -rw-r--r-- | src/telnet.rs | 72 |
1 files changed, 72 insertions, 0 deletions
diff --git a/src/telnet.rs b/src/telnet.rs new file mode 100644 index 0000000..c18fe53 --- /dev/null +++ b/src/telnet.rs @@ -0,0 +1,72 @@ +use std::io::{ErrorKind, Read, Result, Write}; +use std::net::TcpStream; +use std::thread; +use std::time::Duration; + +use crate::renderer::Renderer; +use crate::screen::Screen; + +/// Parses a 16bit buffer by parsing both u8 +fn parse_16bit_buffer(buffer: &[u8]) -> u16 { + let high = buffer.first().unwrap_or(&0); + let low = buffer.get(1).unwrap_or(&0); + (u16::from(*high) << 8) | u16::from(*low) +} + +// Telnet protocol constants +const NAWS_REQUEST: &[u8] = &[0xFF, 0xFD, 0x1F]; // IAC DO NAWS +const NAWS_RESPONSE_PREFIX: &[u8] = &[0xFF, 0xFB, 0x1F, 0xFF, 0xFA, 0x1F]; // IAC WILL NAWS IAC SB NAWS +const ESCAPE_SEQUENCE: &[u8] = &[0xFF, 0xF4, 0xFF, 0xFD, 0x06]; // IAC IP IAC DO TIMING_MARK + +/// Selects a random screen and renderer, waits for the NAWS handshake, and +/// starts the animation. +pub fn handle_client(mut stream: TcpStream, frequency: u64, modulation: u8) -> Result<()> { + let mut buffer = [0; 1024]; + + let screen = Screen::random(); + let renderer = Renderer::random(); + + let mut width: Option<u16> = None; + let mut height: Option<u16> = None; + let mut current_modulation: u8 = 0; + + // Send NAWS request to get terminal size + stream.write_all(NAWS_REQUEST)?; + stream.flush()?; + + loop { + stream.set_read_timeout(Some(Duration::from_millis(10)))?; + match stream.read(&mut buffer) { + Ok(0) => break, // Disconnect + Ok(n) => { + if n >= 10 && buffer[0..6] == *NAWS_RESPONSE_PREFIX { + width = Some(parse_16bit_buffer(&buffer[6..8])); + height = Some(parse_16bit_buffer(&buffer[8..10])); + + // Clear screen once we have dimensions + stream.write_all(b"\x1B[2J")?; // Clear screen + stream.flush()?; + } + + // Check for telnet escape sequence (Ctrl+C) + if n >= 5 && buffer[0..5] == *ESCAPE_SEQUENCE { + break; + } + } + Err(ref e) if e.kind() == ErrorKind::WouldBlock => {} + Err(_) => break, + } + + if let (Some(w), Some(h)) = (width, height) { + let payload = screen.render(current_modulation, w, h, &renderer); + let message = format!("\x1B[1;1H{payload}"); // Reset cursor + payload + stream.write_all(message.as_bytes())?; + stream.flush()?; + + current_modulation = current_modulation.wrapping_add(modulation); + } + + thread::sleep(Duration::from_millis(frequency)); + } + Ok(()) +} |