summaryrefslogtreecommitdiff
path: root/src/telnet.rs
diff options
context:
space:
mode:
authorRuben Beltran del Rio <git@r.bdr.sh>2025-08-25 12:36:37 +0200
committerRuben Beltran del Rio <git@r.bdr.sh>2025-08-25 12:36:37 +0200
commite5de70f2c1dcac767bd34a6b90ac1797a7acb433 (patch)
tree25a8c8f339fb5b496b460391ee06a9effeb64855 /src/telnet.rs
parent6b909d95ec07848136a6f337db28318c1cd46c60 (diff)
parentd7bc21d19e168f3a99f54e5ba4866ed49f1187f1 (diff)
Merge branch 'rust'
Diffstat (limited to 'src/telnet.rs')
-rw-r--r--src/telnet.rs72
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(())
+}