Skip to content

Commit

Permalink
Merge pull request #1 from gongzhengyang/feature-udp
Browse files Browse the repository at this point in the history
Feature udp
  • Loading branch information
gongzhengyang authored May 20, 2023
2 parents 8a03828 + db2b756 commit fbc03b9
Show file tree
Hide file tree
Showing 12 changed files with 260 additions and 75 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

-

# 0.3.0 (10. May, 2023)

- **added:** add `udp` scan argument in binary executor and readme


# 0.2.1 (19. May, 2023)
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "rscan"
version = "0.2.1"
version = "0.3.0"
edition = "2021"
categories = ["asynchronous", "command-line-utilities", "network-programming"]
description = "Fast scan network by sending icmp, tcp, udp packets, inspired by nmap but doesn't depend on nmap"
Expand Down
43 changes: 23 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
A modern port scanner and `icmp` scanner. Fast, effective.

Fast scan network by sending `icmp`, `tcp` packets, inspired by `nmap` but doesn't depend on `nmap`.
Fast scan network by sending `icmp`, `tcp/udp` packets, inspired by `nmap` but doesn't depend on `nmap`.

## Supported Platforms

- Linux
- Android
- FreeBSD
- `Linux`
- `Android`
- `FreeBSD`

## Supported Scan protocols

- `icmp/ping`
- `tcp`
- `udp`

## Usage

Expand All @@ -18,14 +24,6 @@ you can `icmp` a `cidr`,`ipaddress`, separated by commas, set timeout argument

