summaryrefslogtreecommitdiff
path: root/src/main.rs
blob: 0ef956a2c589c1f000b4893c215995bfc97f8acd (plain) (blame)
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
73
74
75
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)
}