summaryrefslogtreecommitdiff
path: root/src/main.rs
diff options
context:
space:
mode:
author53hornet <atc@53hor.net>2021-10-06 11:41:41 -0400
committer53hornet <atc@53hor.net>2021-10-06 11:41:41 -0400
commit353719bbd622d308df2a51dcdb1b4e51f2da16ca (patch)
tree847eed5a6edbf82bb98ca06494ccc8c7f86aaf42 /src/main.rs
downloadfortune-pit-353719bbd622d308df2a51dcdb1b4e51f2da16ca.tar.xz
fortune-pit-353719bbd622d308df2a51dcdb1b4e51f2da16ca.zip
feat: init impl
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs76
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)
+}