summaryrefslogtreecommitdiffstats
path: root/src/main.rs
blob: 6f60514c45660bb6acaaf4680ac2a01853f89964 (plain)
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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
use std::{
    io::{stdout, Write},
    net::TcpStream,
    sync::{Arc, Mutex},
    time::Duration,
};

use clap::Parser;
use config::Config;
use dns_lookup::{lookup_addr, lookup_host};
use tokio::{
    sync::mpsc,
    task::{block_in_place, JoinSet},
};

mod config;

#[tokio::main]
async fn main() -> Result<(), anyhow::Error> {
    let config = Config::parse();

    let mut join_set = JoinSet::new();
    let (tx, mut rx) = mpsc::channel(100);

    let (mut cache, mut names) = config.excluded()?;

    cache.extend(
        local_ip_address::list_afinet_netifas()
            .into_iter()
            .flatten()
            .map(|(_, i)| i),
    );

    let (included_ip, included_hostname) = config.included()?;

    for ip_addr in included_ip {
        tx.send(ip_addr).await?;
    }

    for hostname in included_hostname {
        if names.insert(hostname.clone()) {
            let mut stdout = stdout().lock();
            writeln!(stdout, "{hostname}")?;
        }

        if config.resolve {
            for ip in lookup_host(&hostname)? {
                cache.insert(ip);
            }
        }
    }

    let cache = Arc::new(Mutex::new(cache));
    let names = Arc::new(Mutex::new(names));

    join_set.spawn(sshr::netlink::neighbours(tx.clone()));

    if let Some(ip_net) = config.scan {
        join_set.spawn(sshr::scanner::scan(tx.clone(), ip_net));
    }

    drop(tx);

    let timeout = Duration::from_millis(config.timeout);

    while let Some(ip_addr) = rx.recv().await {
        let names = names.clone();
        let cache = cache.clone();
        join_set.spawn(async move {
            if !cache.lock()?.insert(ip_addr) {
                return Ok(());
            }

            if let Some(port) = config.port {
                let addr = (ip_addr, port).into();
                if block_in_place(|| TcpStream::connect_timeout(&addr, timeout)).is_err() {
                    return Ok(());
                }
            }

            if let Some(s) = config.resolve.then(|| lookup_addr(&ip_addr).ok()).flatten() {
                if names.lock()?.insert(s.clone()) {
                    let mut stdout = stdout().lock();
                    writeln!(stdout, "{s}")?;
                }
            } else {
                let mut stdout = stdout().lock();
                writeln!(stdout, "{ip_addr}")?;
            };

            Ok(())
        });
    }

    while let Some(res) = join_set.join_next().await {
        res??;
    }

    Ok(())
}