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 = None; let mut height: Option = 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(()) }