diff options
author | 53hornet <atc@53hor.net> | 2021-10-06 11:41:41 -0400 |
---|---|---|
committer | 53hornet <atc@53hor.net> | 2021-10-06 11:41:41 -0400 |
commit | 353719bbd622d308df2a51dcdb1b4e51f2da16ca (patch) | |
tree | 847eed5a6edbf82bb98ca06494ccc8c7f86aaf42 /src | |
download | fortune-pit-353719bbd622d308df2a51dcdb1b4e51f2da16ca.tar.xz fortune-pit-353719bbd622d308df2a51dcdb1b4e51f2da16ca.zip |
feat: init impl
Diffstat (limited to 'src')
-rw-r--r-- | src/main.rs | 76 |
1 files changed, 76 insertions, 0 deletions
diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..0ef956a --- /dev/null +++ b/src/main.rs @@ -0,0 +1,76 @@ +use anyhow::{Context, Result}; +use async_std::{ + io, + io::prelude::*, + net::{Ipv4Addr, SocketAddrV4, TcpListener, TcpStream}, + prelude::*, + task, +}; +use std::{env, sync::Arc, time::Duration}; + +static DURATION: Duration = Duration::from_secs(1); + +/// Listens for incoming TCP connections and handles them. +#[async_std::main] +async fn main() -> Result<()> { + let listener = TcpListener::bind(read_addr()?) + .await + .with_context(|| "tarpit: failed to bind TCP listener")?; + + let banner = Arc::new(read_banner().await?); + + let mut incoming = listener.incoming(); + while let Some(stream) = incoming.next().await { + match stream { + Err(e) => { + eprintln!("error\t{}", e); + continue; + } + Ok(s) => { + let banner = banner.clone(); + task::spawn(handler(s, banner)); + } + } + } + + Ok(()) +} + +/// Handles a single TCP connection, continuously writing banner to the TCP stream sloooooowly. +async fn handler(mut stream: TcpStream, banner: Arc<String>) { + let peer_addr = stream + .peer_addr() + .map(|p| p.to_string()) + .unwrap_or_else(|_| String::from("(unknown)")); + + eprintln!("connected\t{}", peer_addr); + + for word in banner.split_inclusive(char::is_whitespace).cycle() { + if stream.write_all(word.as_bytes()).await.is_err() { + eprintln!("disconnected\t{}", peer_addr); + return; + } + task::sleep(DURATION).await; + } +} + +/// Reads address port to bind to from first arguent. +fn read_addr() -> Result<SocketAddrV4> { + let port = env::args() + .nth(1) + .map(|arg| arg.parse()) + .unwrap_or(Ok(22)) + .with_context(|| "tarpit: failed to parse bind port")?; + + Ok(SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), port)) +} + +/// Reads banner to be written to client from STDIN. +async fn read_banner() -> Result<String> { + let mut banner = String::new(); + io::stdin() + .read_to_string(&mut banner) + .await + .with_context(|| "tarpit: failed to read banner")?; + Ok(banner) +} |