├── src ├── db │ └── mod.rs ├── tls │ ├── mod.rs │ ├── key.rs │ └── cert.rs ├── packet │ ├── mod.rs │ ├── setting.rs │ ├── frame.rs │ ├── tcp.rs │ ├── icmp.rs │ └── udp.rs ├── scan │ ├── mod.rs │ ├── scanner.rs │ ├── payload.rs │ ├── packet.rs │ ├── blocking.rs │ ├── setting.rs │ ├── result.rs │ ├── async_io.rs │ └── service.rs ├── lib.rs ├── ip │ └── mod.rs ├── config │ └── mod.rs ├── protocol │ └── mod.rs ├── interface │ └── mod.rs ├── host.rs ├── pcap │ └── mod.rs └── dns │ └── mod.rs ├── .github └── workflows │ └── rust.yml ├── .gitignore ├── LICENSE ├── README.md ├── examples ├── hostscan.rs ├── async_hostscan.rs ├── portscan.rs ├── async_portscan.rs ├── ipv6_portscan.rs ├── ipv6_hostscan.rs ├── async_ipv6_portscan.rs ├── async_ipv6_hostscan.rs └── service_detection.rs └── Cargo.toml /src/db/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod tcp_service; 2 | -------------------------------------------------------------------------------- /src/tls/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod cert; 2 | pub mod key; 3 | -------------------------------------------------------------------------------- /src/packet/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod frame; 2 | pub mod icmp; 3 | pub mod setting; 4 | pub mod tcp; 5 | pub mod udp; 6 | -------------------------------------------------------------------------------- /src/scan/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod async_io; 2 | pub mod blocking; 3 | pub mod packet; 4 | pub mod payload; 5 | pub mod result; 6 | pub mod scanner; 7 | pub mod service; 8 | pub mod setting; 9 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod config; 2 | pub(crate) mod db; 3 | pub mod dns; 4 | pub mod host; 5 | pub(crate) mod interface; 6 | pub(crate) mod ip; 7 | pub(crate) mod packet; 8 | pub(crate) mod pcap; 9 | pub mod protocol; 10 | pub mod scan; 11 | pub mod tls; 12 | -------------------------------------------------------------------------------- /src/ip/mod.rs: -------------------------------------------------------------------------------- 1 | use std::net::IpAddr; 2 | 3 | pub fn is_global_addr(ip_addr: &IpAddr) -> bool { 4 | match ip_addr { 5 | IpAddr::V4(ipv4) => nex::net::ip::is_global_ipv4(&ipv4), 6 | IpAddr::V6(ipv6) => nex::net::ip::is_global_ipv6(&ipv6), 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/config/mod.rs: -------------------------------------------------------------------------------- 1 | pub const DEFAULT_LOCAL_TCP_PORT: u16 = 44322; 2 | pub const DEFAULT_LOCAL_UDP_PORT: u16 = 53445; 3 | pub const DEFAULT_HOP_LIMIT: u8 = 64; 4 | pub const DEFAULT_HOSTS_CONCURRENCY: usize = 50; 5 | pub const DEFAULT_PORTS_CONCURRENCY: usize = 100; 6 | pub const PCAP_WAIT_TIME_MILLIS: u64 = 10; 7 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | name: Check 15 | runs-on: ${{ matrix.os }} 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | os: [ubuntu-latest, macOS-latest] 20 | 21 | steps: 22 | - uses: actions/checkout@v3 23 | - name: Build 24 | run: cargo build -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | 13 | # MSVC Windows builds of rustc generate these, which store debugging information 14 | *.pdb 15 | 16 | # macOS 17 | *.DS_Store 18 | -------------------------------------------------------------------------------- /src/tls/key.rs: -------------------------------------------------------------------------------- 1 | use anyhow::anyhow; 2 | use anyhow::Result; 3 | use rustls::pki_types::{PrivateKeyDer, PrivatePkcs8KeyDer}; 4 | use std::{fs, path::Path}; 5 | 6 | /// Load private key from a file 7 | #[allow(dead_code)] 8 | pub(crate) fn load_key(key_path: &Path) -> Result> { 9 | let key = fs::read(key_path)?; 10 | let key = if key_path.extension().map_or(false, |x| x == "der") { 11 | PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from(key)) 12 | } else { 13 | rustls_pemfile::private_key(&mut &*key)?.ok_or_else(|| anyhow!("no keys found"))? 14 | }; 15 | Ok(key) 16 | } 17 | -------------------------------------------------------------------------------- /src/protocol/mod.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone, Debug, PartialEq, Eq)] 2 | pub enum Protocol { 3 | ARP, 4 | NDP, 5 | ICMP, 6 | TCP, 7 | UDP, 8 | } 9 | 10 | impl Protocol { 11 | pub fn from_str(s: &str) -> Option { 12 | match s.to_lowercase().as_str() { 13 | "arp" => Some(Protocol::ARP), 14 | "ndp" => Some(Protocol::NDP), 15 | "icmp" => Some(Protocol::ICMP), 16 | "tcp" => Some(Protocol::TCP), 17 | "udp" => Some(Protocol::UDP), 18 | _ => None, 19 | } 20 | } 21 | pub fn to_str(&self) -> &str { 22 | match self { 23 | Protocol::ARP => "ARP", 24 | Protocol::NDP => "NDP", 25 | Protocol::ICMP => "ICMP", 26 | Protocol::TCP => "TCP", 27 | Protocol::UDP => "UDP", 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/packet/setting.rs: -------------------------------------------------------------------------------- 1 | use netdev::mac::MacAddr; 2 | use std::net::{IpAddr, Ipv4Addr}; 3 | 4 | #[derive(Clone, Debug)] 5 | pub struct PacketBuildSetting { 6 | pub src_mac: MacAddr, 7 | pub dst_mac: MacAddr, 8 | pub src_ip: IpAddr, 9 | pub dst_ip: IpAddr, 10 | pub src_port: u16, 11 | pub dst_port: u16, 12 | pub hop_limit: u8, 13 | #[allow(dead_code)] 14 | pub payload: Vec, 15 | pub ip_packet: bool, 16 | } 17 | 18 | impl PacketBuildSetting { 19 | pub fn new() -> Self { 20 | Self { 21 | src_mac: MacAddr::zero(), 22 | dst_mac: MacAddr::zero(), 23 | src_ip: IpAddr::V4(Ipv4Addr::LOCALHOST), 24 | dst_ip: IpAddr::V4(Ipv4Addr::LOCALHOST), 25 | src_port: 0, 26 | dst_port: 0, 27 | hop_limit: 64, 28 | payload: Vec::new(), 29 | ip_packet: false, 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/interface/mod.rs: -------------------------------------------------------------------------------- 1 | use netdev::interface::Interface; 2 | use std::collections::HashSet; 3 | use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; 4 | 5 | pub fn get_interface_by_index(index: u32) -> Option { 6 | for iface in netdev::interface::get_interfaces() { 7 | if iface.index == index { 8 | return Some(iface); 9 | } 10 | } 11 | return None; 12 | } 13 | 14 | pub fn get_local_ips(if_index: u32) -> HashSet { 15 | let interface = get_interface_by_index(if_index).unwrap(); 16 | let mut ips: HashSet = HashSet::new(); 17 | for ip in interface.ipv4.clone() { 18 | ips.insert(IpAddr::V4(ip.addr())); 19 | } 20 | for ip in interface.ipv6.clone() { 21 | ips.insert(IpAddr::V6(ip.addr())); 22 | } 23 | // localhost IP addresses 24 | ips.insert(IpAddr::V4(Ipv4Addr::LOCALHOST)); 25 | ips.insert(IpAddr::V6(Ipv6Addr::LOCALHOST)); 26 | ips 27 | } 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 shellrow 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [crates-badge]: https://img.shields.io/crates/v/netscan.svg 2 | [crates-url]: https://crates.io/crates/netscan 3 | [license-badge]: https://img.shields.io/crates/l/netscan.svg 4 | [examples-url]: https://github.com/shellrow/netscan/tree/main/examples 5 | 6 | # netscan [![Crates.io][crates-badge]][crates-url] ![License][license-badge] 7 | Cross-platform network scan library 8 | with the aim of being lightweight and fast. 9 | 10 | ## Features 11 | - Port Scan (IPv4, IPv6) 12 | - TCP SYN scan 13 | - TCP CONNECT scan 14 | - Host Scan (IPv4, IPv6) 15 | - ICMP PING scan 16 | - TCP PING scan 17 | - UDP PING scan 18 | 19 | ## Usage 20 | Add `netscan` to your dependencies 21 | ```toml:Cargo.toml 22 | [dependencies] 23 | netscan = "0.29" 24 | ``` 25 | 26 | ## Example 27 | See [Examples][examples-url] 28 | 29 | ## Supported platform 30 | - Linux 31 | - macOS 32 | - Windows 33 | 34 | ## Privileges 35 | This library requires the ability to create raw sockets. Execute with administrator privileges. 36 | 37 | ## Note for Windows Users 38 | If you are using Windows, please consider the following points before building and running: 39 | 40 | - Npcap or WinPcap Installation: 41 | - Ensure that you have [Npcap](https://npcap.com/#download) or WinPcap installed on your system. 42 | - If using Npcap, make sure to install it with the "Install Npcap in WinPcap API-compatible Mode" option. 43 | - Build Dependencies: 44 | - Place the Packet.lib file from the [Npcap SDK](https://npcap.com/#download) or WinPcap Developers pack in a directory named lib at the root of this repository. 45 | - You can use any of the locations listed in the %LIB% or $Env:LIB environment variables. 46 | - For the 64-bit toolchain, the Packet.lib is located in /Lib/x64/Packet.lib. 47 | - For the 32-bit toolchain, the Packet.lib is located in /Lib/Packet.lib. 48 | -------------------------------------------------------------------------------- /examples/hostscan.rs: -------------------------------------------------------------------------------- 1 | use ipnet::Ipv4Net; 2 | use netscan::host::Host; 3 | use netscan::scan::scanner::HostScanner; 4 | use netscan::scan::setting::{HostScanSetting, HostScanType}; 5 | use std::net::{IpAddr, Ipv4Addr}; 6 | use std::thread; 7 | use std::time::Duration; 8 | 9 | fn main() { 10 | let interface = netdev::get_default_interface().unwrap(); 11 | let mut scan_setting: HostScanSetting = HostScanSetting::default() 12 | .set_if_index(interface.index) 13 | .set_scan_type(HostScanType::IcmpPingScan) 14 | .set_timeout(Duration::from_millis(10000)) 15 | .set_wait_time(Duration::from_millis(500)); 16 | let src_ip: Ipv4Addr = interface.ipv4[0].addr(); 17 | let net: Ipv4Net = Ipv4Net::new(src_ip, 24).unwrap(); 18 | let nw_addr = Ipv4Net::new(net.network(), 24).unwrap(); 19 | let hosts: Vec = nw_addr.hosts().collect(); 20 | // Add scan target 21 | for host in hosts { 22 | let dst: Host = Host::new(IpAddr::V4(host), String::new()); 23 | scan_setting.add_target(dst); 24 | } 25 | let host_scanner: HostScanner = HostScanner::new(scan_setting); 26 | let rx = host_scanner.get_progress_receiver(); 27 | // Run scan 28 | let handle = thread::spawn(move || host_scanner.scan()); 29 | // Print progress 30 | while let Ok(_socket_addr) = rx.lock().unwrap().recv() { 31 | //println!("Check: {}", socket_addr); 32 | } 33 | let result = handle.join().unwrap(); 34 | // Print results 35 | println!("Status: {:?}", result.scan_status); 36 | println!("UP Hosts:"); 37 | for host in result.hosts { 38 | println!("{:?}", host); 39 | } 40 | println!("Fingerprints:"); 41 | for fingerprint in result.fingerprints { 42 | println!("{:?}", fingerprint); 43 | } 44 | println!("Scan Time: {:?} (including wait-time)", result.scan_time); 45 | } 46 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "netscan" 3 | version = "0.29.0" 4 | authors = ["shellrow "] 5 | edition = "2021" 6 | description = "Cross-platform network scan library" 7 | repository = "https://github.com/shellrow/netscan" 8 | readme = "README.md" 9 | keywords = ["network","security","scan"] 10 | categories = ["network-programming"] 11 | license = "MIT" 12 | 13 | [lib] 14 | name = "netscan" 15 | 16 | [dependencies] 17 | anyhow = { version = "1" } 18 | netdev = "0.34" 19 | nex = { version = "0.19.1" } 20 | async-io = "2.4" 21 | futures-lite = "2.6" 22 | futures = {version = "0.3", features = ["executor", "thread-pool"]} 23 | rustls = { version = "0.23", default-features = false, features = ["ring", "std"] } 24 | rustls-native-certs = "0.7" 25 | rustls-pemfile = "2.1" 26 | rustls-pki-types = "1.8" 27 | tokio = "1" 28 | tokio-rustls = { version = "0.26", default-features = false, features = ["ring"]} 29 | hickory-resolver = "0.24" 30 | phf = { version = "0.11", features = ["macros"] } 31 | rand = "0.8" 32 | 33 | [dev-dependencies] 34 | ipnet = "2.7" 35 | 36 | [[example]] 37 | name = "portscan" 38 | path = "examples/portscan.rs" 39 | 40 | [[example]] 41 | name = "ipv6_portscan" 42 | path = "examples/ipv6_portscan.rs" 43 | 44 | [[example]] 45 | name = "hostscan" 46 | path = "examples/hostscan.rs" 47 | 48 | [[example]] 49 | name = "ipv6_hostscan" 50 | path = "examples/ipv6_hostscan.rs" 51 | 52 | [[example]] 53 | name = "async_portscan" 54 | path = "examples/async_portscan.rs" 55 | 56 | [[example]] 57 | name = "async_ipv6_portscan" 58 | path = "examples/async_ipv6_portscan.rs" 59 | 60 | [[example]] 61 | name = "async_hostscan" 62 | path = "examples/async_hostscan.rs" 63 | 64 | [[example]] 65 | name = "async_ipv6_hostscan" 66 | path = "examples/async_ipv6_hostscan.rs" 67 | 68 | [[example]] 69 | name = "service_detection" 70 | path = "examples/service_detection.rs" 71 | -------------------------------------------------------------------------------- /examples/async_hostscan.rs: -------------------------------------------------------------------------------- 1 | use ipnet::Ipv4Net; 2 | use netscan::host::Host; 3 | use netscan::scan::scanner::HostScanner; 4 | use netscan::scan::setting::{HostScanSetting, HostScanType}; 5 | use std::net::{IpAddr, Ipv4Addr}; 6 | use std::thread; 7 | use std::time::Duration; 8 | 9 | fn main() { 10 | let interface = netdev::get_default_interface().unwrap(); 11 | let mut scan_setting: HostScanSetting = HostScanSetting::default() 12 | .set_if_index(interface.index) 13 | .set_scan_type(HostScanType::IcmpPingScan) 14 | .set_timeout(Duration::from_millis(10000)) 15 | .set_wait_time(Duration::from_millis(500)) 16 | .set_async_scan(true); 17 | let src_ip: Ipv4Addr = interface.ipv4[0].addr(); 18 | let net: Ipv4Net = Ipv4Net::new(src_ip, 24).unwrap(); 19 | let nw_addr = Ipv4Net::new(net.network(), 24).unwrap(); 20 | let hosts: Vec = nw_addr.hosts().collect(); 21 | // Add scan target 22 | for host in hosts { 23 | let dst: Host = Host::new(IpAddr::V4(host), String::new()); 24 | scan_setting.add_target(dst); 25 | } 26 | let host_scanner: HostScanner = HostScanner::new(scan_setting); 27 | let rx = host_scanner.get_progress_receiver(); 28 | // Run scan 29 | let handle = thread::spawn(move || host_scanner.scan()); 30 | // Print progress 31 | while let Ok(_socket_addr) = rx.lock().unwrap().recv() { 32 | //println!("Check: {}", socket_addr); 33 | } 34 | let result = handle.join().unwrap(); 35 | // Print results 36 | println!("Status: {:?}", result.scan_status); 37 | println!("UP Hosts:"); 38 | for host in result.hosts { 39 | println!("{:?}", host); 40 | } 41 | println!("Fingerprints:"); 42 | for fingerprint in result.fingerprints { 43 | println!("{:?}", fingerprint); 44 | } 45 | println!("Scan Time: {:?} (including wait-time)", result.scan_time); 46 | } 47 | -------------------------------------------------------------------------------- /examples/portscan.rs: -------------------------------------------------------------------------------- 1 | use netscan::host::{Host, PortStatus}; 2 | use netscan::scan::scanner::PortScanner; 3 | use netscan::scan::setting::{PortScanSetting, PortScanType}; 4 | use std::net::IpAddr; 5 | use std::thread; 6 | use std::time::Duration; 7 | 8 | fn main() { 9 | let interface = netdev::get_default_interface().unwrap(); 10 | // Add scan target 11 | let dst_ip: IpAddr = 12 | netscan::dns::lookup_host_name("scanme.nmap.org").expect("Error resolving host"); 13 | let dst: Host = Host::new(dst_ip, String::new()).with_ports(vec![22, 80, 443, 5000, 8080]); 14 | let scan_setting = PortScanSetting::default() 15 | .set_if_index(interface.index) 16 | .set_scan_type(PortScanType::TcpSynScan) 17 | .add_target(dst) 18 | .set_timeout(Duration::from_millis(10000)) 19 | .set_wait_time(Duration::from_millis(500)) 20 | .set_send_rate(Duration::from_millis(0)); 21 | let port_scanner = PortScanner::new(scan_setting); 22 | 23 | let rx = port_scanner.get_progress_receiver(); 24 | // Run scan 25 | let handle = thread::spawn(move || port_scanner.scan()); 26 | // Print progress 27 | while let Ok(_socket_addr) = rx.lock().unwrap().recv() { 28 | //println!("Check: {}", socket_addr); 29 | } 30 | let result = handle.join().unwrap(); 31 | // Print results 32 | println!("Status: {:?}", result.scan_status); 33 | println!("Results:"); 34 | for host_info in result.hosts { 35 | println!("{} {}", host_info.ip_addr, host_info.hostname); 36 | for port_info in host_info.ports { 37 | if port_info.status == PortStatus::Open { 38 | println!("{}: {:?}", port_info.number, port_info.status); 39 | } 40 | } 41 | } 42 | println!("Fingerprints:"); 43 | for fingerprint in result.fingerprints { 44 | println!("{:?}", fingerprint); 45 | } 46 | println!("Scan Time: {:?} (including wait-time)", result.scan_time); 47 | } 48 | -------------------------------------------------------------------------------- /examples/async_portscan.rs: -------------------------------------------------------------------------------- 1 | use netscan::host::{Host, PortStatus}; 2 | use netscan::scan::scanner::PortScanner; 3 | use netscan::scan::setting::{PortScanSetting, PortScanType}; 4 | use std::net::IpAddr; 5 | use std::thread; 6 | use std::time::Duration; 7 | 8 | fn main() { 9 | let interface = netdev::get_default_interface().unwrap(); 10 | // Add scan target 11 | let dst_ip: IpAddr = 12 | netscan::dns::lookup_host_name("scanme.nmap.org").expect("Error resolving host"); 13 | 14 | let dst: Host = Host::new(dst_ip, String::new()).with_ports(vec![22, 80, 443, 5000, 8080]); 15 | let scan_setting = PortScanSetting::default() 16 | .set_if_index(interface.index) 17 | .set_scan_type(PortScanType::TcpSynScan) 18 | .add_target(dst) 19 | .set_timeout(Duration::from_millis(10000)) 20 | .set_wait_time(Duration::from_millis(500)) 21 | .set_send_rate(Duration::from_millis(0)) 22 | .set_async_scan(true); 23 | let port_scanner = PortScanner::new(scan_setting); 24 | 25 | let rx = port_scanner.get_progress_receiver(); 26 | // Run scan 27 | let handle = thread::spawn(move || port_scanner.scan()); 28 | // Print progress 29 | while let Ok(_socket_addr) = rx.lock().unwrap().recv() { 30 | //println!("Check: {}", socket_addr); 31 | } 32 | let result = handle.join().unwrap(); 33 | // Print results 34 | println!("Status: {:?}", result.scan_status); 35 | println!("Results:"); 36 | for host_info in result.hosts { 37 | println!("{} {}", host_info.ip_addr, host_info.hostname); 38 | for port_info in host_info.ports { 39 | if port_info.status == PortStatus::Open { 40 | println!("{}: {:?}", port_info.number, port_info.status); 41 | } 42 | } 43 | } 44 | println!("Fingerprints:"); 45 | for fingerprint in result.fingerprints { 46 | println!("{:?}", fingerprint); 47 | } 48 | println!("Scan Time: {:?} (including wait-time)", result.scan_time); 49 | } 50 | -------------------------------------------------------------------------------- /examples/ipv6_portscan.rs: -------------------------------------------------------------------------------- 1 | use netscan::host::{Host, PortStatus}; 2 | use netscan::scan::scanner::PortScanner; 3 | use netscan::scan::setting::{PortScanSetting, PortScanType}; 4 | use std::net::{IpAddr, Ipv6Addr}; 5 | use std::thread; 6 | use std::time::Duration; 7 | 8 | fn main() { 9 | let interface = netdev::get_default_interface().unwrap(); 10 | // Add scan target 11 | let dst_ip: IpAddr = IpAddr::V6(Ipv6Addr::new(0x2606, 0x4700, 0x4700, 0, 0, 0, 0, 0x1111)); 12 | let dst: Host = Host::new(dst_ip, String::new()).with_ports(vec![22, 80, 443, 5000, 8080]); 13 | //let dst: HostInfo = HostInfo::new_with_ip_addr(dst_ip).with_port_range(1, 1000); 14 | let scan_setting = PortScanSetting::default() 15 | .set_if_index(interface.index) 16 | .set_scan_type(PortScanType::TcpSynScan) 17 | .add_target(dst) 18 | .set_timeout(Duration::from_millis(10000)) 19 | .set_wait_time(Duration::from_millis(500)) 20 | .set_send_rate(Duration::from_millis(0)); 21 | let port_scanner = PortScanner::new(scan_setting); 22 | 23 | let rx = port_scanner.get_progress_receiver(); 24 | // Run scan 25 | let handle = thread::spawn(move || port_scanner.scan()); 26 | // Print progress 27 | while let Ok(_socket_addr) = rx.lock().unwrap().recv() { 28 | //println!("Check: {}", socket_addr); 29 | } 30 | let result = handle.join().unwrap(); 31 | // Print results 32 | println!("Status: {:?}", result.scan_status); 33 | println!("Results:"); 34 | for host_info in result.hosts { 35 | println!("{} {}", host_info.ip_addr, host_info.hostname); 36 | for port_info in host_info.ports { 37 | if port_info.status == PortStatus::Open { 38 | println!("{}: {:?}", port_info.number, port_info.status); 39 | } 40 | } 41 | } 42 | println!("Fingerprints:"); 43 | for fingerprint in result.fingerprints { 44 | println!("{:?}", fingerprint); 45 | } 46 | println!("Scan Time: {:?} (including wait-time)", result.scan_time); 47 | } 48 | -------------------------------------------------------------------------------- /examples/ipv6_hostscan.rs: -------------------------------------------------------------------------------- 1 | use netscan::host::Host; 2 | use netscan::scan::scanner::HostScanner; 3 | use netscan::scan::setting::{HostScanSetting, HostScanType}; 4 | use std::net::{IpAddr, Ipv6Addr}; 5 | use std::thread; 6 | use std::time::Duration; 7 | 8 | fn main() { 9 | let interface = netdev::get_default_interface().unwrap(); 10 | let mut scan_setting: HostScanSetting = HostScanSetting::default() 11 | .set_if_index(interface.index) 12 | .set_scan_type(HostScanType::IcmpPingScan) 13 | .set_timeout(Duration::from_millis(10000)) 14 | .set_wait_time(Duration::from_millis(500)); 15 | let dst_ip: IpAddr = IpAddr::V6(Ipv6Addr::new(0x2606, 0x4700, 0x4700, 0, 0, 0, 0, 0x1111)); 16 | scan_setting.add_target(Host::new(dst_ip, String::new())); 17 | let dst_ip: IpAddr = IpAddr::V6(Ipv6Addr::new(0x2606, 0x4700, 0x4700, 0, 0, 0, 0, 0x1001)); 18 | scan_setting.add_target(Host::new(dst_ip, String::new())); 19 | let dst_ip: IpAddr = IpAddr::V6(Ipv6Addr::new(0x2001, 0x4860, 0x4860, 0, 0, 0, 0, 0x8888)); 20 | scan_setting.add_target(Host::new(dst_ip, String::new())); 21 | let dst_ip: IpAddr = IpAddr::V6(Ipv6Addr::new(0x2001, 0x4860, 0x4860, 0, 0, 0, 0, 0x8844)); 22 | scan_setting.add_target(Host::new(dst_ip, String::new())); 23 | let host_scanner = HostScanner::new(scan_setting); 24 | let rx = host_scanner.get_progress_receiver(); 25 | // Run scan 26 | let handle = thread::spawn(move || host_scanner.scan()); 27 | // Print progress 28 | while let Ok(_socket_addr) = rx.lock().unwrap().recv() { 29 | //println!("Check: {}", socket_addr); 30 | } 31 | let result = handle.join().unwrap(); 32 | // Print results 33 | println!("Status: {:?}", result.scan_status); 34 | println!("UP Hosts:"); 35 | for host in result.hosts { 36 | println!("{:?}", host); 37 | } 38 | println!("Fingerprints:"); 39 | for fingerprint in result.fingerprints { 40 | println!("{:?}", fingerprint); 41 | } 42 | println!("Scan Time: {:?} (including wait-time)", result.scan_time); 43 | } 44 | -------------------------------------------------------------------------------- /examples/async_ipv6_portscan.rs: -------------------------------------------------------------------------------- 1 | use netscan::host::{Host, PortStatus}; 2 | use netscan::scan::scanner::PortScanner; 3 | use netscan::scan::setting::{PortScanSetting, PortScanType}; 4 | use std::net::{IpAddr, Ipv6Addr}; 5 | use std::thread; 6 | use std::time::Duration; 7 | 8 | fn main() { 9 | let interface = netdev::get_default_interface().unwrap(); 10 | // Add scan target 11 | let dst_ip: IpAddr = IpAddr::V6(Ipv6Addr::new(0x2606, 0x4700, 0x4700, 0, 0, 0, 0, 0x1111)); 12 | let dst: Host = Host::new(dst_ip, String::new()).with_ports(vec![22, 80, 443, 5000, 8080]); 13 | //let dst: HostInfo = HostInfo::new_with_ip_addr(dst_ip).with_port_range(1, 1000); 14 | let scan_setting = PortScanSetting::default() 15 | .set_if_index(interface.index) 16 | .set_scan_type(PortScanType::TcpSynScan) 17 | .add_target(dst) 18 | .set_timeout(Duration::from_millis(10000)) 19 | .set_wait_time(Duration::from_millis(500)) 20 | .set_send_rate(Duration::from_millis(0)) 21 | .set_async_scan(true); 22 | let port_scanner = PortScanner::new(scan_setting); 23 | 24 | let rx = port_scanner.get_progress_receiver(); 25 | // Run scan 26 | let handle = thread::spawn(move || port_scanner.scan()); 27 | // Print progress 28 | while let Ok(_socket_addr) = rx.lock().unwrap().recv() { 29 | //println!("Check: {}", socket_addr); 30 | } 31 | let result = handle.join().unwrap(); 32 | // Print results 33 | println!("Status: {:?}", result.scan_status); 34 | println!("Results:"); 35 | for host_info in result.hosts { 36 | println!("{} {}", host_info.ip_addr, host_info.hostname); 37 | for port_info in host_info.ports { 38 | if port_info.status == PortStatus::Open { 39 | println!("{}: {:?}", port_info.number, port_info.status); 40 | } 41 | } 42 | } 43 | println!("Fingerprints:"); 44 | for fingerprint in result.fingerprints { 45 | println!("{:?}", fingerprint); 46 | } 47 | println!("Scan Time: {:?} (including wait-time)", result.scan_time); 48 | } 49 | -------------------------------------------------------------------------------- /examples/async_ipv6_hostscan.rs: -------------------------------------------------------------------------------- 1 | use netscan::host::Host; 2 | use netscan::scan::scanner::HostScanner; 3 | use netscan::scan::setting::{HostScanSetting, HostScanType}; 4 | use std::net::{IpAddr, Ipv6Addr}; 5 | use std::thread; 6 | use std::time::Duration; 7 | 8 | fn main() { 9 | let interface = netdev::get_default_interface().unwrap(); 10 | let mut scan_setting: HostScanSetting = HostScanSetting::default() 11 | .set_if_index(interface.index) 12 | .set_scan_type(HostScanType::IcmpPingScan) 13 | .set_timeout(Duration::from_millis(10000)) 14 | .set_wait_time(Duration::from_millis(500)) 15 | .set_async_scan(true); 16 | let dst_ip: IpAddr = IpAddr::V6(Ipv6Addr::new(0x2606, 0x4700, 0x4700, 0, 0, 0, 0, 0x1111)); 17 | scan_setting.add_target(Host::new(dst_ip, String::new())); 18 | let dst_ip: IpAddr = IpAddr::V6(Ipv6Addr::new(0x2606, 0x4700, 0x4700, 0, 0, 0, 0, 0x1001)); 19 | scan_setting.add_target(Host::new(dst_ip, String::new())); 20 | let dst_ip: IpAddr = IpAddr::V6(Ipv6Addr::new(0x2001, 0x4860, 0x4860, 0, 0, 0, 0, 0x8888)); 21 | scan_setting.add_target(Host::new(dst_ip, String::new())); 22 | let dst_ip: IpAddr = IpAddr::V6(Ipv6Addr::new(0x2001, 0x4860, 0x4860, 0, 0, 0, 0, 0x8844)); 23 | scan_setting.add_target(Host::new(dst_ip, String::new())); 24 | let host_scanner = HostScanner::new(scan_setting); 25 | let rx = host_scanner.get_progress_receiver(); 26 | // Run scan 27 | let handle = thread::spawn(move || host_scanner.scan()); 28 | // Print progress 29 | while let Ok(_socket_addr) = rx.lock().unwrap().recv() { 30 | //println!("Check: {}", socket_addr); 31 | } 32 | let result = handle.join().unwrap(); 33 | // Print results 34 | println!("Status: {:?}", result.scan_status); 35 | println!("UP Hosts:"); 36 | for host in result.hosts { 37 | println!("{:?}", host); 38 | } 39 | println!("Fingerprints:"); 40 | for fingerprint in result.fingerprints { 41 | println!("{:?}", fingerprint); 42 | } 43 | println!("Scan Time: {:?} (including wait-time)", result.scan_time); 44 | } 45 | -------------------------------------------------------------------------------- /src/packet/frame.rs: -------------------------------------------------------------------------------- 1 | use nex::packet::arp::ArpHeader; 2 | use nex::packet::ethernet::EthernetHeader; 3 | use nex::packet::frame::Frame; 4 | use nex::packet::icmp::IcmpHeader; 5 | use nex::packet::icmpv6::Icmpv6Header; 6 | use nex::packet::ipv4::Ipv4Header; 7 | use nex::packet::ipv6::Ipv6Header; 8 | use nex::packet::tcp::TcpHeader; 9 | use nex::packet::udp::UdpHeader; 10 | 11 | /// Packet Frame. Contains all the possible packet types 12 | #[derive(Clone, Debug)] 13 | pub struct PacketFrame { 14 | pub ethernet_header: Option, 15 | pub arp_header: Option, 16 | pub ipv4_header: Option, 17 | pub ipv6_header: Option, 18 | pub icmp_header: Option, 19 | pub icmpv6_header: Option, 20 | pub tcp_header: Option, 21 | pub udp_header: Option, 22 | pub payload: Vec, 23 | } 24 | 25 | impl PacketFrame { 26 | /// Constructs a new PacketFrame 27 | pub fn new() -> PacketFrame { 28 | PacketFrame { 29 | ethernet_header: None, 30 | arp_header: None, 31 | ipv4_header: None, 32 | ipv6_header: None, 33 | icmp_header: None, 34 | icmpv6_header: None, 35 | tcp_header: None, 36 | udp_header: None, 37 | payload: vec![], 38 | } 39 | } 40 | pub fn from_nex_frame(frame: &Frame) -> PacketFrame { 41 | let mut packet_frame = PacketFrame::new(); 42 | if let Some(datalink) = &frame.datalink { 43 | packet_frame.ethernet_header = datalink.ethernet.clone(); 44 | packet_frame.arp_header = datalink.arp.clone(); 45 | } 46 | if let Some(ip) = &frame.ip { 47 | packet_frame.ipv4_header = ip.ipv4.clone(); 48 | packet_frame.ipv6_header = ip.ipv6.clone(); 49 | packet_frame.icmp_header = ip.icmp.clone(); 50 | packet_frame.icmpv6_header = ip.icmpv6.clone(); 51 | } 52 | if let Some(transport) = &frame.transport { 53 | packet_frame.tcp_header = transport.tcp.clone(); 54 | packet_frame.udp_header = transport.udp.clone(); 55 | } 56 | packet_frame.payload = frame.payload.clone(); 57 | packet_frame 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /examples/service_detection.rs: -------------------------------------------------------------------------------- 1 | use netscan::host::{Host, PortStatus}; 2 | use netscan::scan::scanner::{PortScanner, ServiceDetector}; 3 | use netscan::scan::setting::{PortScanSetting, PortScanType, ServiceProbeSetting}; 4 | use std::net::IpAddr; 5 | use std::thread; 6 | use std::time::Duration; 7 | 8 | fn main() { 9 | // Get default interface 10 | let interface = netdev::get_default_interface().unwrap(); 11 | // Add target 12 | let dst_ip: IpAddr = 13 | netscan::dns::lookup_host_name("scanme.nmap.org").expect("Error resolving host"); 14 | let dst: Host = Host::new(dst_ip, String::from("scanme.nmap.org")) 15 | .with_ports(vec![22, 80, 443, 5000, 8080]); 16 | //let dst: Host = Host::new(dst_ip, String::from("scanme.nmap.org")).with_port_range(1, 1000); 17 | let scan_setting = PortScanSetting::default() 18 | .set_if_index(interface.index) 19 | .set_scan_type(PortScanType::TcpSynScan) 20 | .add_target(dst) 21 | .set_timeout(Duration::from_millis(10000)) 22 | .set_wait_time(Duration::from_millis(200)); 23 | //.set_send_rate(Duration::from_millis(1)); 24 | let port_scanner = PortScanner::new(scan_setting); 25 | 26 | let rx = port_scanner.get_progress_receiver(); 27 | // Run scan 28 | let handle = thread::spawn(move || port_scanner.scan()); 29 | // Print progress 30 | while let Ok(_socket_addr) = rx.lock().unwrap().recv() { 31 | //println!("Check: {}", socket_addr); 32 | } 33 | let result = handle.join().unwrap(); 34 | // Print results 35 | println!("Status: {:?}", result.scan_status); 36 | println!("Results:"); 37 | for host_info in result.hosts { 38 | println!("{} {}", host_info.ip_addr, host_info.hostname); 39 | for port_info in &host_info.ports { 40 | if port_info.status == PortStatus::Open { 41 | println!("{}: {:?}", port_info.number, port_info.status); 42 | } 43 | } 44 | let probe_setting: ServiceProbeSetting = ServiceProbeSetting::default( 45 | host_info.ip_addr, 46 | "scanme.nmap.org".to_string(), 47 | host_info.get_open_port_numbers(), 48 | ); 49 | let service_detector = ServiceDetector::new(probe_setting); 50 | let service_rx = service_detector.get_progress_receiver(); 51 | let service_handle = thread::spawn(move || service_detector.run()); 52 | // Print progress 53 | while let Ok(socket_addr) = service_rx.lock().unwrap().recv() { 54 | println!("Checked: {}", socket_addr); 55 | } 56 | let service_result = service_handle.join().unwrap(); 57 | for (port, result) in service_result { 58 | println!("{}: {:?}", port, result); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/tls/cert.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use rustls::client::danger::ServerCertVerifier; 3 | use rustls::pki_types::{CertificateDer, ServerName, UnixTime}; 4 | use std::sync::Arc; 5 | use std::{fs, io, path::Path}; 6 | 7 | /// Get the native certificates from the system. return rustls::RootCertStore 8 | pub(crate) fn get_native_certs() -> io::Result { 9 | let mut root_store = rustls::RootCertStore::empty(); 10 | match rustls_native_certs::load_native_certs() { 11 | Ok(certs) => { 12 | for cert in certs { 13 | match root_store.add(cert) { 14 | Ok(_) => {} 15 | Err(_) => {} 16 | } 17 | } 18 | Ok(root_store) 19 | } 20 | Err(e) => return Err(e), 21 | } 22 | } 23 | 24 | /// Load certificate chain from a file 25 | #[allow(dead_code)] 26 | pub(crate) fn load_certs(cert_path: &Path) -> Result>> { 27 | let cert_chain = fs::read(cert_path)?; 28 | let cert_chain = if cert_path.extension().map_or(false, |x| x == "der") { 29 | vec![CertificateDer::from(cert_chain)] 30 | } else { 31 | rustls_pemfile::certs(&mut &*cert_chain).collect::, _>>()? 32 | }; 33 | Ok(cert_chain) 34 | } 35 | 36 | /// Dummy certificate verifier that treats any certificate as valid. 37 | /// NOTE, such verification is vulnerable to MITM attacks, but convenient for testing. 38 | #[derive(Debug)] 39 | pub struct SkipServerVerification(Arc); 40 | 41 | impl SkipServerVerification { 42 | pub fn new() -> Arc { 43 | Arc::new(Self(Arc::new(rustls::crypto::ring::default_provider()))) 44 | } 45 | } 46 | 47 | impl ServerCertVerifier for SkipServerVerification { 48 | fn verify_server_cert( 49 | &self, 50 | _end_entity: &CertificateDer<'_>, 51 | _intermediates: &[CertificateDer<'_>], 52 | _server_name: &ServerName<'_>, 53 | _ocsp: &[u8], 54 | _now: UnixTime, 55 | ) -> Result { 56 | Ok(rustls::client::danger::ServerCertVerified::assertion()) 57 | } 58 | 59 | fn verify_tls12_signature( 60 | &self, 61 | message: &[u8], 62 | cert: &CertificateDer<'_>, 63 | dss: &rustls::DigitallySignedStruct, 64 | ) -> Result { 65 | rustls::crypto::verify_tls12_signature( 66 | message, 67 | cert, 68 | dss, 69 | &self.0.signature_verification_algorithms, 70 | ) 71 | } 72 | 73 | fn verify_tls13_signature( 74 | &self, 75 | message: &[u8], 76 | cert: &CertificateDer<'_>, 77 | dss: &rustls::DigitallySignedStruct, 78 | ) -> Result { 79 | rustls::crypto::verify_tls13_signature( 80 | message, 81 | cert, 82 | dss, 83 | &self.0.signature_verification_algorithms, 84 | ) 85 | } 86 | 87 | fn supported_verify_schemes(&self) -> Vec { 88 | self.0.signature_verification_algorithms.supported_schemes() 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/packet/tcp.rs: -------------------------------------------------------------------------------- 1 | use crate::packet::setting::PacketBuildSetting; 2 | use nex::packet::ethernet::EtherType; 3 | use nex::packet::ip::IpNextLevelProtocol; 4 | use nex::packet::tcp::{TcpFlags, TcpOption}; 5 | use nex::util::packet_builder::{ 6 | builder::PacketBuilder, ethernet::EthernetPacketBuilder, ipv4::Ipv4PacketBuilder, 7 | ipv6::Ipv6PacketBuilder, tcp::TcpPacketBuilder, 8 | }; 9 | use std::net::{IpAddr, SocketAddr}; 10 | 11 | /// Build TCP SYN packet with default options 12 | pub fn build_tcp_syn_packet(setting: PacketBuildSetting) -> Vec { 13 | let mut packet_builder = PacketBuilder::new(); 14 | let ethernet_packet_builder = EthernetPacketBuilder { 15 | src_mac: setting.src_mac, 16 | dst_mac: setting.dst_mac, 17 | ether_type: match setting.dst_ip { 18 | IpAddr::V4(_) => EtherType::Ipv4, 19 | IpAddr::V6(_) => EtherType::Ipv6, 20 | }, 21 | }; 22 | packet_builder.set_ethernet(ethernet_packet_builder); 23 | match setting.src_ip { 24 | IpAddr::V4(src_ipv4) => match setting.dst_ip { 25 | IpAddr::V4(dst_ipv4) => { 26 | let mut ipv4_packet_builder = 27 | Ipv4PacketBuilder::new(src_ipv4, dst_ipv4, IpNextLevelProtocol::Tcp); 28 | ipv4_packet_builder.total_length = Some(64); 29 | ipv4_packet_builder.ttl = Some(setting.hop_limit); 30 | packet_builder.set_ipv4(ipv4_packet_builder); 31 | } 32 | IpAddr::V6(_) => {} 33 | }, 34 | IpAddr::V6(src_ipv6) => match setting.dst_ip { 35 | IpAddr::V4(_) => {} 36 | IpAddr::V6(dst_ipv6) => { 37 | let mut ipv6_packet_builder = 38 | Ipv6PacketBuilder::new(src_ipv6, dst_ipv6, IpNextLevelProtocol::Tcp); 39 | ipv6_packet_builder.payload_length = Some(44); 40 | ipv6_packet_builder.hop_limit = Some(setting.hop_limit); 41 | packet_builder.set_ipv6(ipv6_packet_builder); 42 | } 43 | }, 44 | } 45 | let mut tcp_packet_builder = TcpPacketBuilder::new( 46 | SocketAddr::new(setting.src_ip, setting.src_port), 47 | SocketAddr::new(setting.dst_ip, setting.dst_port), 48 | ); 49 | tcp_packet_builder.flags = TcpFlags::SYN; 50 | tcp_packet_builder.window = 65535; 51 | tcp_packet_builder.options = vec![ 52 | TcpOption::mss(1460), 53 | TcpOption::nop(), 54 | TcpOption::wscale(6), 55 | TcpOption::nop(), 56 | TcpOption::nop(), 57 | TcpOption::timestamp(u32::MAX, u32::MIN), 58 | TcpOption::sack_perm(), 59 | ]; 60 | packet_builder.set_tcp(tcp_packet_builder); 61 | 62 | if setting.ip_packet { 63 | packet_builder.ip_packet() 64 | } else { 65 | packet_builder.packet() 66 | } 67 | } 68 | 69 | pub fn build_ip_next_tcp_syn_packet(setting: PacketBuildSetting) -> Vec { 70 | let mut tcp_packet_builder = TcpPacketBuilder::new( 71 | SocketAddr::new(setting.src_ip, setting.src_port), 72 | SocketAddr::new(setting.dst_ip, setting.dst_port), 73 | ); 74 | tcp_packet_builder.flags = TcpFlags::SYN; 75 | tcp_packet_builder.window = 65535; 76 | tcp_packet_builder.options = vec![ 77 | TcpOption::mss(1460), 78 | TcpOption::nop(), 79 | TcpOption::wscale(6), 80 | TcpOption::nop(), 81 | TcpOption::nop(), 82 | TcpOption::timestamp(u32::MAX, u32::MIN), 83 | TcpOption::sack_perm(), 84 | ]; 85 | tcp_packet_builder.build() 86 | } 87 | -------------------------------------------------------------------------------- /src/packet/icmp.rs: -------------------------------------------------------------------------------- 1 | use nex::packet::ethernet::EtherType; 2 | use nex::packet::icmp::IcmpType; 3 | use nex::packet::icmpv6::Icmpv6Type; 4 | use nex::packet::ip::IpNextLevelProtocol; 5 | use nex::util::packet_builder::builder::PacketBuilder; 6 | use nex::util::packet_builder::ethernet::EthernetPacketBuilder; 7 | use nex::util::packet_builder::icmp::IcmpPacketBuilder; 8 | use nex::util::packet_builder::icmpv6::Icmpv6PacketBuilder; 9 | use nex::util::packet_builder::ipv4::Ipv4PacketBuilder; 10 | use nex::util::packet_builder::ipv6::Ipv6PacketBuilder; 11 | use std::net::IpAddr; 12 | 13 | use crate::packet::setting::PacketBuildSetting; 14 | 15 | /// Build ICMP packet. Supports both ICMPv4 and ICMPv6 16 | pub fn build_icmp_packet(setting: PacketBuildSetting) -> Vec { 17 | let mut packet_builder = PacketBuilder::new(); 18 | 19 | // Ethernet Header 20 | let ethernet_packet_builder = EthernetPacketBuilder { 21 | src_mac: setting.src_mac, 22 | dst_mac: setting.dst_mac, 23 | ether_type: match setting.dst_ip { 24 | IpAddr::V4(_) => EtherType::Ipv4, 25 | IpAddr::V6(_) => EtherType::Ipv6, 26 | }, 27 | }; 28 | packet_builder.set_ethernet(ethernet_packet_builder); 29 | 30 | // IP Header 31 | match setting.dst_ip { 32 | IpAddr::V4(dst_ipv4) => match setting.src_ip { 33 | IpAddr::V4(src_ipv4) => { 34 | let mut ipv4_packet_builder = 35 | Ipv4PacketBuilder::new(src_ipv4, dst_ipv4, IpNextLevelProtocol::Icmp); 36 | ipv4_packet_builder.ttl = Some(setting.hop_limit); 37 | packet_builder.set_ipv4(ipv4_packet_builder); 38 | } 39 | IpAddr::V6(_) => {} 40 | }, 41 | IpAddr::V6(dst_ipv6) => match setting.src_ip { 42 | IpAddr::V4(_) => {} 43 | IpAddr::V6(src_ipv4) => { 44 | let mut ipv6_packet_builder = 45 | Ipv6PacketBuilder::new(src_ipv4, dst_ipv6, IpNextLevelProtocol::Icmpv6); 46 | ipv6_packet_builder.hop_limit = Some(setting.hop_limit); 47 | packet_builder.set_ipv6(ipv6_packet_builder); 48 | } 49 | }, 50 | } 51 | // ICMP Header 52 | match setting.dst_ip { 53 | IpAddr::V4(dst_ipv4) => match setting.src_ip { 54 | IpAddr::V4(src_ipv4) => { 55 | let mut icmp_packet_builder = IcmpPacketBuilder::new(src_ipv4, dst_ipv4); 56 | icmp_packet_builder.icmp_type = IcmpType::EchoRequest; 57 | packet_builder.set_icmp(icmp_packet_builder); 58 | } 59 | IpAddr::V6(_) => {} 60 | }, 61 | IpAddr::V6(dst_ipv6) => match setting.src_ip { 62 | IpAddr::V4(_) => {} 63 | IpAddr::V6(src_ipv6) => { 64 | let mut icmpv6_packet_builder = Icmpv6PacketBuilder::new(src_ipv6, dst_ipv6); 65 | icmpv6_packet_builder.icmpv6_type = Icmpv6Type::EchoRequest; 66 | packet_builder.set_icmpv6(icmpv6_packet_builder); 67 | } 68 | }, 69 | } 70 | if setting.ip_packet { 71 | packet_builder.ip_packet() 72 | } else { 73 | packet_builder.packet() 74 | } 75 | } 76 | 77 | pub fn build_ip_next_icmp_packet(setting: PacketBuildSetting) -> Vec { 78 | // ICMP Header 79 | match setting.dst_ip { 80 | IpAddr::V4(dst_ipv4) => match setting.src_ip { 81 | IpAddr::V4(src_ipv4) => { 82 | let mut icmp_packet_builder = IcmpPacketBuilder::new(src_ipv4, dst_ipv4); 83 | icmp_packet_builder.icmp_type = IcmpType::EchoRequest; 84 | icmp_packet_builder.build() 85 | } 86 | IpAddr::V6(_) => Vec::new(), 87 | }, 88 | IpAddr::V6(dst_ipv6) => match setting.src_ip { 89 | IpAddr::V4(_) => Vec::new(), 90 | IpAddr::V6(src_ipv6) => { 91 | let mut icmpv6_packet_builder = Icmpv6PacketBuilder::new(src_ipv6, dst_ipv6); 92 | icmpv6_packet_builder.icmpv6_type = Icmpv6Type::EchoRequest; 93 | icmpv6_packet_builder.build() 94 | } 95 | }, 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/packet/udp.rs: -------------------------------------------------------------------------------- 1 | use crate::packet::setting::PacketBuildSetting; 2 | use nex::packet::ethernet::EtherType; 3 | use nex::packet::ip::IpNextLevelProtocol; 4 | use nex::util::packet_builder::{ 5 | builder::PacketBuilder, ethernet::EthernetPacketBuilder, ipv4::Ipv4PacketBuilder, 6 | ipv6::Ipv6PacketBuilder, udp::UdpPacketBuilder, 7 | }; 8 | use std::net::{IpAddr, SocketAddr}; 9 | 10 | /// Build UDP packet 11 | pub fn build_udp_packet(setting: PacketBuildSetting) -> Vec { 12 | let mut packet_builder = PacketBuilder::new(); 13 | 14 | // Ethernet Header 15 | let ethernet_packet_builder = EthernetPacketBuilder { 16 | src_mac: setting.src_mac, 17 | dst_mac: setting.dst_mac, 18 | ether_type: match setting.dst_ip { 19 | IpAddr::V4(_) => EtherType::Ipv4, 20 | IpAddr::V6(_) => EtherType::Ipv6, 21 | }, 22 | }; 23 | packet_builder.set_ethernet(ethernet_packet_builder); 24 | 25 | // IP Header 26 | match setting.dst_ip { 27 | IpAddr::V4(dst_ipv4) => match setting.src_ip { 28 | IpAddr::V4(src_ipv4) => { 29 | let mut ipv4_packet_builder = 30 | Ipv4PacketBuilder::new(src_ipv4, dst_ipv4, IpNextLevelProtocol::Udp); 31 | ipv4_packet_builder.ttl = Some(setting.hop_limit); 32 | packet_builder.set_ipv4(ipv4_packet_builder); 33 | } 34 | IpAddr::V6(_) => {} 35 | }, 36 | IpAddr::V6(dst_ipv6) => match setting.src_ip { 37 | IpAddr::V4(_) => {} 38 | IpAddr::V6(src_ipv4) => { 39 | let mut ipv6_packet_builder = 40 | Ipv6PacketBuilder::new(src_ipv4, dst_ipv6, IpNextLevelProtocol::Udp); 41 | ipv6_packet_builder.hop_limit = Some(setting.hop_limit); 42 | packet_builder.set_ipv6(ipv6_packet_builder); 43 | } 44 | }, 45 | } 46 | // UDP Header 47 | match setting.dst_ip { 48 | IpAddr::V4(dst_ipv4) => match setting.src_ip { 49 | IpAddr::V4(src_ipv4) => { 50 | let udp_packet_builder = UdpPacketBuilder::new( 51 | SocketAddr::new(IpAddr::V4(src_ipv4), setting.src_port), 52 | SocketAddr::new(IpAddr::V4(dst_ipv4), setting.dst_port), 53 | ); 54 | packet_builder.set_udp(udp_packet_builder); 55 | } 56 | IpAddr::V6(_) => {} 57 | }, 58 | IpAddr::V6(dst_ipv6) => match setting.src_ip { 59 | IpAddr::V4(_) => {} 60 | IpAddr::V6(src_ipv6) => { 61 | let udp_packet_builder = UdpPacketBuilder::new( 62 | SocketAddr::new(IpAddr::V6(src_ipv6), setting.src_port), 63 | SocketAddr::new(IpAddr::V6(dst_ipv6), setting.dst_port), 64 | ); 65 | packet_builder.set_udp(udp_packet_builder); 66 | } 67 | }, 68 | } 69 | if setting.ip_packet { 70 | packet_builder.ip_packet() 71 | } else { 72 | packet_builder.packet() 73 | } 74 | } 75 | 76 | pub fn build_ip_next_udp_packet(setting: PacketBuildSetting) -> Vec { 77 | // UDP Header 78 | match setting.dst_ip { 79 | IpAddr::V4(dst_ipv4) => match setting.src_ip { 80 | IpAddr::V4(src_ipv4) => { 81 | let udp_packet_builder = UdpPacketBuilder::new( 82 | SocketAddr::new(IpAddr::V4(src_ipv4), setting.src_port), 83 | SocketAddr::new(IpAddr::V4(dst_ipv4), setting.dst_port), 84 | ); 85 | udp_packet_builder.build() 86 | } 87 | IpAddr::V6(_) => Vec::new(), 88 | }, 89 | IpAddr::V6(dst_ipv6) => match setting.src_ip { 90 | IpAddr::V4(_) => Vec::new(), 91 | IpAddr::V6(src_ipv6) => { 92 | let udp_packet_builder = UdpPacketBuilder::new( 93 | SocketAddr::new(IpAddr::V6(src_ipv6), setting.src_port), 94 | SocketAddr::new(IpAddr::V6(dst_ipv6), setting.dst_port), 95 | ); 96 | udp_packet_builder.build() 97 | } 98 | }, 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/scan/scanner.rs: -------------------------------------------------------------------------------- 1 | use crate::host::Host; 2 | use crate::scan::setting::{HostScanSetting, PortScanSetting}; 3 | use std::collections::HashMap; 4 | use std::net::SocketAddr; 5 | use std::sync::mpsc::{channel, Receiver, Sender}; 6 | use std::sync::{Arc, Mutex}; 7 | 8 | use super::async_io; 9 | use super::blocking; 10 | use super::result::{ScanResult, ServiceProbeResult}; 11 | use super::setting::ServiceProbeSetting; 12 | 13 | /// Host Scanner 14 | #[derive(Clone, Debug)] 15 | pub struct HostScanner { 16 | /// Scan Setting 17 | pub scan_setting: HostScanSetting, 18 | /// Sender for progress messaging 19 | pub tx: Arc>>, 20 | /// Receiver for progress messaging 21 | pub rx: Arc>>, 22 | } 23 | 24 | impl HostScanner { 25 | /// Create new HostScanner 26 | pub fn new(scan_setting: HostScanSetting) -> Self { 27 | let (tx, rx) = channel(); 28 | Self { 29 | scan_setting, 30 | tx: Arc::new(Mutex::new(tx)), 31 | rx: Arc::new(Mutex::new(rx)), 32 | } 33 | } 34 | /// Get progress receiver 35 | pub fn get_progress_receiver(&self) -> Arc>> { 36 | self.rx.clone() 37 | } 38 | // Scan hosts 39 | pub fn scan(&self) -> ScanResult { 40 | if self.scan_setting.async_scan { 41 | let rt = tokio::runtime::Runtime::new().unwrap(); 42 | rt.block_on(async_io::scan_hosts(self.scan_setting.clone(), &self.tx)) 43 | } else { 44 | blocking::scan_hosts(self.scan_setting.clone(), &self.tx) 45 | } 46 | } 47 | } 48 | 49 | /// Port Scanner 50 | #[derive(Clone, Debug)] 51 | pub struct PortScanner { 52 | /// Scan Setting 53 | pub scan_setting: PortScanSetting, 54 | /// Sender for progress messaging 55 | pub tx: Arc>>, 56 | /// Receiver for progress messaging 57 | pub rx: Arc>>, 58 | } 59 | 60 | impl PortScanner { 61 | /// Create new PortScanner 62 | pub fn new(scan_setting: PortScanSetting) -> Self { 63 | let (tx, rx) = channel(); 64 | Self { 65 | scan_setting, 66 | tx: Arc::new(Mutex::new(tx)), 67 | rx: Arc::new(Mutex::new(rx)), 68 | } 69 | } 70 | /// Get progress receiver 71 | pub fn get_progress_receiver(&self) -> Arc>> { 72 | self.rx.clone() 73 | } 74 | /// Scan ports 75 | pub fn scan(&self) -> ScanResult { 76 | match self.scan_setting.scan_type { 77 | crate::scan::setting::PortScanType::TcpSynScan => { 78 | if self.scan_setting.async_scan { 79 | let rt = tokio::runtime::Runtime::new().unwrap(); 80 | rt.block_on(async_io::scan_ports(self.scan_setting.clone(), &self.tx)) 81 | } else { 82 | blocking::scan_ports(self.scan_setting.clone(), &self.tx) 83 | } 84 | } 85 | crate::scan::setting::PortScanType::TcpConnectScan => { 86 | async_io::run_connect_scan(self.scan_setting.clone(), &self.tx) 87 | } 88 | } 89 | } 90 | } 91 | 92 | /// Struct for service detection 93 | #[derive(Clone, Debug)] 94 | pub struct ServiceDetector { 95 | /// Probe setting for service detection 96 | pub setting: ServiceProbeSetting, 97 | /// Sender for progress messaging 98 | pub tx: Arc>>, 99 | /// Receiver for progress messaging 100 | pub rx: Arc>>, 101 | } 102 | 103 | impl ServiceDetector { 104 | /// Create new ServiceDetector 105 | pub fn new(setting: ServiceProbeSetting) -> Self { 106 | let (tx, rx) = channel(); 107 | Self { 108 | setting, 109 | tx: Arc::new(Mutex::new(tx)), 110 | rx: Arc::new(Mutex::new(rx)), 111 | } 112 | } 113 | /// Get progress receiver 114 | pub fn get_progress_receiver(&self) -> Arc>> { 115 | self.rx.clone() 116 | } 117 | /// Run service detection 118 | pub fn run(&self) -> HashMap { 119 | let rt = tokio::runtime::Runtime::new().unwrap(); 120 | rt.block_on(super::service::run_service_probe(&self.setting, &self.tx)) 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/host.rs: -------------------------------------------------------------------------------- 1 | use crate::dns; 2 | use netdev::mac::MacAddr; 3 | use std::net::IpAddr; 4 | 5 | /// Status of the scanned port 6 | #[derive(Clone, Copy, Debug, PartialEq)] 7 | pub enum PortStatus { 8 | Open, 9 | Closed, 10 | Filtered, 11 | Unknown, 12 | } 13 | 14 | impl PortStatus { 15 | pub fn id(&self) -> String { 16 | match *self { 17 | PortStatus::Open => String::from("open"), 18 | PortStatus::Closed => String::from("closed"), 19 | PortStatus::Filtered => String::from("filtered"), 20 | PortStatus::Unknown => String::from("unknown"), 21 | } 22 | } 23 | pub fn name(&self) -> String { 24 | match *self { 25 | PortStatus::Open => String::from("Open"), 26 | PortStatus::Closed => String::from("Closed"), 27 | PortStatus::Filtered => String::from("Filtered"), 28 | PortStatus::Unknown => String::from("Unknown"), 29 | } 30 | } 31 | } 32 | 33 | /// Port Information 34 | #[derive(Clone, Debug, PartialEq)] 35 | pub struct Port { 36 | /// Port number 37 | pub number: u16, 38 | /// Port status 39 | pub status: PortStatus, 40 | /// Service name 41 | pub service_name: String, 42 | /// Service version 43 | pub service_version: String, 44 | } 45 | 46 | impl Port { 47 | pub fn new(number: u16) -> Self { 48 | Self { 49 | number: number, 50 | status: PortStatus::Unknown, 51 | service_name: String::new(), 52 | service_version: String::new(), 53 | } 54 | } 55 | } 56 | 57 | /// Host Information 58 | #[derive(Clone, Debug, PartialEq)] 59 | pub struct Host { 60 | /// IP address of the host 61 | pub ip_addr: IpAddr, 62 | /// Host name 63 | pub hostname: String, 64 | /// List of ports 65 | pub ports: Vec, 66 | /// MAC address of the host 67 | pub mac_addr: MacAddr, 68 | /// TTL 69 | pub ttl: u8, 70 | } 71 | 72 | impl Host { 73 | pub fn new(ip_addr: IpAddr, hostname: String) -> Self { 74 | Self { 75 | ip_addr: ip_addr, 76 | hostname: hostname, 77 | ports: Vec::new(), 78 | mac_addr: MacAddr::zero(), 79 | ttl: 0, 80 | } 81 | } 82 | pub fn with_port_range(mut self, start: u16, end: u16) -> Self { 83 | for port in start..end { 84 | self.ports.push(Port::new(port)); 85 | } 86 | self 87 | } 88 | pub fn with_ports(mut self, ports: Vec) -> Self { 89 | for port in ports { 90 | self.ports.push(Port::new(port)); 91 | } 92 | self 93 | } 94 | pub fn get_ports(&self) -> Vec { 95 | self.ports.iter().map(|port| port.number).collect() 96 | } 97 | pub fn get_open_port_numbers(&self) -> Vec { 98 | self.ports 99 | .iter() 100 | .filter(|port| port.status == PortStatus::Open) 101 | .map(|port| port.number) 102 | .collect() 103 | } 104 | pub fn get_open_ports(&self) -> Vec { 105 | self.ports 106 | .iter() 107 | .filter(|port| port.status == PortStatus::Open) 108 | .map(|port| port.clone()) 109 | .collect() 110 | } 111 | } 112 | 113 | /// Node type 114 | #[derive(Clone, Debug, PartialEq)] 115 | pub enum NodeType { 116 | DefaultGateway, 117 | Relay, 118 | Destination, 119 | } 120 | 121 | impl NodeType { 122 | pub fn id(&self) -> String { 123 | match *self { 124 | NodeType::DefaultGateway => String::from("default_gateway"), 125 | NodeType::Relay => String::from("relay"), 126 | NodeType::Destination => String::from("destination"), 127 | } 128 | } 129 | pub fn name(&self) -> String { 130 | match *self { 131 | NodeType::DefaultGateway => String::from("DefaultGateway"), 132 | NodeType::Relay => String::from("Relay"), 133 | NodeType::Destination => String::from("Destination"), 134 | } 135 | } 136 | } 137 | 138 | // Check if the target is an IP address 139 | pub fn is_valid_ip_addr(target: &str) -> bool { 140 | match target.parse::() { 141 | Ok(_) => true, 142 | Err(_) => false, 143 | } 144 | } 145 | 146 | // Check if the target is a valid hostname 147 | pub fn is_valid_hostname(target: &str) -> bool { 148 | dns::lookup_host_name(target).is_some() 149 | } 150 | 151 | // Check if the target is valid 152 | pub fn is_valid_target(target: &str) -> bool { 153 | is_valid_ip_addr(target) || is_valid_hostname(target) 154 | } 155 | -------------------------------------------------------------------------------- /src/scan/payload.rs: -------------------------------------------------------------------------------- 1 | /// Payloads for service detection 2 | #[derive(Clone, Debug, PartialEq, Eq)] 3 | pub enum PayloadType { 4 | /// No payload. Just open TCP connection and read response. 5 | Null, 6 | /// HTTP request 7 | Http, 8 | /// HTTPS request 9 | Https, 10 | /// Common payload. Write payload and read response. 11 | Common, 12 | /// Common payload for TLS. Write payload and read response with TLS. 13 | CommonTls, 14 | } 15 | 16 | /// Payload information for service detection 17 | #[derive(Clone, Debug, PartialEq)] 18 | pub struct PayloadInfo { 19 | pub payload: Vec, 20 | pub payload_type: PayloadType, 21 | } 22 | 23 | /// Payload builder for service detection 24 | #[derive(Clone, Debug)] 25 | pub struct PayloadBuilder { 26 | payload_info: PayloadInfo, 27 | } 28 | 29 | impl PayloadBuilder { 30 | /// Create new PayloadBuilder 31 | pub fn new() -> Self { 32 | PayloadBuilder { 33 | payload_info: PayloadInfo { 34 | payload: vec![], 35 | payload_type: PayloadType::Common, 36 | }, 37 | } 38 | } 39 | /// Create new PayloadBuilder for TLS 40 | pub fn new_tls() -> Self { 41 | PayloadBuilder { 42 | payload_info: PayloadInfo { 43 | payload: vec![], 44 | payload_type: PayloadType::CommonTls, 45 | }, 46 | } 47 | } 48 | /// Add byte to payload 49 | pub fn add_byte(&mut self, byte: u8) -> &mut Self { 50 | self.payload_info.payload.push(byte); 51 | self 52 | } 53 | /// Add bytes to payload 54 | pub fn add_bytes(&mut self, bytes: &[u8]) -> &mut Self { 55 | self.payload_info.payload.extend_from_slice(bytes); 56 | self 57 | } 58 | /// Add bytes (from string) to payload 59 | pub fn add_str(&mut self, s: &str) -> &mut Self { 60 | self.payload_info.payload.extend_from_slice(s.as_bytes()); 61 | self 62 | } 63 | /// Enable/Diable TLS 64 | pub fn set_tls(&mut self, tls_enabled: bool) -> &mut Self { 65 | if tls_enabled { 66 | self.payload_info.payload_type = PayloadType::CommonTls; 67 | } else { 68 | self.payload_info.payload_type = PayloadType::Common; 69 | } 70 | self 71 | } 72 | /// Return payload as Vec 73 | pub fn bytes(self) -> Vec { 74 | self.payload_info.payload 75 | } 76 | /// Return payload as PayloadInfo 77 | pub fn payload(self) -> PayloadInfo { 78 | self.payload_info 79 | } 80 | /* pub fn null() -> PayloadInfo { 81 | PayloadInfo { 82 | payload: vec![0x00], 83 | payload_type: PayloadType::Null, 84 | } 85 | } */ 86 | /// Create a new PayloadInfo with a generic line 87 | pub fn generic_line() -> PayloadInfo { 88 | PayloadInfo { 89 | payload: "\r\n\r\n".as_bytes().to_vec(), 90 | payload_type: PayloadType::Common, 91 | } 92 | } 93 | /// Create a new PayloadInfo with a generic line for TLS 94 | pub fn generic_line_tls() -> PayloadInfo { 95 | PayloadInfo { 96 | payload: "\r\n\r\n".as_bytes().to_vec(), 97 | payload_type: PayloadType::CommonTls, 98 | } 99 | } 100 | /// Create a new PayloadInfo with a hello message 101 | pub fn hello() -> PayloadInfo { 102 | PayloadInfo { 103 | payload: "EHLO\r\n".as_bytes().to_vec(), 104 | payload_type: PayloadType::Common, 105 | } 106 | } 107 | /// Create a new PayloadInfo with a hello message for TLS 108 | pub fn hello_tls() -> PayloadInfo { 109 | PayloadInfo { 110 | payload: "EHLO\r\n".as_bytes().to_vec(), 111 | payload_type: PayloadType::CommonTls, 112 | } 113 | } 114 | /// Create a new PayloadInfo with a HTTP head request 115 | pub fn http_head() -> PayloadInfo { 116 | PayloadInfo { 117 | payload: "HEAD / HTTP/1.0\r\n\r\n".as_bytes().to_vec(), 118 | payload_type: PayloadType::Http, 119 | } 120 | } 121 | /// Create a new PayloadInfo with a HTTPS head request 122 | pub fn https_head(hostname: &str) -> PayloadInfo { 123 | let req: String = format!( 124 | "HEAD / HTTP/1.1\r\nHost: {}\r\nConnection: close\r\nAccept-Encoding: identity\r\n\r\n", 125 | hostname 126 | ); 127 | PayloadInfo { 128 | payload: req.into_bytes(), 129 | payload_type: PayloadType::Https, 130 | } 131 | } 132 | /// Create a new PayloadInfo with a HTTP get request 133 | pub fn http_get(path: &str) -> PayloadInfo { 134 | let req = format!("GET {} HTTP/1.1\r\nHost: example.com\r\n\r\n", path); 135 | PayloadInfo { 136 | payload: req.into_bytes(), 137 | payload_type: PayloadType::Http, 138 | } 139 | } 140 | /// Create a new PayloadInfo with a HTTPS get request 141 | pub fn https_get(path: &str, hostname: &str) -> PayloadInfo { 142 | let req = format!( 143 | "GET {} HTTP/1.1\r\nHost: {}\r\nConnection: close\r\nAccept-Encoding: identity\r\n\r\n", 144 | path, hostname 145 | ); 146 | PayloadInfo { 147 | payload: req.into_bytes(), 148 | payload_type: PayloadType::Https, 149 | } 150 | } 151 | /* pub fn ftp_user(username: &str) -> PayloadInfo { 152 | let req = format!("USER {}\r\n", username); 153 | PayloadInfo { 154 | payload: req.into_bytes(), 155 | payload_type: PayloadType::Common, 156 | } 157 | } */ 158 | /* pub fn smtp_ehlo() -> PayloadInfo { 159 | PayloadInfo { 160 | payload: "EHLO example.com\r\n".as_bytes().to_vec(), 161 | payload_type: PayloadType::Common, 162 | } 163 | } */ 164 | /* pub fn tls_1_1_session_request() -> PayloadInfo { 165 | PayloadInfo { 166 | payload: vec![ 167 | 0x16, 0x03, 0x02, // Content Type: Handshake (22), Version: TLS 1.1 (0x0302) 168 | 0x00, 0x01, 0xfc, 0x01, 0x00, 0x00, 0xf8, // Length: 1 byte, Handshake Type: Session Request (0x00) 169 | ], 170 | payload_type: PayloadType::CommonTls, 171 | } 172 | } */ 173 | /* pub fn tls_1_2_session_request() -> PayloadInfo { 174 | PayloadInfo { 175 | payload: vec![ 176 | 0x16, 0x03, 0x03, // Content Type: Handshake (22), Version: TLS 1.2 (0x0303) 177 | 0x00, 0x01, 0xfc, 0x01, 0x00, 0x00, 0xf8, // Length: 1 byte, Handshake Type: Session Request (0x00) 178 | ], 179 | payload_type: PayloadType::CommonTls, 180 | } 181 | } */ 182 | /* pub fn tls_1_3_session_request() -> PayloadInfo { 183 | PayloadInfo { 184 | payload: vec![ 185 | 0x16, 0x03, 0x04, // Content Type: Handshake (22), Version: TLS 1.3 (0x0304) 186 | 0x00, 0x01, 0xfc, 0x01, 0x00, 0x00, 0xf8, // Length: 1 byte, Handshake Type: Session Request (0x00) 187 | ], 188 | payload_type: PayloadType::CommonTls, 189 | } 190 | } */ 191 | /* pub fn ssh_public_key_request(username: &str) -> PayloadInfo { 192 | let payload = format!("{}\0ssh-connection\0\0\0\0\0\0\0\0\0\0\0\0\0\0", username); 193 | PayloadInfo { 194 | payload: payload.as_bytes().to_vec(), 195 | payload_type: PayloadType::Common, 196 | } 197 | } */ 198 | } 199 | -------------------------------------------------------------------------------- /src/scan/packet.rs: -------------------------------------------------------------------------------- 1 | use super::setting::HostScanType; 2 | use crate::config::{DEFAULT_HOP_LIMIT, DEFAULT_LOCAL_TCP_PORT, DEFAULT_LOCAL_UDP_PORT}; 3 | use crate::host::Host; 4 | use crate::packet::setting::PacketBuildSetting; 5 | use netdev::Interface; 6 | use nex::net::ip::is_global_ipv6; 7 | use std::net::IpAddr; 8 | 9 | pub(crate) fn build_hostscan_packet( 10 | interface: &Interface, 11 | target_host: &Host, 12 | scan_type: &HostScanType, 13 | ip_packet: bool, 14 | ) -> Vec { 15 | let mut build_setting = PacketBuildSetting::new(); 16 | if let Some(mac_addr) = &interface.mac_addr { 17 | build_setting.src_mac = *mac_addr; 18 | } 19 | if let Some(gateway) = &interface.gateway { 20 | build_setting.dst_mac = gateway.mac_addr; 21 | } 22 | match target_host.ip_addr { 23 | IpAddr::V4(ipv4_addr) => { 24 | interface.ipv4.iter().for_each(|ipv4| { 25 | build_setting.src_ip = IpAddr::V4(ipv4.addr()); 26 | }); 27 | build_setting.dst_ip = IpAddr::V4(ipv4_addr); 28 | } 29 | IpAddr::V6(ipv6_addr) => { 30 | if is_global_ipv6(&ipv6_addr) { 31 | interface.ipv6.iter().for_each(|ipv6| { 32 | if is_global_ipv6(&ipv6.addr()) { 33 | build_setting.src_ip = IpAddr::V6(ipv6.addr()); 34 | } 35 | }); 36 | } else { 37 | interface.ipv6.iter().for_each(|ipv6| { 38 | build_setting.src_ip = IpAddr::V6(ipv6.addr()); 39 | }); 40 | } 41 | build_setting.dst_ip = IpAddr::V6(ipv6_addr); 42 | } 43 | } 44 | if target_host.ports.len() > 0 { 45 | build_setting.dst_port = target_host.ports[0].number; 46 | } 47 | build_setting.hop_limit = DEFAULT_HOP_LIMIT; 48 | if ip_packet || interface.is_tun() || interface.is_loopback() { 49 | build_setting.ip_packet = true; 50 | } 51 | match scan_type { 52 | HostScanType::IcmpPingScan => crate::packet::icmp::build_icmp_packet(build_setting), 53 | HostScanType::TcpPingScan => { 54 | build_setting.src_port = DEFAULT_LOCAL_TCP_PORT; 55 | crate::packet::tcp::build_tcp_syn_packet(build_setting) 56 | } 57 | HostScanType::UdpPingScan => { 58 | build_setting.src_port = DEFAULT_LOCAL_UDP_PORT; 59 | crate::packet::udp::build_udp_packet(build_setting) 60 | } 61 | } 62 | } 63 | 64 | pub(crate) fn build_hostscan_ip_next_packet( 65 | interface: &Interface, 66 | target_host: &Host, 67 | scan_type: &HostScanType, 68 | ) -> Vec { 69 | let mut build_setting = PacketBuildSetting::new(); 70 | if let Some(mac_addr) = &interface.mac_addr { 71 | build_setting.src_mac = *mac_addr; 72 | } 73 | if let Some(gateway) = &interface.gateway { 74 | build_setting.dst_mac = gateway.mac_addr; 75 | } 76 | match target_host.ip_addr { 77 | IpAddr::V4(ipv4_addr) => { 78 | interface.ipv4.iter().for_each(|ipv4| { 79 | build_setting.src_ip = IpAddr::V4(ipv4.addr()); 80 | }); 81 | build_setting.dst_ip = IpAddr::V4(ipv4_addr); 82 | } 83 | IpAddr::V6(ipv6_addr) => { 84 | if is_global_ipv6(&ipv6_addr) { 85 | interface.ipv6.iter().for_each(|ipv6| { 86 | if is_global_ipv6(&ipv6.addr()) { 87 | build_setting.src_ip = IpAddr::V6(ipv6.addr()); 88 | } 89 | }); 90 | } else { 91 | interface.ipv6.iter().for_each(|ipv6| { 92 | build_setting.src_ip = IpAddr::V6(ipv6.addr()); 93 | }); 94 | } 95 | build_setting.dst_ip = IpAddr::V6(ipv6_addr); 96 | } 97 | } 98 | if target_host.ports.len() > 0 { 99 | build_setting.dst_port = target_host.ports[0].number; 100 | } 101 | build_setting.hop_limit = DEFAULT_HOP_LIMIT; 102 | if interface.is_tun() || interface.is_loopback() { 103 | build_setting.ip_packet = true; 104 | } 105 | match scan_type { 106 | HostScanType::IcmpPingScan => crate::packet::icmp::build_ip_next_icmp_packet(build_setting), 107 | HostScanType::TcpPingScan => { 108 | build_setting.src_port = DEFAULT_LOCAL_TCP_PORT; 109 | crate::packet::tcp::build_ip_next_tcp_syn_packet(build_setting) 110 | } 111 | HostScanType::UdpPingScan => { 112 | build_setting.src_port = DEFAULT_LOCAL_UDP_PORT; 113 | crate::packet::udp::build_ip_next_udp_packet(build_setting) 114 | } 115 | } 116 | } 117 | 118 | pub(crate) fn build_portscan_packet( 119 | interface: &Interface, 120 | target_ip_addr: IpAddr, 121 | target_port: u16, 122 | ip_packet: bool, 123 | ) -> Vec { 124 | let mut build_setting = PacketBuildSetting::new(); 125 | if let Some(mac_addr) = &interface.mac_addr { 126 | build_setting.src_mac = *mac_addr; 127 | } 128 | if let Some(gateway) = &interface.gateway { 129 | build_setting.dst_mac = gateway.mac_addr; 130 | } 131 | match target_ip_addr { 132 | IpAddr::V4(ipv4_addr) => { 133 | interface.ipv4.iter().for_each(|ipv4| { 134 | build_setting.src_ip = IpAddr::V4(ipv4.addr()); 135 | }); 136 | build_setting.dst_ip = IpAddr::V4(ipv4_addr); 137 | } 138 | IpAddr::V6(ipv6_addr) => { 139 | if is_global_ipv6(&ipv6_addr) { 140 | interface.ipv6.iter().for_each(|ipv6| { 141 | if is_global_ipv6(&ipv6.addr()) { 142 | build_setting.src_ip = IpAddr::V6(ipv6.addr()); 143 | } 144 | }); 145 | } else { 146 | interface.ipv6.iter().for_each(|ipv6| { 147 | build_setting.src_ip = IpAddr::V6(ipv6.addr()); 148 | }); 149 | } 150 | build_setting.dst_ip = IpAddr::V6(ipv6_addr); 151 | } 152 | } 153 | build_setting.dst_port = target_port; 154 | build_setting.hop_limit = DEFAULT_HOP_LIMIT; 155 | if ip_packet || interface.is_tun() || interface.is_loopback() { 156 | build_setting.ip_packet = true; 157 | } 158 | build_setting.src_port = DEFAULT_LOCAL_TCP_PORT; 159 | crate::packet::tcp::build_tcp_syn_packet(build_setting) 160 | } 161 | 162 | pub(crate) fn build_portscan_ip_next_packet( 163 | interface: &Interface, 164 | target_ip_addr: IpAddr, 165 | target_port: u16, 166 | ) -> Vec { 167 | let mut build_setting = PacketBuildSetting::new(); 168 | if let Some(mac_addr) = &interface.mac_addr { 169 | build_setting.src_mac = *mac_addr; 170 | } 171 | if let Some(gateway) = &interface.gateway { 172 | build_setting.dst_mac = gateway.mac_addr; 173 | } 174 | match target_ip_addr { 175 | IpAddr::V4(ipv4_addr) => { 176 | interface.ipv4.iter().for_each(|ipv4| { 177 | build_setting.src_ip = IpAddr::V4(ipv4.addr()); 178 | }); 179 | build_setting.dst_ip = IpAddr::V4(ipv4_addr); 180 | } 181 | IpAddr::V6(ipv6_addr) => { 182 | if is_global_ipv6(&ipv6_addr) { 183 | interface.ipv6.iter().for_each(|ipv6| { 184 | if is_global_ipv6(&ipv6.addr()) { 185 | build_setting.src_ip = IpAddr::V6(ipv6.addr()); 186 | } 187 | }); 188 | } else { 189 | interface.ipv6.iter().for_each(|ipv6| { 190 | build_setting.src_ip = IpAddr::V6(ipv6.addr()); 191 | }); 192 | } 193 | build_setting.dst_ip = IpAddr::V6(ipv6_addr); 194 | } 195 | } 196 | build_setting.dst_port = target_port; 197 | build_setting.hop_limit = DEFAULT_HOP_LIMIT; 198 | if interface.is_tun() || interface.is_loopback() { 199 | build_setting.ip_packet = true; 200 | } 201 | build_setting.src_port = DEFAULT_LOCAL_TCP_PORT; 202 | crate::packet::tcp::build_ip_next_tcp_syn_packet(build_setting) 203 | } 204 | -------------------------------------------------------------------------------- /src/pcap/mod.rs: -------------------------------------------------------------------------------- 1 | use std::net::IpAddr; 2 | //use std::sync::mpsc::Sender; 3 | use crate::packet::frame::PacketFrame; 4 | use nex::datalink::RawReceiver; 5 | use nex::packet::frame::Frame; 6 | use nex::packet::frame::ParseOption; 7 | use nex::packet::{ethernet::EtherType, ip::IpNextLevelProtocol}; 8 | use std::collections::HashSet; 9 | use std::sync::{Arc, Mutex}; 10 | use std::time::Duration; 11 | use std::time::Instant; 12 | 13 | /// Packet capture options 14 | #[derive(Debug, Clone)] 15 | pub struct PacketCaptureOptions { 16 | /// Interface index 17 | #[allow(dead_code)] 18 | pub interface_index: u32, 19 | /// Source IP addresses to filter. If empty, all source IP addresses will be captured 20 | pub src_ips: HashSet, 21 | /// Destination IP addresses to filter. If empty, all destination IP addresses will be captured 22 | pub dst_ips: HashSet, 23 | /// Source ports to filter. If empty, all source ports will be captured 24 | pub src_ports: HashSet, 25 | /// Destination ports to filter. If empty, all destination ports will be captured 26 | pub dst_ports: HashSet, 27 | /// Ether types to filter. If empty, all ether types will be captured 28 | pub ether_types: HashSet, 29 | /// IP protocols to filter. If empty, all IP protocols will be captured 30 | pub ip_protocols: HashSet, 31 | /// Capture duration limit 32 | pub capture_timeout: Duration, 33 | /// Use TUN interface 34 | pub tunnel: bool, 35 | /// Loopback interface 36 | pub loopback: bool, 37 | } 38 | 39 | /// Start packet capture 40 | pub fn start_capture( 41 | rx: &mut Box, 42 | capture_options: PacketCaptureOptions, 43 | stop: &Arc>, 44 | ) -> Vec { 45 | let mut frames = Vec::new(); 46 | let start_time = Instant::now(); 47 | loop { 48 | match rx.next() { 49 | Ok(packet) => { 50 | let mut parse_option: ParseOption = ParseOption::default(); 51 | if capture_options.tunnel 52 | || (cfg!(any(target_os = "macos", target_os = "ios")) 53 | && capture_options.loopback) 54 | { 55 | let payload_offset; 56 | if capture_options.loopback { 57 | payload_offset = 14; 58 | } else { 59 | payload_offset = 0; 60 | } 61 | parse_option.from_ip_packet = true; 62 | parse_option.offset = payload_offset; 63 | } 64 | let frame: Frame = Frame::from_bytes(&packet, parse_option); 65 | if filter_packet(&frame, &capture_options) { 66 | let packet_frame = PacketFrame::from_nex_frame(&frame); 67 | frames.push(packet_frame); 68 | /* match msg_tx.send(packet_frame) { 69 | Ok(_) => {} 70 | Err(_) => {} 71 | } */ 72 | } 73 | } 74 | Err(_) => {} 75 | } 76 | match stop.lock() { 77 | Ok(stop) => { 78 | if *stop { 79 | break; 80 | } 81 | } 82 | Err(_) => {} 83 | } 84 | if Instant::now().duration_since(start_time) > capture_options.capture_timeout { 85 | break; 86 | } 87 | } 88 | frames 89 | } 90 | 91 | /* /// Start packet capture 92 | pub fn start_capture( 93 | capture_options: PacketCaptureOptions, 94 | stop: &Arc>, 95 | interface: Interface, 96 | ) -> Vec { 97 | let mut frames = Vec::new(); 98 | let config = nex::datalink::Config { 99 | write_buffer_size: 4096, 100 | read_buffer_size: 4096, 101 | read_timeout: Some(capture_options.read_timeout), 102 | write_timeout: None, 103 | channel_type: nex::datalink::ChannelType::Layer2, 104 | bpf_fd_attempts: 1000, 105 | linux_fanout: None, 106 | promiscuous: capture_options.promiscuous, 107 | }; 108 | let (mut _tx, mut rx) = match nex::datalink::channel(&interface, config) { 109 | Ok(nex::datalink::Channel::Ethernet(tx, rx)) => (tx, rx), 110 | Ok(_) => { 111 | //thread_log!(warn, "Unknown channel type"); 112 | return frames; 113 | }, 114 | Err(_e) => { 115 | //thread_log!(error, "Error happened {}", e); 116 | return frames; 117 | }, 118 | }; 119 | let start_time = Instant::now(); 120 | loop { 121 | match rx.next() { 122 | Ok(packet) => { 123 | let mut parse_option: ParseOption = ParseOption::default(); 124 | if interface.is_tun() || (cfg!(any(target_os = "macos", target_os = "ios")) && interface.is_loopback()) { 125 | let payload_offset; 126 | if interface.is_loopback() { 127 | payload_offset = 14; 128 | } else { 129 | payload_offset = 0; 130 | } 131 | parse_option.from_ip_packet = true; 132 | parse_option.offset = payload_offset; 133 | } 134 | let frame: Frame = Frame::from_bytes(&packet, parse_option); 135 | if filter_packet(&frame, &capture_options) { 136 | let packet_frame = PacketFrame::from_nex_frame(&frame); 137 | frames.push(packet_frame); 138 | /* match msg_tx.send(packet_frame) { 139 | Ok(_) => {} 140 | Err(_) => {} 141 | } */ 142 | } 143 | } 144 | Err(_) => {} 145 | } 146 | match stop.lock() { 147 | Ok(stop) => { 148 | if *stop { 149 | break; 150 | } 151 | } 152 | Err(_) => {} 153 | } 154 | if Instant::now().duration_since(start_time) > capture_options.capture_timeout { 155 | break; 156 | } 157 | } 158 | frames 159 | } */ 160 | 161 | fn filter_packet(frame: &Frame, capture_options: &PacketCaptureOptions) -> bool { 162 | if let Some(datalink) = &frame.datalink { 163 | if let Some(ethernet_header) = &datalink.ethernet { 164 | if !filter_ether_type(ethernet_header.ethertype, capture_options) { 165 | return false; 166 | } 167 | } 168 | if let Some(arp_header) = &datalink.arp { 169 | if !filter_host( 170 | IpAddr::V4(arp_header.sender_proto_addr), 171 | IpAddr::V4(arp_header.target_proto_addr), 172 | capture_options, 173 | ) { 174 | return false; 175 | } 176 | } 177 | } 178 | if let Some(ip) = &frame.ip { 179 | if let Some(ipv4_header) = &ip.ipv4 { 180 | if !filter_host( 181 | IpAddr::V4(ipv4_header.source), 182 | IpAddr::V4(ipv4_header.destination), 183 | capture_options, 184 | ) { 185 | return false; 186 | } 187 | if !filter_ip_protocol(ipv4_header.next_level_protocol, capture_options) { 188 | return false; 189 | } 190 | } 191 | if let Some(ipv6_header) = &ip.ipv6 { 192 | if !filter_host( 193 | IpAddr::V6(ipv6_header.source), 194 | IpAddr::V6(ipv6_header.destination), 195 | capture_options, 196 | ) { 197 | return false; 198 | } 199 | if !filter_ip_protocol(ipv6_header.next_header, capture_options) { 200 | return false; 201 | } 202 | } 203 | } 204 | if let Some(transport) = &frame.transport { 205 | if let Some(tcp_header) = &transport.tcp { 206 | if !filter_port(tcp_header.source, tcp_header.destination, capture_options) { 207 | return false; 208 | } 209 | } 210 | if let Some(udp_header) = &transport.udp { 211 | if !filter_port(udp_header.source, udp_header.destination, capture_options) { 212 | return false; 213 | } 214 | } 215 | } 216 | true 217 | } 218 | 219 | fn filter_host(src_ip: IpAddr, dst_ip: IpAddr, capture_options: &PacketCaptureOptions) -> bool { 220 | if capture_options.src_ips.len() == 0 && capture_options.dst_ips.len() == 0 { 221 | return true; 222 | } 223 | if capture_options.src_ips.contains(&src_ip) || capture_options.dst_ips.contains(&dst_ip) { 224 | return true; 225 | } else { 226 | return false; 227 | } 228 | } 229 | 230 | fn filter_port(src_port: u16, dst_port: u16, capture_options: &PacketCaptureOptions) -> bool { 231 | if capture_options.src_ports.len() == 0 && capture_options.dst_ports.len() == 0 { 232 | return true; 233 | } 234 | if capture_options.src_ports.contains(&src_port) 235 | || capture_options.dst_ports.contains(&dst_port) 236 | { 237 | return true; 238 | } else { 239 | return false; 240 | } 241 | } 242 | 243 | fn filter_ether_type(ether_type: EtherType, capture_options: &PacketCaptureOptions) -> bool { 244 | if capture_options.ether_types.len() == 0 || capture_options.ether_types.contains(ðer_type) { 245 | return true; 246 | } else { 247 | return false; 248 | } 249 | } 250 | 251 | fn filter_ip_protocol( 252 | protocol: IpNextLevelProtocol, 253 | capture_options: &PacketCaptureOptions, 254 | ) -> bool { 255 | if capture_options.ip_protocols.len() == 0 || capture_options.ip_protocols.contains(&protocol) { 256 | return true; 257 | } else { 258 | return false; 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /src/dns/mod.rs: -------------------------------------------------------------------------------- 1 | use std::net::IpAddr; 2 | use std::time::Duration; 3 | 4 | #[cfg(not(any(unix, target_os = "windows")))] 5 | use hickory_resolver::config::{ResolverConfig, ResolverOpts}; 6 | use hickory_resolver::Resolver; 7 | 8 | use futures::stream::{self, StreamExt}; 9 | 10 | use hickory_resolver::AsyncResolver; 11 | use std::collections::HashMap; 12 | use std::str::FromStr; 13 | use std::thread; 14 | 15 | #[cfg(not(target_os = "windows"))] 16 | const DEFAULT_TIMEOUT: Duration = Duration::from_millis(200); 17 | #[cfg(not(target_os = "windows"))] 18 | const DEFAULT_TIMEOUT_GLOBAL: Duration = Duration::from_millis(1000); 19 | #[cfg(target_os = "windows")] 20 | const DEFAULT_TIMEOUT: Duration = Duration::from_millis(20); 21 | #[cfg(target_os = "windows")] 22 | const DEFAULT_TIMEOUT_GLOBAL: Duration = Duration::from_millis(1000); 23 | 24 | pub fn lookup_host_name(host_name: &str) -> Option { 25 | let ip_vec: Vec = resolve_domain(host_name.to_string()); 26 | let mut ipv6_vec: Vec = vec![]; 27 | for ip in ip_vec { 28 | match ip { 29 | IpAddr::V4(_) => { 30 | return Some(ip); 31 | } 32 | IpAddr::V6(_) => { 33 | ipv6_vec.push(ip); 34 | } 35 | } 36 | } 37 | if ipv6_vec.len() > 0 { 38 | return Some(ipv6_vec[0]); 39 | } else { 40 | None 41 | } 42 | } 43 | 44 | pub async fn lookup_host_name_async(host_name: String) -> Option { 45 | let ip_vec: Vec = resolve_domain_async(host_name).await; 46 | let mut ipv6_vec: Vec = vec![]; 47 | for ip in ip_vec { 48 | match ip { 49 | IpAddr::V4(_) => { 50 | return Some(ip); 51 | } 52 | IpAddr::V6(_) => { 53 | ipv6_vec.push(ip); 54 | } 55 | } 56 | } 57 | if ipv6_vec.len() > 0 { 58 | return Some(ipv6_vec[0]); 59 | } else { 60 | None 61 | } 62 | } 63 | 64 | pub fn lookup_ip_addr(ip_addr: &IpAddr) -> Option { 65 | let names: Vec = resolve_ip(ip_addr); 66 | if names.len() > 0 { 67 | return Some(names[0].clone()); 68 | } else { 69 | return None; 70 | } 71 | } 72 | 73 | pub async fn lookup_ip_addr_async(ip_addr: String) -> String { 74 | let ips: Vec = resolve_ip_async(ip_addr).await; 75 | if ips.len() > 0 { 76 | return ips[0].clone(); 77 | } else { 78 | return String::new(); 79 | } 80 | } 81 | 82 | #[cfg(any(unix, target_os = "windows"))] 83 | fn resolve_domain(host_name: String) -> Vec { 84 | let mut ips: Vec = vec![]; 85 | let resolver = Resolver::from_system_conf().unwrap(); 86 | match resolver.lookup_ip(host_name) { 87 | Ok(lip) => { 88 | for ip in lip.iter() { 89 | ips.push(ip); 90 | } 91 | } 92 | Err(_) => {} 93 | } 94 | ips 95 | } 96 | 97 | #[cfg(not(any(unix, target_os = "windows")))] 98 | fn resolve_domain(host_name: String) -> Vec { 99 | let mut ips: Vec = vec![]; 100 | let resolver = Resolver::new(ResolverConfig::default(), ResolverOpts::default()).unwrap(); 101 | match resolver.lookup_ip(host_name) { 102 | Ok(lip) => { 103 | for ip in lip.iter() { 104 | ips.push(ip); 105 | } 106 | } 107 | Err(_) => {} 108 | } 109 | ips 110 | } 111 | 112 | #[cfg(any(unix, target_os = "windows"))] 113 | fn resolve_ip(ip_addr: &IpAddr) -> Vec { 114 | let mut names: Vec = vec![]; 115 | let mut system_conf = hickory_resolver::system_conf::read_system_conf().unwrap(); 116 | if crate::ip::is_global_addr(ip_addr) { 117 | system_conf.1.timeout = DEFAULT_TIMEOUT_GLOBAL; 118 | } else { 119 | system_conf.1.timeout = DEFAULT_TIMEOUT; 120 | } 121 | let resolver = Resolver::new(system_conf.0, system_conf.1).unwrap(); 122 | match resolver.reverse_lookup(*ip_addr) { 123 | Ok(rlookup) => { 124 | for record in rlookup.as_lookup().record_iter() { 125 | match record.data() { 126 | Some(data) => { 127 | let name = data.to_string(); 128 | if name.ends_with(".") { 129 | names.push(name[0..name.len() - 1].to_string()); 130 | } else { 131 | names.push(name); 132 | } 133 | } 134 | None => {} 135 | } 136 | } 137 | names 138 | } 139 | Err(_) => { 140 | return names; 141 | } 142 | } 143 | } 144 | 145 | #[cfg(not(any(unix, target_os = "windows")))] 146 | fn resolve_ip(ip_addr: IpAddr) -> Vec { 147 | let mut names: Vec = vec![]; 148 | let resolver = Resolver::new(ResolverConfig::default(), ResolverOpts::default()).unwrap(); 149 | match resolver.reverse_lookup(ip_addr) { 150 | Ok(rlookup) => { 151 | for record in rlookup.as_lookup().record_iter() { 152 | match record.data() { 153 | Some(data) => { 154 | let name = data.to_string(); 155 | if name.ends_with(".") { 156 | names.push(name[0..name.len() - 1].to_string()); 157 | } else { 158 | names.push(name); 159 | } 160 | } 161 | None => {} 162 | } 163 | } 164 | names 165 | } 166 | Err(_) => { 167 | return names; 168 | } 169 | } 170 | } 171 | 172 | #[cfg(any(unix, target_os = "windows"))] 173 | async fn resolve_domain_async(host_name: String) -> Vec { 174 | let mut ips: Vec = vec![]; 175 | let resolver = AsyncResolver::tokio_from_system_conf().unwrap(); 176 | match resolver.lookup_ip(host_name).await { 177 | Ok(lip) => { 178 | for ip in lip.iter() { 179 | ips.push(ip); 180 | } 181 | } 182 | Err(_) => {} 183 | } 184 | ips 185 | } 186 | 187 | #[cfg(not(any(unix, target_os = "windows")))] 188 | async fn resolve_domain_async(host_name: String) -> Vec { 189 | let mut ips: Vec = vec![]; 190 | let resolver = 191 | AsyncResolver::tokio(ResolverConfig::default(), ResolverOpts::default()).unwrap(); 192 | match resolver.lookup_ip(host_name).await { 193 | Ok(lip) => { 194 | for ip in lip.iter() { 195 | ips.push(ip); 196 | } 197 | } 198 | Err(_) => {} 199 | } 200 | ips 201 | } 202 | 203 | #[cfg(any(unix, target_os = "windows"))] 204 | async fn resolve_ip_async(ip_addr: String) -> Vec { 205 | let ip_addr: IpAddr = IpAddr::from_str(ip_addr.as_str()).unwrap(); 206 | let mut names: Vec = vec![]; 207 | let mut system_conf = hickory_resolver::system_conf::read_system_conf().unwrap(); 208 | if crate::ip::is_global_addr(&ip_addr) { 209 | system_conf.1.timeout = DEFAULT_TIMEOUT_GLOBAL; 210 | } else { 211 | system_conf.1.timeout = DEFAULT_TIMEOUT; 212 | } 213 | let resolver = AsyncResolver::tokio(system_conf.0, system_conf.1); 214 | match resolver.reverse_lookup(ip_addr).await { 215 | Ok(rlookup) => { 216 | for record in rlookup.as_lookup().record_iter() { 217 | match record.data() { 218 | Some(data) => { 219 | let name = data.to_string(); 220 | if name.ends_with(".") { 221 | names.push(name[0..name.len() - 1].to_string()); 222 | } else { 223 | names.push(name); 224 | } 225 | } 226 | None => {} 227 | } 228 | } 229 | names 230 | } 231 | Err(_) => { 232 | return names; 233 | } 234 | } 235 | } 236 | 237 | #[cfg(not(any(unix, target_os = "windows")))] 238 | async fn resolve_ip_async(ip_addr: String) -> Vec { 239 | let mut names: Vec = vec![]; 240 | let resolver = 241 | AsyncResolver::tokio(ResolverConfig::default(), ResolverOpts::default()).unwrap(); 242 | match resolver 243 | .reverse_lookup(IpAddr::from_str(ip_addr.as_str()).unwrap()) 244 | .await 245 | { 246 | Ok(rlookup) => { 247 | for record in rlookup.as_lookup().record_iter() { 248 | match record.data() { 249 | Some(data) => { 250 | let name = data.to_string(); 251 | if name.ends_with(".") { 252 | names.push(name[0..name.len() - 1].to_string()); 253 | } else { 254 | names.push(name); 255 | } 256 | } 257 | None => {} 258 | } 259 | } 260 | names 261 | } 262 | Err(_) => { 263 | return names; 264 | } 265 | } 266 | } 267 | 268 | pub async fn lookup_ips_async(ips: Vec) -> HashMap { 269 | let mut tasks = stream::iter(ips) 270 | .map(|ip| async move { 271 | let names = resolve_ip_async(ip.to_string()).await; 272 | (ip, names) 273 | }) 274 | .buffer_unordered(10); 275 | let mut results: HashMap = HashMap::new(); 276 | while let Some(result) = tasks.next().await { 277 | results.insert( 278 | result.0, 279 | result.1.first().unwrap_or(&String::new()).to_string(), 280 | ); 281 | } 282 | results 283 | } 284 | 285 | pub fn lookup_ips(ips: Vec) -> HashMap { 286 | let rt: tokio::runtime::Runtime = tokio::runtime::Runtime::new().unwrap(); 287 | let handle = thread::spawn(move || rt.block_on(async { lookup_ips_async(ips).await })); 288 | handle.join().unwrap() 289 | } 290 | 291 | pub fn lookup_host(host: String) -> Vec { 292 | resolve_domain(host) 293 | } 294 | 295 | pub fn lookup_addr(addr: &IpAddr) -> Vec { 296 | resolve_ip(addr) 297 | } 298 | -------------------------------------------------------------------------------- /src/scan/blocking.rs: -------------------------------------------------------------------------------- 1 | use crate::config::PCAP_WAIT_TIME_MILLIS; 2 | use crate::host::Host; 3 | use crate::packet::frame::PacketFrame; 4 | use crate::pcap::PacketCaptureOptions; 5 | use crate::scan::setting::{HostScanSetting, PortScanSetting}; 6 | use netdev::Interface; 7 | use nex::datalink::RawSender; 8 | use nex::packet::ip::IpNextLevelProtocol; 9 | use std::collections::HashSet; 10 | use std::net::SocketAddr; 11 | use std::sync::mpsc::Sender; 12 | use std::sync::{Arc, Mutex}; 13 | use std::thread; 14 | use std::time::Duration; 15 | 16 | use super::packet::{build_hostscan_packet, build_portscan_packet}; 17 | use super::result::{parse_hostscan_result, parse_portscan_result, ScanResult, ScanStatus}; 18 | use super::setting::{HostScanType, PortScanType}; 19 | 20 | pub(crate) fn send_hostscan_packets( 21 | tx: &mut Box, 22 | interface: &Interface, 23 | targets: Vec, 24 | ptx: &Arc>>, 25 | scan_type: HostScanType, 26 | ) { 27 | // Acquire message sender lock 28 | let ptx_lock = match ptx.lock() { 29 | Ok(ptx) => ptx, 30 | Err(e) => { 31 | eprintln!("Failed to lock ptx: {}", e); 32 | return; 33 | } 34 | }; 35 | for target in targets { 36 | let packet = build_hostscan_packet(&interface, &target, &scan_type, false); 37 | match tx.send(&packet) { 38 | Some(_) => { 39 | // Notify packet sent 40 | match ptx_lock.send(target) { 41 | Ok(_) => {} 42 | Err(e) => { 43 | eprintln!("Failed to send message: {}", e); 44 | } 45 | } 46 | } 47 | None => { 48 | eprintln!("Failed to send packet"); 49 | } 50 | } 51 | } 52 | // Drop message sender lock 53 | drop(ptx_lock); 54 | } 55 | 56 | pub(crate) fn send_portscan_packets( 57 | tx: &mut Box, 58 | interface: &Interface, 59 | targets: Vec, 60 | ptx: &Arc>>, 61 | scan_type: PortScanType, 62 | ) { 63 | // Acquire message sender lock 64 | let ptx_lock = match ptx.lock() { 65 | Ok(ptx) => ptx, 66 | Err(e) => { 67 | eprintln!("Failed to lock ptx: {}", e); 68 | return; 69 | } 70 | }; 71 | for target in targets { 72 | match scan_type { 73 | PortScanType::TcpSynScan => { 74 | for port in target.ports { 75 | let packet = 76 | build_portscan_packet(&interface, target.ip_addr, port.number, false); 77 | match tx.send(&packet) { 78 | Some(_) => { 79 | // Notify packet sent 80 | match ptx_lock.send(SocketAddr::new(target.ip_addr, port.number)) { 81 | Ok(_) => {} 82 | Err(e) => { 83 | eprintln!("Failed to send message: {}", e); 84 | } 85 | } 86 | } 87 | None => { 88 | eprintln!("Failed to send packet"); 89 | } 90 | } 91 | } 92 | } 93 | PortScanType::TcpConnectScan => { 94 | // TODO 95 | } 96 | } 97 | } 98 | // Drop message sender lock 99 | drop(ptx_lock); 100 | } 101 | 102 | pub(crate) fn scan_hosts( 103 | scan_setting: HostScanSetting, 104 | ptx: &Arc>>, 105 | ) -> ScanResult { 106 | let interface = match crate::interface::get_interface_by_index(scan_setting.if_index) { 107 | Some(interface) => interface, 108 | None => return ScanResult::new(), 109 | }; 110 | // Create sender 111 | let config = nex::datalink::Config { 112 | write_buffer_size: 4096, 113 | read_buffer_size: 4096, 114 | read_timeout: Some(scan_setting.wait_time), 115 | write_timeout: None, 116 | channel_type: nex::datalink::ChannelType::Layer2, 117 | bpf_fd_attempts: 1000, 118 | linux_fanout: None, 119 | promiscuous: false, 120 | }; 121 | let (mut tx, mut rx) = match nex::datalink::channel(&interface, config) { 122 | Ok(nex::datalink::Channel::Ethernet(tx, rx)) => (tx, rx), 123 | Ok(_) => return ScanResult::error("Unhandled channel type".to_string()), 124 | Err(e) => return ScanResult::error(format!("Failed to create channel: {}", e)), 125 | }; 126 | let mut capture_options: PacketCaptureOptions = PacketCaptureOptions { 127 | interface_index: interface.index, 128 | src_ips: HashSet::new(), 129 | dst_ips: HashSet::new(), 130 | src_ports: HashSet::new(), 131 | dst_ports: HashSet::new(), 132 | ether_types: HashSet::new(), 133 | ip_protocols: HashSet::new(), 134 | capture_timeout: scan_setting.timeout, 135 | tunnel: interface.is_tun(), 136 | loopback: interface.is_loopback(), 137 | }; 138 | for target in scan_setting.targets.clone() { 139 | capture_options.src_ips.insert(target.ip_addr); 140 | } 141 | match scan_setting.scan_type { 142 | HostScanType::IcmpPingScan => { 143 | capture_options 144 | .ip_protocols 145 | .insert(IpNextLevelProtocol::Icmp); 146 | capture_options 147 | .ip_protocols 148 | .insert(IpNextLevelProtocol::Icmpv6); 149 | } 150 | HostScanType::TcpPingScan => { 151 | capture_options 152 | .ip_protocols 153 | .insert(IpNextLevelProtocol::Tcp); 154 | for target in scan_setting.targets.clone() { 155 | for port in target.ports { 156 | capture_options.src_ports.insert(port.number); 157 | } 158 | } 159 | } 160 | HostScanType::UdpPingScan => { 161 | capture_options 162 | .ip_protocols 163 | .insert(IpNextLevelProtocol::Udp); 164 | capture_options 165 | .ip_protocols 166 | .insert(IpNextLevelProtocol::Icmp); 167 | capture_options 168 | .ip_protocols 169 | .insert(IpNextLevelProtocol::Icmpv6); 170 | } 171 | } 172 | let stop: Arc> = Arc::new(Mutex::new(false)); 173 | let stop_handle = Arc::clone(&stop); 174 | let packets: Arc>> = Arc::new(Mutex::new(vec![])); 175 | let receive_packets: Arc>> = Arc::clone(&packets); 176 | // Spawn pcap thread 177 | let pcap_handler = thread::spawn(move || { 178 | let packets: Vec = 179 | crate::pcap::start_capture(&mut rx, capture_options, &stop_handle); 180 | match receive_packets.lock() { 181 | Ok(mut receive_packets) => { 182 | for p in packets { 183 | receive_packets.push(p); 184 | } 185 | } 186 | Err(e) => { 187 | eprintln!("Failed to lock receive_packets: {}", e); 188 | } 189 | } 190 | }); 191 | // Wait for listener to start (need fix for better way) 192 | thread::sleep(Duration::from_millis(PCAP_WAIT_TIME_MILLIS)); 193 | let start_time = std::time::Instant::now(); 194 | // Send probe packets 195 | send_hostscan_packets( 196 | &mut tx, 197 | &interface, 198 | scan_setting.targets.clone(), 199 | ptx, 200 | scan_setting.scan_type.clone(), 201 | ); 202 | thread::sleep(scan_setting.wait_time); 203 | // Stop pcap 204 | match stop.lock() { 205 | Ok(mut stop) => { 206 | *stop = true; 207 | } 208 | Err(e) => { 209 | eprintln!("Failed to lock stop: {}", e); 210 | } 211 | } 212 | // Wait for listener to stop 213 | match pcap_handler.join() { 214 | Ok(_) => {} 215 | Err(e) => { 216 | eprintln!("Failed to join pcap_handler: {:?}", e); 217 | } 218 | } 219 | let mut scan_result: ScanResult = ScanResult::new(); 220 | match packets.lock() { 221 | Ok(packets) => { 222 | scan_result = parse_hostscan_result(packets.clone(), scan_setting); 223 | } 224 | Err(e) => { 225 | eprintln!("Failed to lock packets: {}", e); 226 | } 227 | } 228 | scan_result.scan_time = start_time.elapsed(); 229 | scan_result.scan_status = ScanStatus::Done; 230 | scan_result 231 | } 232 | 233 | pub(crate) fn scan_ports( 234 | scan_setting: PortScanSetting, 235 | ptx: &Arc>>, 236 | ) -> ScanResult { 237 | let interface = match crate::interface::get_interface_by_index(scan_setting.if_index) { 238 | Some(interface) => interface, 239 | None => return ScanResult::new(), 240 | }; 241 | // Create sender 242 | let config = nex::datalink::Config { 243 | write_buffer_size: 4096, 244 | read_buffer_size: 4096, 245 | read_timeout: Some(scan_setting.wait_time), 246 | write_timeout: None, 247 | channel_type: nex::datalink::ChannelType::Layer2, 248 | bpf_fd_attempts: 1000, 249 | linux_fanout: None, 250 | promiscuous: false, 251 | }; 252 | let (mut tx, mut rx) = match nex::datalink::channel(&interface, config) { 253 | Ok(nex::datalink::Channel::Ethernet(tx, rx)) => (tx, rx), 254 | Ok(_) => return ScanResult::error("Unhandled channel type".to_string()), 255 | Err(e) => return ScanResult::error(format!("Failed to create channel: {}", e)), 256 | }; 257 | let mut capture_options: PacketCaptureOptions = PacketCaptureOptions { 258 | interface_index: interface.index, 259 | src_ips: HashSet::new(), 260 | dst_ips: HashSet::new(), 261 | src_ports: HashSet::new(), 262 | dst_ports: HashSet::new(), 263 | ether_types: HashSet::new(), 264 | ip_protocols: HashSet::new(), 265 | capture_timeout: scan_setting.timeout, 266 | tunnel: interface.is_tun(), 267 | loopback: interface.is_loopback(), 268 | }; 269 | for target in scan_setting.targets.clone() { 270 | capture_options.src_ips.insert(target.ip_addr); 271 | capture_options.src_ports.extend(target.get_ports()); 272 | } 273 | match scan_setting.scan_type { 274 | PortScanType::TcpSynScan => { 275 | capture_options 276 | .ip_protocols 277 | .insert(IpNextLevelProtocol::Tcp); 278 | } 279 | PortScanType::TcpConnectScan => { 280 | capture_options 281 | .ip_protocols 282 | .insert(IpNextLevelProtocol::Tcp); 283 | } 284 | } 285 | let stop: Arc> = Arc::new(Mutex::new(false)); 286 | let stop_handle = Arc::clone(&stop); 287 | let packets: Arc>> = Arc::new(Mutex::new(vec![])); 288 | let receive_packets: Arc>> = Arc::clone(&packets); 289 | // Spawn pcap thread 290 | let pcap_handler = thread::spawn(move || { 291 | let packets: Vec = 292 | crate::pcap::start_capture(&mut rx, capture_options, &stop_handle); 293 | match receive_packets.lock() { 294 | Ok(mut receive_packets) => { 295 | for p in packets { 296 | receive_packets.push(p); 297 | } 298 | } 299 | Err(e) => { 300 | eprintln!("Failed to lock receive_packets: {}", e); 301 | } 302 | } 303 | }); 304 | // Wait for listener to start (need fix for better way) 305 | thread::sleep(Duration::from_millis(PCAP_WAIT_TIME_MILLIS)); 306 | let start_time = std::time::Instant::now(); 307 | // Send probe packets 308 | send_portscan_packets( 309 | &mut tx, 310 | &interface, 311 | scan_setting.targets.clone(), 312 | ptx, 313 | scan_setting.scan_type.clone(), 314 | ); 315 | thread::sleep(scan_setting.wait_time); 316 | // Stop pcap 317 | match stop.lock() { 318 | Ok(mut stop) => { 319 | *stop = true; 320 | } 321 | Err(e) => { 322 | eprintln!("Failed to lock stop: {}", e); 323 | } 324 | } 325 | // Wait for listener to stop 326 | match pcap_handler.join() { 327 | Ok(_) => {} 328 | Err(e) => { 329 | eprintln!("Failed to join pcap_handler: {:?}", e); 330 | } 331 | } 332 | let mut scan_result: ScanResult = ScanResult::new(); 333 | match packets.lock() { 334 | Ok(packets) => { 335 | scan_result = parse_portscan_result(packets.clone(), scan_setting); 336 | } 337 | Err(e) => { 338 | eprintln!("Failed to lock packets: {}", e); 339 | } 340 | } 341 | scan_result.scan_time = start_time.elapsed(); 342 | scan_result.scan_status = ScanStatus::Done; 343 | scan_result 344 | } 345 | -------------------------------------------------------------------------------- /src/scan/setting.rs: -------------------------------------------------------------------------------- 1 | use crate::host::Host; 2 | use crate::protocol::Protocol; 3 | use crate::scan::payload::PayloadBuilder; 4 | use rand::seq::SliceRandom; 5 | use std::collections::HashMap; 6 | use std::net::{IpAddr, Ipv4Addr}; 7 | use std::time::Duration; 8 | 9 | use crate::config::{DEFAULT_HOSTS_CONCURRENCY, DEFAULT_PORTS_CONCURRENCY}; 10 | 11 | use super::payload::PayloadInfo; 12 | 13 | /* /// Scan Type 14 | #[derive(Deserialize, Serialize, Clone, Debug)] 15 | pub enum ScanType { 16 | /// Port scan type. 17 | PortScan(PortScanType), 18 | /// Host scan type. 19 | HostScan(HostScanType), 20 | } 21 | */ 22 | 23 | /// Port Scan Type 24 | #[derive(Clone, Debug)] 25 | pub enum PortScanType { 26 | /// Default fast port scan type. 27 | /// 28 | /// Send TCP packet with SYN flag to the target ports and check response. 29 | TcpSynScan, 30 | /// Attempt TCP connection and check port status. 31 | /// 32 | /// Slow but can be run without administrator privileges. 33 | TcpConnectScan, 34 | } 35 | 36 | impl PortScanType { 37 | pub fn from_str(scan_type: &str) -> PortScanType { 38 | match scan_type { 39 | "SYN" | "TCP-SYN" | "TCP_SYN" => PortScanType::TcpSynScan, 40 | "CONNECT" | "TCP-CONNECT" | "TCP_CONNECT" => PortScanType::TcpConnectScan, 41 | _ => PortScanType::TcpSynScan, 42 | } 43 | } 44 | pub fn to_str(&self) -> &str { 45 | match self { 46 | PortScanType::TcpSynScan => "TCP-SYN", 47 | PortScanType::TcpConnectScan => "TCP-CONNECT", 48 | } 49 | } 50 | } 51 | 52 | #[derive(Clone, Debug)] 53 | pub struct PortScanSetting { 54 | pub if_index: u32, 55 | pub targets: Vec, 56 | pub protocol: Protocol, 57 | pub scan_type: PortScanType, 58 | pub concurrency: usize, 59 | pub timeout: Duration, 60 | pub wait_time: Duration, 61 | pub send_rate: Duration, 62 | pub randomize: bool, 63 | pub minimize_packet: bool, 64 | pub dns_map: HashMap, 65 | pub async_scan: bool, 66 | } 67 | 68 | impl Default for PortScanSetting { 69 | fn default() -> Self { 70 | Self { 71 | if_index: 0, 72 | targets: Vec::new(), 73 | protocol: Protocol::TCP, 74 | scan_type: PortScanType::TcpSynScan, 75 | concurrency: DEFAULT_PORTS_CONCURRENCY, 76 | timeout: Duration::from_secs(30), 77 | wait_time: Duration::from_secs(200), 78 | send_rate: Duration::from_millis(0), 79 | randomize: true, 80 | minimize_packet: false, 81 | dns_map: HashMap::new(), 82 | async_scan: false, 83 | } 84 | } 85 | } 86 | 87 | impl PortScanSetting { 88 | // support builder pattern for all fields 89 | pub fn set_if_index(mut self, if_index: u32) -> Self { 90 | self.if_index = if_index; 91 | self 92 | } 93 | pub fn add_target(mut self, target: Host) -> Self { 94 | self.targets.push(target); 95 | self 96 | } 97 | pub fn set_targets(mut self, targets: Vec) -> Self { 98 | self.targets = targets; 99 | self 100 | } 101 | pub fn set_protocol(mut self, protocol: Protocol) -> Self { 102 | self.protocol = protocol; 103 | self 104 | } 105 | pub fn set_scan_type(mut self, scan_type: PortScanType) -> Self { 106 | self.scan_type = scan_type; 107 | self 108 | } 109 | pub fn set_concurrency(mut self, concurrency: usize) -> Self { 110 | self.concurrency = concurrency; 111 | self 112 | } 113 | pub fn set_timeout(mut self, timeout: Duration) -> Self { 114 | self.timeout = timeout; 115 | self 116 | } 117 | pub fn set_wait_time(mut self, wait_time: Duration) -> Self { 118 | self.wait_time = wait_time; 119 | self 120 | } 121 | pub fn set_send_rate(mut self, send_rate: Duration) -> Self { 122 | self.send_rate = send_rate; 123 | self 124 | } 125 | pub fn set_randomize(mut self, randomize: bool) -> Self { 126 | self.randomize = randomize; 127 | self 128 | } 129 | pub fn set_minimize_packet(mut self, minimize_packet: bool) -> Self { 130 | self.minimize_packet = minimize_packet; 131 | self 132 | } 133 | pub fn set_dns_map(mut self, dns_map: HashMap) -> Self { 134 | self.dns_map = dns_map; 135 | self 136 | } 137 | pub fn set_async_scan(mut self, async_scan: bool) -> Self { 138 | self.async_scan = async_scan; 139 | self 140 | } 141 | pub fn randomize_hosts(&mut self) { 142 | let mut rng = rand::thread_rng(); 143 | self.targets.shuffle(&mut rng); 144 | } 145 | pub fn randomize_ports(&mut self) { 146 | for target in &mut self.targets { 147 | target.ports.shuffle(&mut rand::thread_rng()); 148 | } 149 | } 150 | } 151 | 152 | /// Host Scan Type 153 | #[derive(Clone, Debug)] 154 | pub enum HostScanType { 155 | /// Default host scan type. 156 | /// 157 | /// Send ICMP echo request and check response. 158 | IcmpPingScan, 159 | /// Perform host scan for a specific service. 160 | /// 161 | /// Send TCP packets with SYN flag to a specific port and check response. 162 | TcpPingScan, 163 | /// Send UDP packets to a probably closed port and check response. 164 | /// This expects ICMP port unreachable message. 165 | UdpPingScan, 166 | } 167 | 168 | impl HostScanType { 169 | pub fn from_str(scan_type: &str) -> HostScanType { 170 | match scan_type { 171 | "ICMP" | "ICMP-PING" | "ICMP_PING" => HostScanType::IcmpPingScan, 172 | "TCP" | "TCP-PING" | "TCP_PING" => HostScanType::TcpPingScan, 173 | "UDP" | "UDP-PING" | "UDP_PING" => HostScanType::UdpPingScan, 174 | _ => HostScanType::IcmpPingScan, 175 | } 176 | } 177 | pub fn to_str(&self) -> &str { 178 | match self { 179 | HostScanType::IcmpPingScan => "ICMP-PING", 180 | HostScanType::TcpPingScan => "TCP-PING", 181 | HostScanType::UdpPingScan => "UDP-PING", 182 | } 183 | } 184 | } 185 | 186 | #[derive(Clone, Debug)] 187 | pub struct HostScanSetting { 188 | pub if_index: u32, 189 | pub targets: Vec, 190 | pub protocol: Protocol, 191 | pub scan_type: HostScanType, 192 | pub concurrency: usize, 193 | pub timeout: Duration, 194 | pub wait_time: Duration, 195 | pub send_rate: Duration, 196 | pub randomize: bool, 197 | pub minimize_packet: bool, 198 | pub dns_map: HashMap, 199 | pub async_scan: bool, 200 | } 201 | 202 | impl Default for HostScanSetting { 203 | fn default() -> Self { 204 | Self { 205 | if_index: 0, 206 | targets: Vec::new(), 207 | protocol: Protocol::ICMP, 208 | scan_type: HostScanType::IcmpPingScan, 209 | concurrency: DEFAULT_HOSTS_CONCURRENCY, 210 | timeout: Duration::from_secs(30), 211 | wait_time: Duration::from_secs(200), 212 | send_rate: Duration::from_millis(0), 213 | randomize: true, 214 | minimize_packet: false, 215 | dns_map: HashMap::new(), 216 | async_scan: false, 217 | } 218 | } 219 | } 220 | 221 | impl HostScanSetting { 222 | // support builder pattern for all fields 223 | pub fn set_if_index(mut self, if_index: u32) -> Self { 224 | self.if_index = if_index; 225 | self 226 | } 227 | pub fn set_targets(mut self, targets: Vec) -> Self { 228 | self.targets = targets; 229 | self 230 | } 231 | pub fn set_protocol(mut self, protocol: Protocol) -> Self { 232 | self.protocol = protocol; 233 | self 234 | } 235 | pub fn set_scan_type(mut self, scan_type: HostScanType) -> Self { 236 | self.scan_type = scan_type; 237 | self 238 | } 239 | pub fn set_concurrency(mut self, concurrency: usize) -> Self { 240 | self.concurrency = concurrency; 241 | self 242 | } 243 | pub fn set_timeout(mut self, timeout: Duration) -> Self { 244 | self.timeout = timeout; 245 | self 246 | } 247 | pub fn set_wait_time(mut self, wait_time: Duration) -> Self { 248 | self.wait_time = wait_time; 249 | self 250 | } 251 | pub fn set_send_rate(mut self, send_rate: Duration) -> Self { 252 | self.send_rate = send_rate; 253 | self 254 | } 255 | pub fn set_randomize(mut self, randomize: bool) -> Self { 256 | self.randomize = randomize; 257 | self 258 | } 259 | pub fn set_minimize_packet(mut self, minimize_packet: bool) -> Self { 260 | self.minimize_packet = minimize_packet; 261 | self 262 | } 263 | pub fn set_dns_map(mut self, dns_map: HashMap) -> Self { 264 | self.dns_map = dns_map; 265 | self 266 | } 267 | pub fn add_target(&mut self, target: Host) { 268 | self.targets.push(target); 269 | } 270 | pub fn set_async_scan(mut self, async_scan: bool) -> Self { 271 | self.async_scan = async_scan; 272 | self 273 | } 274 | pub fn randomize_hosts(&mut self) { 275 | let mut rng = rand::thread_rng(); 276 | self.targets.shuffle(&mut rng); 277 | } 278 | pub fn randomize_ports(&mut self) { 279 | for target in &mut self.targets { 280 | target.ports.shuffle(&mut rand::thread_rng()); 281 | } 282 | } 283 | } 284 | 285 | /// Probe setting for service detection 286 | #[derive(Clone, Debug)] 287 | pub struct ServiceProbeSetting { 288 | /// Destination IP address 289 | pub ip_addr: IpAddr, 290 | /// Destination Host Name 291 | pub hostname: String, 292 | /// Target ports for service detection 293 | pub ports: Vec, 294 | /// TCP connect (open) timeout 295 | pub connect_timeout: Duration, 296 | /// TCP read timeout 297 | pub read_timeout: Duration, 298 | /// SSL/TLS certificate validation when detecting HTTPS services. 299 | /// 300 | /// Default value is false, which means validation is enabled. 301 | pub accept_invalid_certs: bool, 302 | /// Payloads for specified ports. 303 | /// 304 | /// If not set, default null probe will be used. (No payload, just open TCP connection and read response) 305 | pub payload_map: HashMap, 306 | /// Concurrent connection limit for service detection 307 | pub concurrent_limit: usize, 308 | } 309 | 310 | impl ServiceProbeSetting { 311 | /// Create new ProbeSetting 312 | pub fn new() -> ServiceProbeSetting { 313 | ServiceProbeSetting { 314 | ip_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), 315 | hostname: String::new(), 316 | ports: vec![], 317 | connect_timeout: Duration::from_millis(200), 318 | read_timeout: Duration::from_secs(5), 319 | accept_invalid_certs: false, 320 | payload_map: HashMap::new(), 321 | concurrent_limit: 10, 322 | } 323 | } 324 | pub fn default(ip_addr: IpAddr, hostname: String, ports: Vec) -> ServiceProbeSetting { 325 | let mut payload_map: HashMap = HashMap::new(); 326 | let http_head = PayloadBuilder::http_head(); 327 | let https_head = PayloadBuilder::https_head(&hostname); 328 | payload_map.insert(80, http_head.clone()); 329 | payload_map.insert(443, https_head.clone()); 330 | payload_map.insert(8080, http_head); 331 | payload_map.insert(8443, https_head); 332 | ServiceProbeSetting { 333 | ip_addr: ip_addr, 334 | hostname: hostname, 335 | ports: ports, 336 | connect_timeout: Duration::from_secs(1), 337 | read_timeout: Duration::from_secs(5), 338 | accept_invalid_certs: false, 339 | payload_map: payload_map, 340 | concurrent_limit: 10, 341 | } 342 | } 343 | /// Set Destination IP address 344 | pub fn with_ip_addr(&mut self, ip_addr: IpAddr) -> &mut Self { 345 | self.ip_addr = ip_addr; 346 | self 347 | } 348 | /// Set Destination Host Name. If IP address is not set, it will be resolved from the hostname. 349 | pub fn with_hostname(&mut self, hostname: String) -> &mut Self { 350 | self.hostname = hostname; 351 | if self.ip_addr == IpAddr::V4(Ipv4Addr::LOCALHOST) 352 | || self.ip_addr == IpAddr::V4(Ipv4Addr::UNSPECIFIED) 353 | || self.ip_addr == IpAddr::V6(std::net::Ipv6Addr::LOCALHOST) 354 | || self.ip_addr == IpAddr::V6(std::net::Ipv6Addr::UNSPECIFIED) 355 | { 356 | if let Some(ip_addr) = crate::dns::lookup_host_name(&self.hostname) { 357 | self.ip_addr = ip_addr; 358 | } 359 | } 360 | self 361 | } 362 | /// Add target port 363 | pub fn add_port(&mut self, port: u16) { 364 | self.ports.push(port); 365 | } 366 | /// Set connect (open) timeout in milliseconds 367 | pub fn set_connect_timeout_millis(&mut self, connect_timeout_millis: u64) { 368 | self.connect_timeout = Duration::from_millis(connect_timeout_millis); 369 | } 370 | /// Set TCP read timeout in milliseconds 371 | pub fn set_read_timeout_millis(&mut self, read_timeout_millis: u64) { 372 | self.read_timeout = Duration::from_millis(read_timeout_millis); 373 | } 374 | } 375 | -------------------------------------------------------------------------------- /src/scan/result.rs: -------------------------------------------------------------------------------- 1 | use netdev::mac::MacAddr; 2 | use netdev::Interface; 3 | use nex::packet::tcp::TcpFlags; 4 | 5 | use crate::host::{Host, Port, PortStatus}; 6 | use crate::packet::frame::PacketFrame; 7 | use std::collections::HashSet; 8 | use std::net::{IpAddr, SocketAddr}; 9 | use std::time::Duration; 10 | 11 | use super::setting::{HostScanSetting, HostScanType, PortScanSetting}; 12 | 13 | /// Status of scan task 14 | #[derive(Clone, Debug, PartialEq)] 15 | pub enum ScanStatus { 16 | Done, 17 | Timeout, 18 | Error(String), 19 | } 20 | 21 | /// Result of scan 22 | #[derive(Clone, Debug)] 23 | pub struct ScanResult { 24 | /// List of scanned Host info and their respective ports 25 | pub hosts: Vec, 26 | /// Time taken to scan 27 | pub scan_time: Duration, 28 | /// Status of the scan task 29 | pub scan_status: ScanStatus, 30 | /// Captured packet fingerprints 31 | pub fingerprints: Vec, 32 | } 33 | 34 | impl ScanResult { 35 | pub fn new() -> ScanResult { 36 | ScanResult { 37 | hosts: vec![], 38 | scan_time: Duration::from_millis(0), 39 | scan_status: ScanStatus::Done, 40 | fingerprints: vec![], 41 | } 42 | } 43 | pub fn error(message: String) -> ScanResult { 44 | ScanResult { 45 | hosts: vec![], 46 | scan_time: Duration::from_millis(0), 47 | scan_status: ScanStatus::Error(message), 48 | fingerprints: vec![], 49 | } 50 | } 51 | /// Returns IP addresses from the scan result 52 | pub fn get_hosts(&self) -> Vec { 53 | let mut hosts: Vec = vec![]; 54 | for host in self.hosts.clone() { 55 | hosts.push(host.ip_addr); 56 | } 57 | hosts 58 | } 59 | /// Get open ports of the specified IP address from the scan results 60 | pub fn get_open_port_numbers(&self, ip_addr: IpAddr) -> Vec { 61 | let mut open_ports: Vec = vec![]; 62 | self.hosts.iter().for_each(|host_info| { 63 | if host_info.ip_addr == ip_addr { 64 | host_info 65 | .ports 66 | .iter() 67 | .for_each(|port_info| match port_info.status { 68 | PortStatus::Open => { 69 | open_ports.push(port_info.number); 70 | } 71 | _ => {} 72 | }); 73 | } 74 | }); 75 | open_ports 76 | } 77 | /// Get open port fingerprint 78 | pub fn get_syn_ack_fingerprint(&self, ip_addr: IpAddr, port: u16) -> Option { 79 | for fingerprint in self.fingerprints.iter() { 80 | if let Some(ipv4_packet) = &fingerprint.ipv4_header { 81 | if ipv4_packet.source == ip_addr { 82 | if let Some(tcp_packet) = &fingerprint.tcp_header { 83 | if tcp_packet.source == port 84 | && tcp_packet.flags == TcpFlags::SYN | TcpFlags::ACK 85 | { 86 | return Some(fingerprint.clone()); 87 | } 88 | } 89 | } 90 | } else if let Some(ipv6_packet) = &fingerprint.ipv6_header { 91 | if ipv6_packet.source == ip_addr { 92 | if let Some(tcp_packet) = &fingerprint.tcp_header { 93 | if tcp_packet.source == port 94 | && tcp_packet.flags == TcpFlags::SYN | TcpFlags::ACK 95 | { 96 | return Some(fingerprint.clone()); 97 | } 98 | } 99 | } 100 | } 101 | } 102 | None 103 | } 104 | pub fn get_host(&self, ip_addr: IpAddr) -> Option { 105 | for host in self.hosts.iter() { 106 | if host.ip_addr == ip_addr { 107 | return Some(host.clone()); 108 | } 109 | } 110 | None 111 | } 112 | pub fn sort_hosts(&mut self) { 113 | self.hosts.sort_by(|a, b| a.ip_addr.cmp(&b.ip_addr)); 114 | } 115 | pub fn sort_ports(&mut self) { 116 | for host in self.hosts.iter_mut() { 117 | host.ports.sort_by(|a, b| a.number.cmp(&b.number)); 118 | } 119 | } 120 | } 121 | 122 | /// Result of a service probe 123 | #[derive(Clone, Debug, PartialEq)] 124 | pub struct ServiceProbeResult { 125 | pub port: u16, 126 | pub service_name: String, 127 | pub service_detail: Option, 128 | pub response: Vec, 129 | pub error: Option, 130 | } 131 | 132 | impl ServiceProbeResult { 133 | /// Create a new successful probe result 134 | pub fn new(port: u16, service_name: String, response: Vec) -> Self { 135 | ServiceProbeResult { 136 | port, 137 | service_name, 138 | service_detail: None, 139 | response, 140 | error: None, 141 | } 142 | } 143 | 144 | /// Create a new probe result with an error 145 | pub fn with_error(port: u16, service_name: String, error: ServiceProbeError) -> Self { 146 | ServiceProbeResult { 147 | port, 148 | service_name, 149 | service_detail: None, 150 | response: Vec::new(), 151 | error: Some(error), 152 | } 153 | } 154 | 155 | /// Check if the result contains an error 156 | pub fn has_error(&self) -> bool { 157 | self.error.is_some() 158 | } 159 | 160 | /// Get a reference to the contained error, if any 161 | pub fn error(&self) -> Option<&ServiceProbeError> { 162 | self.error.as_ref() 163 | } 164 | 165 | /// Extract the error, consuming the result 166 | pub fn into_error(self) -> Option { 167 | self.error 168 | } 169 | } 170 | 171 | #[derive(Clone, Debug, PartialEq)] 172 | pub enum ServiceProbeError { 173 | ConnectionError(String), 174 | WriteError(String), 175 | ReadError(String), 176 | TlsError(String), 177 | CustomError(String), 178 | } 179 | 180 | pub(crate) fn parse_hostscan_result( 181 | packets: Vec, 182 | scan_setting: HostScanSetting, 183 | ) -> ScanResult { 184 | let mut result: ScanResult = ScanResult::new(); 185 | let iface: Interface = match crate::interface::get_interface_by_index(scan_setting.if_index) { 186 | Some(iface) => iface, 187 | None => return ScanResult::error("Interface not found".to_string()), 188 | }; 189 | let iface_ips: HashSet = crate::interface::get_local_ips(scan_setting.if_index); 190 | for p in packets { 191 | let mac_addr: MacAddr; 192 | if let Some(ethernet_frame) = &p.ethernet_header { 193 | if ethernet_frame.destination != iface.mac_addr.unwrap_or(MacAddr::zero()) { 194 | continue; 195 | } 196 | mac_addr = ethernet_frame.source; 197 | } else { 198 | mac_addr = MacAddr::zero(); 199 | } 200 | let mut ports: Vec = vec![]; 201 | match scan_setting.scan_type { 202 | HostScanType::IcmpPingScan => { 203 | if p.icmp_header.is_none() && p.icmpv6_header.is_none() { 204 | continue; 205 | } 206 | } 207 | HostScanType::TcpPingScan => { 208 | if p.tcp_header.is_none() { 209 | continue; 210 | } 211 | if let Some(tcp_packet) = &p.tcp_header { 212 | if tcp_packet.flags == TcpFlags::SYN | TcpFlags::ACK { 213 | let port_info: Port = Port { 214 | number: tcp_packet.source, 215 | status: PortStatus::Open, 216 | service_name: String::new(), 217 | service_version: String::new(), 218 | }; 219 | ports.push(port_info); 220 | } else if tcp_packet.flags == TcpFlags::RST | TcpFlags::ACK { 221 | let port_info: Port = Port { 222 | number: tcp_packet.source, 223 | status: PortStatus::Closed, 224 | service_name: String::new(), 225 | service_version: String::new(), 226 | }; 227 | ports.push(port_info); 228 | } else { 229 | continue; 230 | } 231 | } else { 232 | continue; 233 | } 234 | } 235 | HostScanType::UdpPingScan => { 236 | if p.icmp_header.is_none() && p.icmp_header.is_none() { 237 | continue; 238 | } 239 | } 240 | } 241 | let host_info: Host = if let Some(ipv4_packet) = &p.ipv4_header { 242 | Host { 243 | ip_addr: IpAddr::V4(ipv4_packet.source), 244 | hostname: scan_setting 245 | .dns_map 246 | .get(&IpAddr::V4(ipv4_packet.source)) 247 | .unwrap_or(&String::new()) 248 | .clone(), 249 | ports: ports, 250 | mac_addr: if iface_ips.contains(&IpAddr::V4(ipv4_packet.source)) { 251 | iface.mac_addr.unwrap_or(MacAddr::zero()) 252 | } else { 253 | mac_addr 254 | }, 255 | ttl: ipv4_packet.ttl, 256 | } 257 | } else if let Some(ipv6_packet) = &p.ipv6_header { 258 | Host { 259 | ip_addr: IpAddr::V6(ipv6_packet.source), 260 | hostname: scan_setting 261 | .dns_map 262 | .get(&IpAddr::V6(ipv6_packet.source)) 263 | .unwrap_or(&String::new()) 264 | .clone(), 265 | ports: ports, 266 | mac_addr: if iface_ips.contains(&IpAddr::V6(ipv6_packet.source)) { 267 | iface.mac_addr.unwrap_or(MacAddr::zero()) 268 | } else { 269 | mac_addr 270 | }, 271 | ttl: ipv6_packet.hop_limit, 272 | } 273 | } else { 274 | continue; 275 | }; 276 | if !result.hosts.contains(&host_info) { 277 | result.hosts.push(host_info); 278 | result.fingerprints.push(p.clone()); 279 | } 280 | } 281 | return result; 282 | } 283 | 284 | pub(crate) fn parse_portscan_result( 285 | packets: Vec, 286 | scan_setting: PortScanSetting, 287 | ) -> ScanResult { 288 | let mut result: ScanResult = ScanResult::new(); 289 | let mut socket_set: HashSet = HashSet::new(); 290 | let iface: Interface = match crate::interface::get_interface_by_index(scan_setting.if_index) { 291 | Some(iface) => iface, 292 | None => return ScanResult::error("Interface not found".to_string()), 293 | }; 294 | for p in packets { 295 | if p.ipv4_header.is_none() && p.ipv6_header.is_none() { 296 | continue; 297 | } 298 | let mac_addr: MacAddr; 299 | if let Some(ethernet_frame) = &p.ethernet_header { 300 | if ethernet_frame.destination != iface.mac_addr.unwrap_or(MacAddr::zero()) { 301 | continue; 302 | } 303 | mac_addr = ethernet_frame.source; 304 | } else { 305 | mac_addr = MacAddr::zero(); 306 | } 307 | let ip_addr: IpAddr = { 308 | if let Some(ipv4_packet) = &p.ipv4_header { 309 | if let Some(tcp_packet) = &p.tcp_header { 310 | if socket_set.contains(&SocketAddr::new( 311 | IpAddr::V4(ipv4_packet.source), 312 | tcp_packet.source, 313 | )) { 314 | continue; 315 | } 316 | } else { 317 | continue; 318 | } 319 | IpAddr::V4(ipv4_packet.source) 320 | } else if let Some(ipv6_packet) = &p.ipv6_header { 321 | if let Some(tcp_packet) = &p.tcp_header { 322 | if socket_set.contains(&SocketAddr::new( 323 | IpAddr::V6(ipv6_packet.source), 324 | tcp_packet.source, 325 | )) { 326 | continue; 327 | } 328 | } else { 329 | continue; 330 | } 331 | IpAddr::V6(ipv6_packet.source) 332 | } else { 333 | continue; 334 | } 335 | }; 336 | let ttl = if let Some(ipv4_packet) = &p.ipv4_header { 337 | ipv4_packet.ttl 338 | } else if let Some(ipv6_packet) = &p.ipv6_header { 339 | ipv6_packet.hop_limit 340 | } else { 341 | 0 342 | }; 343 | let port_info: Port = if let Some(tcp_packet) = &p.tcp_header { 344 | if tcp_packet.flags == TcpFlags::SYN | TcpFlags::ACK { 345 | Port { 346 | number: tcp_packet.source, 347 | status: PortStatus::Open, 348 | service_name: String::new(), 349 | service_version: String::new(), 350 | } 351 | } else if tcp_packet.flags == TcpFlags::RST | TcpFlags::ACK { 352 | Port { 353 | number: tcp_packet.source, 354 | status: PortStatus::Closed, 355 | service_name: String::new(), 356 | service_version: String::new(), 357 | } 358 | } else { 359 | continue; 360 | } 361 | } else { 362 | continue; 363 | }; 364 | let mut exists: bool = false; 365 | for host in result.hosts.iter_mut() { 366 | if host.ip_addr == ip_addr { 367 | host.ports.push(port_info.clone()); 368 | exists = true; 369 | } 370 | } 371 | if !exists { 372 | let host_info: Host = Host { 373 | ip_addr: ip_addr, 374 | hostname: scan_setting 375 | .dns_map 376 | .get(&ip_addr) 377 | .unwrap_or(&String::new()) 378 | .clone(), 379 | ports: vec![port_info.clone()], 380 | mac_addr: mac_addr, 381 | ttl: ttl, 382 | }; 383 | result.hosts.push(host_info); 384 | } 385 | result.fingerprints.push(p.clone()); 386 | socket_set.insert(SocketAddr::new(ip_addr, port_info.number)); 387 | } 388 | result 389 | } 390 | -------------------------------------------------------------------------------- /src/scan/async_io.rs: -------------------------------------------------------------------------------- 1 | use futures::stream::{self, StreamExt}; 2 | use netdev::Interface; 3 | use nex::socket::{AsyncSocket, IpVersion, SocketOption, SocketType}; 4 | use std::net::{IpAddr, SocketAddr}; 5 | use std::sync::mpsc::{self, Sender}; 6 | use std::sync::{Arc, Mutex}; 7 | use std::time::Duration; 8 | 9 | use crate::host::{Host, Port, PortStatus}; 10 | 11 | use super::packet::{build_hostscan_ip_next_packet, build_portscan_ip_next_packet}; 12 | use super::result::ScanResult; 13 | use super::setting::{HostScanSetting, PortScanSetting}; 14 | 15 | use crate::config::PCAP_WAIT_TIME_MILLIS; 16 | use crate::packet::frame::PacketFrame; 17 | use crate::pcap::PacketCaptureOptions; 18 | use nex::packet::ip::IpNextLevelProtocol; 19 | use std::collections::HashSet; 20 | use std::thread; 21 | 22 | use super::result::{parse_hostscan_result, parse_portscan_result, ScanStatus}; 23 | use super::setting::{HostScanType, PortScanType}; 24 | 25 | pub(crate) async fn send_portscan_packets( 26 | interface: &Interface, 27 | socket: &AsyncSocket, 28 | scan_setting: &PortScanSetting, 29 | ptx: &Arc>>, 30 | ) { 31 | let fut_host = stream::iter(scan_setting.targets.clone()).for_each_concurrent( 32 | scan_setting.concurrency, 33 | |dst| async move { 34 | let fut_port = stream::iter(dst.get_ports()).for_each_concurrent( 35 | scan_setting.concurrency, 36 | |port| { 37 | let target = dst.clone(); 38 | let dst_socket_addr: SocketAddr = SocketAddr::new(target.ip_addr, port); 39 | async move { 40 | let packet_bytes: Vec = 41 | build_portscan_ip_next_packet(&interface, target.ip_addr, port); 42 | match socket.send_to(&packet_bytes, dst_socket_addr).await { 43 | Ok(_) => {} 44 | Err(_) => {} 45 | } 46 | match ptx.lock() { 47 | Ok(lr) => match lr.send(dst_socket_addr) { 48 | Ok(_) => {} 49 | Err(_) => {} 50 | }, 51 | Err(_) => {} 52 | } 53 | //thread::sleep(scan_setting.send_rate); 54 | } 55 | }, 56 | ); 57 | fut_port.await; 58 | }, 59 | ); 60 | fut_host.await; 61 | } 62 | 63 | pub(crate) async fn send_hostscan_packets( 64 | interface: &Interface, 65 | scan_setting: &HostScanSetting, 66 | ptx: &Arc>>, 67 | ) { 68 | let fut_host = stream::iter(scan_setting.targets.clone()).for_each_concurrent( 69 | scan_setting.concurrency, 70 | |dst| async move { 71 | let socket: AsyncSocket = match scan_setting.scan_type { 72 | HostScanType::IcmpPingScan => match dst.ip_addr { 73 | IpAddr::V4(_) => { 74 | let socket_option = SocketOption { 75 | ip_version: IpVersion::V4, 76 | socket_type: SocketType::Raw, 77 | protocol: Some(IpNextLevelProtocol::Icmp), 78 | non_blocking: true, 79 | }; 80 | AsyncSocket::new(socket_option).unwrap() 81 | } 82 | IpAddr::V6(_) => { 83 | let socket_option = SocketOption { 84 | ip_version: IpVersion::V6, 85 | socket_type: SocketType::Raw, 86 | protocol: Some(IpNextLevelProtocol::Icmpv6), 87 | non_blocking: true, 88 | }; 89 | AsyncSocket::new(socket_option).unwrap() 90 | } 91 | }, 92 | HostScanType::TcpPingScan => { 93 | let socket_option = SocketOption { 94 | ip_version: if dst.ip_addr.is_ipv4() { 95 | IpVersion::V4 96 | } else { 97 | IpVersion::V6 98 | }, 99 | socket_type: SocketType::Raw, 100 | protocol: Some(IpNextLevelProtocol::Tcp), 101 | non_blocking: true, 102 | }; 103 | AsyncSocket::new(socket_option).unwrap() 104 | } 105 | HostScanType::UdpPingScan => { 106 | let socket_option = SocketOption { 107 | ip_version: if dst.ip_addr.is_ipv4() { 108 | IpVersion::V4 109 | } else { 110 | IpVersion::V6 111 | }, 112 | socket_type: SocketType::Raw, 113 | protocol: Some(IpNextLevelProtocol::Udp), 114 | non_blocking: true, 115 | }; 116 | AsyncSocket::new(socket_option).unwrap() 117 | } 118 | }; 119 | let dst_socket_addr: SocketAddr = SocketAddr::new(dst.ip_addr, 0); 120 | let packet_bytes = 121 | build_hostscan_ip_next_packet(&interface, &dst, &scan_setting.scan_type); 122 | match socket.send_to(&packet_bytes, dst_socket_addr).await { 123 | Ok(_) => {} 124 | Err(_) => {} 125 | } 126 | match ptx.lock() { 127 | Ok(lr) => match lr.send(dst) { 128 | Ok(_) => {} 129 | Err(_) => {} 130 | }, 131 | Err(_) => {} 132 | } 133 | //thread::sleep(scan_setting.send_rate); 134 | }, 135 | ); 136 | fut_host.await; 137 | } 138 | 139 | pub async fn try_connect_ports( 140 | target: Host, 141 | concurrency: usize, 142 | timeout: Duration, 143 | ptx: &Arc>>, 144 | ) -> Host { 145 | let (channel_tx, channel_rx) = mpsc::channel(); 146 | let fut = stream::iter(target.get_ports()).for_each_concurrent(concurrency, |port| { 147 | let channel_tx = channel_tx.clone(); 148 | async move { 149 | let socket_addr: SocketAddr = SocketAddr::new(target.ip_addr, port); 150 | match AsyncSocket::new_with_async_connect_timeout(&socket_addr, timeout).await { 151 | Ok(async_socket) => { 152 | let _ = channel_tx.send(port); 153 | match async_socket.shutdown(std::net::Shutdown::Both).await { 154 | Ok(_) => {} 155 | Err(_) => {} 156 | } 157 | } 158 | Err(_) => {} 159 | } 160 | match ptx.lock() { 161 | Ok(lr) => match lr.send(socket_addr) { 162 | Ok(_) => {} 163 | Err(_) => {} 164 | }, 165 | Err(_) => {} 166 | } 167 | } 168 | }); 169 | fut.await; 170 | drop(channel_tx); 171 | let mut open_ports: Vec = vec![]; 172 | loop { 173 | match channel_rx.recv() { 174 | Ok(port) => { 175 | open_ports.push(Port { 176 | number: port, 177 | status: PortStatus::Open, 178 | service_name: String::new(), 179 | service_version: String::new(), 180 | }); 181 | } 182 | Err(_) => { 183 | break; 184 | } 185 | } 186 | } 187 | Host { 188 | ip_addr: target.ip_addr, 189 | hostname: target.hostname, 190 | ports: open_ports, 191 | mac_addr: target.mac_addr, 192 | ttl: target.ttl, 193 | } 194 | } 195 | 196 | pub fn run_connect_scan( 197 | scan_setting: PortScanSetting, 198 | ptx: &Arc>>, 199 | ) -> ScanResult { 200 | let rt = tokio::runtime::Runtime::new().unwrap(); 201 | let result = rt.block_on(async { 202 | let start_time = std::time::Instant::now(); 203 | let mut tasks = vec![]; 204 | for target in scan_setting.targets { 205 | let ptx = ptx.clone(); 206 | tasks.push(tokio::spawn(async move { 207 | let host = 208 | try_connect_ports(target, scan_setting.concurrency, scan_setting.timeout, &ptx) 209 | .await; 210 | host 211 | })); 212 | } 213 | let mut hosts: Vec = vec![]; 214 | for task in tasks { 215 | match task.await { 216 | Ok(host) => { 217 | hosts.push(host); 218 | } 219 | Err(e) => { 220 | println!("error: {}", e); 221 | } 222 | } 223 | } 224 | let mut result = ScanResult::new(); 225 | result.hosts = hosts; 226 | result.scan_time = start_time.elapsed(); 227 | result.scan_status = crate::scan::result::ScanStatus::Done; 228 | result 229 | }); 230 | result 231 | } 232 | 233 | pub(crate) async fn scan_hosts( 234 | scan_setting: HostScanSetting, 235 | ptx: &Arc>>, 236 | ) -> ScanResult { 237 | let interface = match crate::interface::get_interface_by_index(scan_setting.if_index) { 238 | Some(interface) => interface, 239 | None => return ScanResult::new(), 240 | }; 241 | // Create sender 242 | let config = nex::datalink::Config { 243 | write_buffer_size: 4096, 244 | read_buffer_size: 4096, 245 | read_timeout: Some(scan_setting.wait_time), 246 | write_timeout: None, 247 | channel_type: nex::datalink::ChannelType::Layer2, 248 | bpf_fd_attempts: 1000, 249 | linux_fanout: None, 250 | promiscuous: false, 251 | }; 252 | let (mut _tx, mut rx) = match nex::datalink::channel(&interface, config) { 253 | Ok(nex::datalink::Channel::Ethernet(tx, rx)) => (tx, rx), 254 | Ok(_) => return ScanResult::error("Unhandled channel type".to_string()), 255 | Err(e) => return ScanResult::error(format!("Failed to create channel: {}", e)), 256 | }; 257 | let mut capture_options: PacketCaptureOptions = PacketCaptureOptions { 258 | interface_index: interface.index, 259 | src_ips: HashSet::new(), 260 | dst_ips: HashSet::new(), 261 | src_ports: HashSet::new(), 262 | dst_ports: HashSet::new(), 263 | ether_types: HashSet::new(), 264 | ip_protocols: HashSet::new(), 265 | capture_timeout: scan_setting.timeout, 266 | tunnel: interface.is_tun(), 267 | loopback: interface.is_loopback(), 268 | }; 269 | for target in scan_setting.targets.clone() { 270 | capture_options.src_ips.insert(target.ip_addr); 271 | } 272 | match scan_setting.scan_type { 273 | HostScanType::IcmpPingScan => { 274 | capture_options 275 | .ip_protocols 276 | .insert(IpNextLevelProtocol::Icmp); 277 | capture_options 278 | .ip_protocols 279 | .insert(IpNextLevelProtocol::Icmpv6); 280 | } 281 | HostScanType::TcpPingScan => { 282 | capture_options 283 | .ip_protocols 284 | .insert(IpNextLevelProtocol::Tcp); 285 | for target in scan_setting.targets.clone() { 286 | for port in target.get_ports() { 287 | capture_options.src_ports.insert(port); 288 | } 289 | } 290 | } 291 | HostScanType::UdpPingScan => { 292 | capture_options 293 | .ip_protocols 294 | .insert(IpNextLevelProtocol::Udp); 295 | capture_options 296 | .ip_protocols 297 | .insert(IpNextLevelProtocol::Icmp); 298 | capture_options 299 | .ip_protocols 300 | .insert(IpNextLevelProtocol::Icmpv6); 301 | } 302 | } 303 | let stop: Arc> = Arc::new(Mutex::new(false)); 304 | let stop_handle = Arc::clone(&stop); 305 | let packets: Arc>> = Arc::new(Mutex::new(vec![])); 306 | let receive_packets: Arc>> = Arc::clone(&packets); 307 | // Spawn pcap thread 308 | let pcap_handler = thread::spawn(move || { 309 | let packets: Vec = 310 | crate::pcap::start_capture(&mut rx, capture_options, &stop_handle); 311 | match receive_packets.lock() { 312 | Ok(mut receive_packets) => { 313 | for p in packets { 314 | receive_packets.push(p); 315 | } 316 | } 317 | Err(e) => { 318 | eprintln!("Failed to lock receive_packets: {}", e); 319 | } 320 | } 321 | }); 322 | 323 | // Wait for listener to start (need fix for better way) 324 | thread::sleep(Duration::from_millis(PCAP_WAIT_TIME_MILLIS)); 325 | let start_time = std::time::Instant::now(); 326 | // Send probe packets 327 | send_hostscan_packets(&interface, &scan_setting, ptx).await; 328 | thread::sleep(scan_setting.wait_time); 329 | // Stop pcap 330 | match stop.lock() { 331 | Ok(mut stop) => { 332 | *stop = true; 333 | } 334 | Err(e) => { 335 | eprintln!("Failed to lock stop: {}", e); 336 | } 337 | } 338 | // Wait for listener to stop 339 | match pcap_handler.join() { 340 | Ok(_) => {} 341 | Err(e) => { 342 | eprintln!("Failed to join pcap_handler: {:?}", e); 343 | } 344 | } 345 | 346 | let mut scan_result: ScanResult = ScanResult::new(); 347 | match packets.lock() { 348 | Ok(packets) => { 349 | scan_result = parse_hostscan_result(packets.clone(), scan_setting); 350 | } 351 | Err(e) => { 352 | eprintln!("Failed to lock packets: {}", e); 353 | } 354 | } 355 | scan_result.scan_time = start_time.elapsed(); 356 | scan_result.scan_status = ScanStatus::Done; 357 | scan_result 358 | } 359 | 360 | pub(crate) async fn scan_ports( 361 | scan_setting: PortScanSetting, 362 | ptx: &Arc>>, 363 | ) -> ScanResult { 364 | let interface = match crate::interface::get_interface_by_index(scan_setting.if_index) { 365 | Some(interface) => interface, 366 | None => return ScanResult::new(), 367 | }; 368 | // Create sender 369 | let config = nex::datalink::Config { 370 | write_buffer_size: 4096, 371 | read_buffer_size: 4096, 372 | read_timeout: Some(scan_setting.wait_time), 373 | write_timeout: None, 374 | channel_type: nex::datalink::ChannelType::Layer2, 375 | bpf_fd_attempts: 1000, 376 | linux_fanout: None, 377 | promiscuous: false, 378 | }; 379 | let (mut _tx, mut rx) = match nex::datalink::channel(&interface, config) { 380 | Ok(nex::datalink::Channel::Ethernet(tx, rx)) => (tx, rx), 381 | Ok(_) => return ScanResult::error("Unhandled channel type".to_string()), 382 | Err(e) => return ScanResult::error(format!("Failed to create channel: {}", e)), 383 | }; 384 | let socket_option = SocketOption { 385 | ip_version: if scan_setting.targets.len() > 0 { 386 | if scan_setting.targets[0].ip_addr.is_ipv4() { 387 | IpVersion::V4 388 | } else { 389 | IpVersion::V6 390 | } 391 | } else { 392 | IpVersion::V4 393 | }, 394 | socket_type: SocketType::Raw, 395 | protocol: Some(IpNextLevelProtocol::Tcp), 396 | non_blocking: true, 397 | }; 398 | let socket: AsyncSocket = AsyncSocket::new(socket_option).unwrap(); 399 | let mut capture_options: PacketCaptureOptions = PacketCaptureOptions { 400 | interface_index: interface.index, 401 | src_ips: HashSet::new(), 402 | dst_ips: HashSet::new(), 403 | src_ports: HashSet::new(), 404 | dst_ports: HashSet::new(), 405 | ether_types: HashSet::new(), 406 | ip_protocols: HashSet::new(), 407 | capture_timeout: scan_setting.timeout, 408 | tunnel: interface.is_tun(), 409 | loopback: interface.is_loopback(), 410 | }; 411 | for target in scan_setting.targets.clone() { 412 | capture_options.src_ips.insert(target.ip_addr); 413 | capture_options.src_ports.extend(target.get_ports()); 414 | } 415 | match scan_setting.scan_type { 416 | PortScanType::TcpSynScan => { 417 | capture_options 418 | .ip_protocols 419 | .insert(IpNextLevelProtocol::Tcp); 420 | } 421 | PortScanType::TcpConnectScan => { 422 | capture_options 423 | .ip_protocols 424 | .insert(IpNextLevelProtocol::Tcp); 425 | } 426 | } 427 | let stop: Arc> = Arc::new(Mutex::new(false)); 428 | let stop_handle = Arc::clone(&stop); 429 | let packets: Arc>> = Arc::new(Mutex::new(vec![])); 430 | let receive_packets: Arc>> = Arc::clone(&packets); 431 | // Spawn pcap thread 432 | let pcap_handler = thread::spawn(move || { 433 | let packets: Vec = 434 | crate::pcap::start_capture(&mut rx, capture_options, &stop_handle); 435 | match receive_packets.lock() { 436 | Ok(mut receive_packets) => { 437 | for p in packets { 438 | receive_packets.push(p); 439 | } 440 | } 441 | Err(e) => { 442 | eprintln!("Failed to lock receive_packets: {}", e); 443 | } 444 | } 445 | }); 446 | // Wait for listener to start (need fix for better way) 447 | thread::sleep(Duration::from_millis(PCAP_WAIT_TIME_MILLIS)); 448 | let start_time = std::time::Instant::now(); 449 | // Send probe packets 450 | send_portscan_packets(&interface, &socket, &scan_setting, ptx).await; 451 | thread::sleep(scan_setting.wait_time); 452 | // Stop pcap 453 | match stop.lock() { 454 | Ok(mut stop) => { 455 | *stop = true; 456 | } 457 | Err(e) => { 458 | eprintln!("Failed to lock stop: {}", e); 459 | } 460 | } 461 | // Wait for listener to stop 462 | match pcap_handler.join() { 463 | Ok(_) => {} 464 | Err(e) => { 465 | eprintln!("Failed to join pcap_handler: {:?}", e); 466 | } 467 | } 468 | let mut scan_result: ScanResult = ScanResult::new(); 469 | match packets.lock() { 470 | Ok(packets) => { 471 | scan_result = parse_portscan_result(packets.clone(), scan_setting); 472 | } 473 | Err(e) => { 474 | eprintln!("Failed to lock packets: {}", e); 475 | } 476 | } 477 | scan_result.scan_time = start_time.elapsed(); 478 | scan_result.scan_status = ScanStatus::Done; 479 | scan_result 480 | } 481 | -------------------------------------------------------------------------------- /src/scan/service.rs: -------------------------------------------------------------------------------- 1 | use super::payload::{PayloadInfo, PayloadType}; 2 | use super::result::{ServiceProbeError, ServiceProbeResult}; 3 | use super::setting::ServiceProbeSetting; 4 | use crate::db::tcp_service::PORT_SERVICE_MAP; 5 | use futures::stream::{self, StreamExt}; 6 | use std::collections::HashMap; 7 | use std::net::{IpAddr, SocketAddr}; 8 | use std::sync::mpsc::Sender; 9 | use std::sync::{Arc, Mutex}; 10 | use std::time::Duration; 11 | use tokio::io::{AsyncReadExt, AsyncWriteExt}; 12 | use tokio::net::TcpStream; 13 | use tokio_rustls::TlsConnector; 14 | 15 | /// Parse HTTP header and return server name 16 | /// 17 | /// The server name possibly contains version number. 18 | fn parse_http_header(res_bytes: &Vec) -> Option { 19 | let res_string: String = res_bytes.iter().map(|&c| c as char).collect(); 20 | let header_fields: Vec<&str> = res_string.split("\r\n").collect(); 21 | if header_fields.len() == 1 { 22 | if res_string.contains("Server:") { 23 | return Some(res_string); 24 | } else { 25 | return None; 26 | } 27 | } 28 | for field in header_fields { 29 | if field.contains("Server:") { 30 | let server_info: String = field.trim().to_string(); 31 | return Some(server_info); 32 | } 33 | } 34 | None 35 | } 36 | 37 | /// Read to end and return response as Vec 38 | /// This ignore io::Error on read_to_end because it is expected when reading response. 39 | /// If no response is received, and io::Error is occurred, return Err. 40 | async fn read_response_timeout( 41 | tcp_stream: &mut TcpStream, 42 | timeout_duration: Duration, 43 | ) -> std::io::Result> { 44 | let mut response = Vec::new(); 45 | let mut buf = [0u8; 1024]; 46 | 47 | loop { 48 | match tokio::time::timeout(timeout_duration, tcp_stream.read(&mut buf)).await { 49 | Ok(Ok(0)) => break, 50 | Ok(Ok(n)) => { 51 | response.extend_from_slice(&buf[..n]); 52 | break; 53 | } 54 | Ok(Err(e)) => return Err(e), 55 | Err(_) => break, 56 | } 57 | } 58 | 59 | if response.is_empty() { 60 | Err(std::io::Error::new( 61 | std::io::ErrorKind::TimedOut, 62 | "No response", 63 | )) 64 | } else { 65 | Ok(response) 66 | } 67 | } 68 | 69 | fn set_read_timeout(tcp_stream: TcpStream, timeout: Duration) -> std::io::Result { 70 | // Convert to std::net::TcpStream 71 | let std_tcp_stream = tcp_stream.into_std()?; 72 | // Set read timeout 73 | std_tcp_stream.set_read_timeout(Some(timeout))?; 74 | // Convert back to tokio TcpStream 75 | let tokio_tcp_stream = TcpStream::from_std(std_tcp_stream)?; 76 | Ok(tokio_tcp_stream) 77 | } 78 | 79 | async fn probe_port( 80 | ip_addr: IpAddr, 81 | hostname: String, 82 | port: u16, 83 | payload_info: Option, 84 | timeout: Duration, 85 | ) -> ServiceProbeResult { 86 | let service_name: String = match PORT_SERVICE_MAP.get(&port) { 87 | Some(name) => name.to_string(), 88 | None => String::new(), 89 | }; 90 | let socket_addr: SocketAddr = SocketAddr::new(ip_addr, port); 91 | let tcp_stream = match tokio::time::timeout(timeout, TcpStream::connect(socket_addr)).await { 92 | Ok(connect_result) => match connect_result { 93 | Ok(tcp_stream) => tcp_stream, 94 | Err(e) => { 95 | return ServiceProbeResult::with_error( 96 | port, 97 | service_name, 98 | ServiceProbeError::ConnectionError(e.to_string()), 99 | ) 100 | } 101 | }, 102 | Err(elapsed) => { 103 | return ServiceProbeResult::with_error( 104 | port, 105 | service_name, 106 | ServiceProbeError::ConnectionError(elapsed.to_string()), 107 | ) 108 | } 109 | }; 110 | // Set read timeout 111 | let mut tcp_stream = match set_read_timeout(tcp_stream, timeout) { 112 | Ok(tcp_stream) => tcp_stream, 113 | Err(e) => { 114 | return ServiceProbeResult::with_error( 115 | port, 116 | service_name, 117 | ServiceProbeError::ConnectionError(e.to_string()), 118 | ) 119 | } 120 | }; 121 | if let Some(payload) = payload_info { 122 | match payload.payload_type { 123 | PayloadType::Http => match tcp_stream.write_all(&payload.payload).await { 124 | Ok(_) => { 125 | match tcp_stream.flush().await { 126 | Ok(_) => {} 127 | Err(e) => { 128 | return ServiceProbeResult::with_error( 129 | port, 130 | service_name, 131 | ServiceProbeError::WriteError(e.to_string()), 132 | ) 133 | } 134 | } 135 | match read_response_timeout(&mut tcp_stream, timeout).await { 136 | Ok(res) => { 137 | let mut result = 138 | ServiceProbeResult::new(port, service_name, res.clone()); 139 | result.service_detail = parse_http_header(&res); 140 | return result; 141 | } 142 | Err(e) => { 143 | return ServiceProbeResult::with_error( 144 | port, 145 | service_name, 146 | ServiceProbeError::ReadError(e.to_string()), 147 | ) 148 | } 149 | } 150 | } 151 | Err(e) => { 152 | return ServiceProbeResult::with_error( 153 | port, 154 | service_name, 155 | ServiceProbeError::WriteError(e.to_string()), 156 | ) 157 | } 158 | }, 159 | PayloadType::Https => { 160 | let native_certs = crate::tls::cert::get_native_certs().unwrap(); 161 | let config = rustls::ClientConfig::builder() 162 | .with_root_certificates(native_certs) 163 | .with_no_client_auth(); 164 | let tls_connector = TlsConnector::from(Arc::new(config)); 165 | let name = match rustls_pki_types::ServerName::try_from(hostname) { 166 | Ok(name) => name, 167 | Err(e) => { 168 | return ServiceProbeResult::with_error( 169 | port, 170 | service_name, 171 | ServiceProbeError::ConnectionError(e.to_string()), 172 | ) 173 | } 174 | }; 175 | let mut tls_stream = 176 | match tokio::time::timeout(timeout, tls_connector.connect(name, tcp_stream)) 177 | .await 178 | { 179 | Ok(connect_result) => match connect_result { 180 | Ok(tls_stream) => tls_stream, 181 | Err(e) => { 182 | return ServiceProbeResult::with_error( 183 | port, 184 | service_name, 185 | ServiceProbeError::ConnectionError(e.to_string()), 186 | ) 187 | } 188 | }, 189 | Err(elapsed) => { 190 | return ServiceProbeResult::with_error( 191 | port, 192 | service_name, 193 | ServiceProbeError::ConnectionError(elapsed.to_string()), 194 | ) 195 | } 196 | }; 197 | match tls_stream.write_all(&payload.payload).await { 198 | Ok(_) => { 199 | match tls_stream.flush().await { 200 | Ok(_) => {} 201 | Err(e) => { 202 | return ServiceProbeResult::with_error( 203 | port, 204 | service_name, 205 | ServiceProbeError::WriteError(e.to_string()), 206 | ) 207 | } 208 | } 209 | let mut buf: Vec = Vec::new(); 210 | match tls_stream.read_to_end(&mut buf).await { 211 | Ok(_) => { 212 | let mut result = 213 | ServiceProbeResult::new(port, service_name, buf.clone()); 214 | result.service_detail = parse_http_header(&buf); 215 | return result; 216 | } 217 | Err(e) => { 218 | return ServiceProbeResult::with_error( 219 | port, 220 | service_name, 221 | ServiceProbeError::ReadError(e.to_string()), 222 | ) 223 | } 224 | } 225 | } 226 | Err(e) => { 227 | return ServiceProbeResult::with_error( 228 | port, 229 | service_name, 230 | ServiceProbeError::WriteError(e.to_string()), 231 | ) 232 | } 233 | } 234 | } 235 | PayloadType::Common => match tcp_stream.write_all(&payload.payload).await { 236 | Ok(_) => { 237 | match tcp_stream.flush().await { 238 | Ok(_) => {} 239 | Err(e) => { 240 | return ServiceProbeResult::with_error( 241 | port, 242 | service_name, 243 | ServiceProbeError::WriteError(e.to_string()), 244 | ) 245 | } 246 | } 247 | match read_response_timeout(&mut tcp_stream, timeout).await { 248 | Ok(res) => { 249 | let mut result = 250 | ServiceProbeResult::new(port, service_name, res.clone()); 251 | result.service_detail = Some( 252 | String::from_utf8(res) 253 | .unwrap_or(String::new()) 254 | .replace("\r\n", ""), 255 | ); 256 | return result; 257 | } 258 | Err(e) => { 259 | return ServiceProbeResult::with_error( 260 | port, 261 | service_name, 262 | ServiceProbeError::ReadError(e.to_string()), 263 | ) 264 | } 265 | } 266 | } 267 | Err(e) => { 268 | return ServiceProbeResult::with_error( 269 | port, 270 | service_name, 271 | ServiceProbeError::WriteError(e.to_string()), 272 | ) 273 | } 274 | }, 275 | PayloadType::CommonTls => { 276 | let native_certs = crate::tls::cert::get_native_certs().unwrap(); 277 | let config = rustls::ClientConfig::builder() 278 | .with_root_certificates(native_certs) 279 | .with_no_client_auth(); 280 | let tls_connector = TlsConnector::from(Arc::new(config)); 281 | let name = match rustls_pki_types::ServerName::try_from(hostname) { 282 | Ok(name) => name, 283 | Err(e) => { 284 | return ServiceProbeResult::with_error( 285 | port, 286 | service_name, 287 | ServiceProbeError::ConnectionError(e.to_string()), 288 | ) 289 | } 290 | }; 291 | let mut tls_stream = 292 | match tokio::time::timeout(timeout, tls_connector.connect(name, tcp_stream)) 293 | .await 294 | { 295 | Ok(connect_result) => match connect_result { 296 | Ok(tls_stream) => tls_stream, 297 | Err(e) => { 298 | return ServiceProbeResult::with_error( 299 | port, 300 | service_name, 301 | ServiceProbeError::ConnectionError(e.to_string()), 302 | ) 303 | } 304 | }, 305 | Err(elapsed) => { 306 | return ServiceProbeResult::with_error( 307 | port, 308 | service_name, 309 | ServiceProbeError::ConnectionError(elapsed.to_string()), 310 | ) 311 | } 312 | }; 313 | match tls_stream.write_all(&payload.payload).await { 314 | Ok(_) => { 315 | match tls_stream.flush().await { 316 | Ok(_) => {} 317 | Err(e) => { 318 | return ServiceProbeResult::with_error( 319 | port, 320 | service_name, 321 | ServiceProbeError::WriteError(e.to_string()), 322 | ) 323 | } 324 | } 325 | let mut buf: Vec = Vec::new(); 326 | match tls_stream.read_to_end(&mut buf).await { 327 | Ok(_) => { 328 | let mut result = 329 | ServiceProbeResult::new(port, service_name, buf.clone()); 330 | result.service_detail = Some( 331 | String::from_utf8(buf).unwrap_or(String::new()).to_string(), 332 | ); 333 | return result; 334 | } 335 | Err(e) => { 336 | return ServiceProbeResult::with_error( 337 | port, 338 | service_name, 339 | ServiceProbeError::ReadError(e.to_string()), 340 | ) 341 | } 342 | } 343 | } 344 | Err(e) => { 345 | return ServiceProbeResult::with_error( 346 | port, 347 | service_name, 348 | ServiceProbeError::WriteError(e.to_string()), 349 | ) 350 | } 351 | } 352 | } 353 | PayloadType::Null => match read_response_timeout(&mut tcp_stream, timeout).await { 354 | Ok(res) => { 355 | let mut result = ServiceProbeResult::new(port, service_name, res.clone()); 356 | result.service_detail = Some( 357 | String::from_utf8(res) 358 | .unwrap_or(String::new()) 359 | .replace("\r\n", ""), 360 | ); 361 | return result; 362 | } 363 | Err(e) => { 364 | return ServiceProbeResult::with_error( 365 | port, 366 | service_name, 367 | ServiceProbeError::ReadError(e.to_string()), 368 | ) 369 | } 370 | }, 371 | } 372 | } else { 373 | match read_response_timeout(&mut tcp_stream, timeout).await { 374 | Ok(res) => { 375 | let mut result = ServiceProbeResult::new(port, service_name, res.clone()); 376 | result.service_detail = Some( 377 | String::from_utf8(res) 378 | .unwrap_or(String::new()) 379 | .replace("\r\n", ""), 380 | ); 381 | return result; 382 | } 383 | Err(e) => { 384 | return ServiceProbeResult::with_error( 385 | port, 386 | service_name, 387 | ServiceProbeError::ReadError(e.to_string()), 388 | ) 389 | } 390 | } 391 | } 392 | } 393 | 394 | pub async fn run_service_probe( 395 | setting: &ServiceProbeSetting, 396 | ptx: &Arc>>, 397 | ) -> HashMap { 398 | let service_map: Arc>> = 399 | Arc::new(Mutex::new(HashMap::new())); 400 | let fut_port = 401 | stream::iter(setting.clone().ports).for_each_concurrent(setting.concurrent_limit, |port| { 402 | let c_service_map: Arc>> = 403 | Arc::clone(&service_map); 404 | async move { 405 | let ip_addr = setting.ip_addr; 406 | let hostname = setting.hostname.clone(); 407 | let probe_result: ServiceProbeResult = probe_port( 408 | ip_addr, 409 | hostname, 410 | port, 411 | setting.payload_map.get(&port).cloned(), 412 | setting.read_timeout, 413 | ) 414 | .await; 415 | c_service_map.lock().unwrap().insert(port, probe_result); 416 | match ptx.lock() { 417 | Ok(lr) => match lr.send(SocketAddr::new(ip_addr, port)) { 418 | Ok(_) => {} 419 | Err(_) => {} 420 | }, 421 | Err(_) => {} 422 | } 423 | } 424 | }); 425 | fut_port.await; 426 | let result_map: HashMap = service_map.lock().unwrap().clone(); 427 | result_map 428 | } 429 | --------------------------------------------------------------------------------