1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
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(())
}
|