```
$ sudo ./rscan icmp 1.1.1.1/28,1.0.0.0/24 --timeout 10
rscan|icmp|1.0.0.50|
rscan|icmp|1.0.0.52|
rscan|icmp|1.0.0.121|
rscan|icmp|1.1.1.5|
rscan|icmp|1.1.1.6|
rscan|icmp|1.0.0.60|
rscan|icmp|1.0.0.247|
rscan|icmp|1.1.1.10|
rscan|icmp|1.0.0.55|
rscan|icmp|1.0.0.116|
rscan|icmp|1.1.1.12|
Expand All @@ -52,20 +50,25 @@ use `tcp` as argument, add ports options

```
$ sudo ./rscan tcp 1.1.1.1/28 --ports 80,443 --timeout 10
rscan|tcp|1.1.1.11:80|
rscan|tcp|1.1.1.12:443|
rscan|tcp|1.1.1.11:443|
rscan|tcp|1.1.1.10:80|
rscan|tcp|1.1.1.7:443|
rscan|tcp|1.1.1.3:443|
rscan|tcp|1.1.1.6:443|
rscan|tcp|1.1.1.5:80|
rscan|tcp|1.1.1.10:443|
rscan|tcp|1.1.1.3:80|
rscan|tcp|1.1.1.7:80|
...
```

### `udp` Scan

`udp` scan based on `icmp` reply with Port Unreachable for `udp` packets if `udp` port is not open, please make sure timeout is big enough to receive all `icmp` for all `udp` packets, so the `udp` scan cannot guarantee 100% accuracy.

Each `ip` limit sends `udp` packets at least 0.5 seconds apart.

```
$ sudo ./rscan udp 10.30.6.0/24 --ports 151-165 --timeout=50
rscan|udp|10.30.6.165:161|
rscan|udp|10.30.6.200:162|
...
```

## License


Expand Down
6 changes: 5 additions & 1 deletion rscanner/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

-

# 0.3.0 (10. May, 2023)

- **added:** add common trait named `SocketScanner` for `tcp/udp` scan
- **changed:** change `rscanner/execute/tcp` to `impl SocketScanner`
- **added:** add `udp` scanner

# 0.2.1(19. May, 2023)

- **added:** icmp, tcp scan support
- **added:** `icmp`, `tcp` scan support
- **added:** system default limit change
2 changes: 1 addition & 1 deletion rscanner/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "rscanner"
version = "0.2.1"
version = "0.3.0"
edition = "2021"
categories = ["asynchronous", "command-line-utilities", "network-programming"]
description = "Fast scan network by sending icmp, tcp, udp packets, inspired by nmap but doesn't depend on nmap"
Expand Down
48 changes: 48 additions & 0 deletions rscanner/src/execute/common.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
use std::net::{IpAddr, SocketAddr};
use std::time::Duration;

use async_trait::async_trait;

use crate::err::APPError;
use crate::opts::ScanOpts;
use crate::sockets_iter::SocketIterator;

#[async_trait]
pub trait SocketScanner {
async fn socket_success(socket: SocketAddr, timeout: u64);

async fn socket_connect(socket: SocketAddr, timeout: u64) -> anyhow::Result<()>;

async fn pre_scan(_scan_opts: &ScanOpts) -> anyhow::Result<()> {
Ok(())
}

async fn pre_send_socket(_socket: &SocketAddr) -> anyhow::Result<()> {
Ok(())
}

async fn scan(scan_opts: ScanOpts) -> anyhow::Result<()> {
Self::pre_scan(&scan_opts).await?;
let ips = scan_opts
.hosts
.iter()
.map(|x| IpAddr::V4(*x))
.collect::<Vec<IpAddr>>();
let ports = scan_opts.ports.ok_or(APPError::PortIsEmpty)?;
let socket_iter = SocketIterator::new(&ips, &ports);
for socket_addr in socket_iter {
let per_timeout = scan_opts.per_timeout;
Self::pre_send_socket(&socket_addr)
.await
.unwrap_or_else(|e| tracing::error!("pre send socket error with {e:?}"));
tokio::spawn(async move { Self::socket_success(socket_addr, per_timeout).await });
}
tokio::time::sleep(Duration::from_secs(scan_opts.timeout)).await;
Self::after_scan().await?;
Ok(())
}

async fn after_scan() -> anyhow::Result<()> {
Ok(())
}
}
5 changes: 3 additions & 2 deletions rscanner/src/execute/icmp/receive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use pnet::packet::Packet;
use tokio::sync::OnceCell;

use super::common;
use super::receive;

static RECEIVE_PACKETS: OnceCell<Arc<Mutex<HashSet<IpAddr>>>> = OnceCell::const_new();

Expand All @@ -31,8 +32,8 @@ pub async fn receive_packets() -> anyhow::Result<()> {
loop {
tokio::time::sleep(Duration::from_nanos(1)).await;
if let Ok(Some((packet, addr))) = iter.next_with_timeout(Duration::from_secs(1)) {
if is_addr_received(&addr).await {
continue;
if receive::is_addr_received(&addr).await {
return Ok(());
}
if let Some(reply_packet) = EchoReplyPacket::new(packet.packet()) {
if reply_packet.get_icmp_type() == IcmpTypes::EchoReply {
Expand Down
1 change: 0 additions & 1 deletion rscanner/src/execute/icmp/send.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,5 @@ pub async fn icmp_ips_chunks(hosts: Vec<Ipv4Addr>) -> anyhow::Result<()> {
R.fetch_add(1, Ordering::Relaxed);
tracing::debug!("sending packets count {}", R.load(Ordering::Relaxed));
}

Ok(())
}
1 change: 1 addition & 0 deletions rscanner/src/execute/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod common;
pub mod icmp;
pub mod tcp;
pub mod udp;
41 changes: 15 additions & 26 deletions rscanner/src/execute/tcp.rs
Original file line number Diff line number Diff line change
@@ -1,35 +1,24 @@
use std::net::{IpAddr, SocketAddr};
use std::net::SocketAddr;
use std::time::Duration;

use async_trait::async_trait;
use tokio::net::TcpStream;

use crate::err::APPError;
use crate::opts::ScanOpts;
use crate::sockets_iter::SocketIterator;
use super::common::SocketScanner;

pub async fn tcp_success(socket: SocketAddr, timeout: u64) {
tracing::debug!("trying connect socket {socket} with timeout {timeout}");
if tcp_connect(socket, timeout).await.is_ok() {
println!("rscan|tcp|{socket}|");
}
}
pub struct TcpSocketScanner;

pub async fn tcp_connect(socket: SocketAddr, timeout: u64) -> anyhow::Result<TcpStream> {
Ok(tokio::time::timeout(Duration::from_secs(timeout), TcpStream::connect(socket)).await??)
}
#[async_trait]
impl SocketScanner for TcpSocketScanner {
async fn socket_success(socket: SocketAddr, timeout: u64) {
tracing::debug!("trying connect socket {socket} with timeout {timeout}");
if Self::socket_connect(socket, timeout).await.is_ok() {
println!("rscan|tcp|{socket}|");
}
}

pub async fn scan(scan_opts: ScanOpts) -> anyhow::Result<()> {
let ips = scan_opts
.hosts
.iter()
.map(|x| IpAddr::V4(*x))
.collect::<Vec<IpAddr>>();
let ports = scan_opts.ports.ok_or(APPError::PortIsEmpty)?;
let socket_iter = SocketIterator::new(&ips, &ports);
for socket_addr in socket_iter {
let per_timeout = scan_opts.per_timeout;
tokio::spawn(async move { tcp_success(socket_addr, per_timeout).await });
async fn socket_connect(socket: SocketAddr, timeout: u64) -> anyhow::Result<()> {
tokio::time::timeout(Duration::from_secs(timeout), TcpStream::connect(socket)).await??;
Ok(())
}
tokio::time::sleep(Duration::from_secs(scan_opts.timeout)).await;
Ok(())
}
Loading

0 comments on commit fbc03b9

Please sign in to comment.