1use crate::parser::{parse_ports, Args};
2use log::{error, info};
3use serde::{Deserialize, Serialize};
4use std::io;
5use std::net::{SocketAddr, ToSocketAddrs};
6use std::sync::Arc;
7use std::time::{Duration, Instant};
8use threadpool::ThreadPool;
9use tokio::io::{AsyncReadExt, AsyncWriteExt};
10use tokio::net::TcpStream;
11use tokio::sync::Mutex as AsyncMutex;
12use tokio::time::timeout;
13
14#[derive(Serialize, Deserialize)]
21pub struct ScanResult {
22 port: u16,
23 status: String,
24 banner: Option<String>,
25}
26
27async fn probe(target: &str, port: u16, timeout_ms: u64) -> Option<String> {
28 let address = format!("{}:{}", target, port);
29 let socket_addr: SocketAddr = address.to_socket_addrs().ok()?.next()?;
30
31 info!("Attempting to connect to {}", address);
32
33 match timeout(
34 Duration::from_millis(timeout_ms),
35 TcpStream::connect(&socket_addr),
36 )
37 .await
38 {
39 Ok(Ok(mut stream)) => {
40 info!("Connected to {}", address);
41
42 let http_request = format!(
43 "GET / HTTP/1.1\r\nHost: {}\r\nConnection: close\r\n\r\n",
44 target
45 );
46 match stream.write_all(http_request.as_bytes()).await {
47 Ok(_) => info!("Sent HTTP GET request to {}", address),
48 Err(e) => {
49 error!("Failed to send HTTP GET request to {}: {:?}", address, e);
50 return None;
51 }
52 }
53
54 let mut banner = vec![0; 1024];
55
56 match timeout(Duration::from_secs(1), stream.read(&mut banner)).await {
58 Ok(Ok(n)) if n > 0 => {
59 info!("Read {} bytes from {}", n, address);
60 return Some(String::from_utf8_lossy(&banner[..n]).to_string());
61 }
62 Ok(Ok(_)) => {
63 error!("No data read from {}", address);
64 }
65 Ok(Err(e)) => {
66 error!("Failed to read from {}: {:?}", address, e);
67 }
68 Err(_) => {
69 error!("Read operation timed out for {}", address);
70 }
71 }
72 }
73 Ok(Err(e)) => {
74 error!("Failed to connect to {}: {:?}", address, e);
75 }
76 Err(_) => {
77 error!("Connection attempt timed out for {}", address);
78 }
79 }
80
81 None
82}
83
84async fn check_port(
85 target: Arc<String>,
86 port: u16,
87 timeout_ms: u64,
88 do_probe: bool,
89 results: Arc<AsyncMutex<Vec<ScanResult>>>,
90) {
91 let address = format!("{}:{}", target, port);
92 let socket_addr: SocketAddr = match address.to_socket_addrs() {
93 Ok(mut addrs) => match addrs.next() {
94 Some(addr) => addr,
95 None => {
96 error!("Could not resolve address: {}", address);
97 return;
98 }
99 },
100 Err(e) => {
101 error!("Failed to resolve address {}: {:?}", address, e);
102 return;
103 }
104 };
105
106 match timeout(
107 Duration::from_secs(timeout_ms),
108 TcpStream::connect(&socket_addr),
109 )
110 .await
111 {
112 Ok(Ok(_)) => {
113 if do_probe {
114 let banner = probe(&target, port, timeout_ms).await;
115 let mut results = results.lock().await;
116 results.push(ScanResult {
117 port,
118 status: "open".to_string(),
119 banner,
120 });
121 } else {
122 let mut results = results.lock().await;
123 results.push(ScanResult {
124 port,
125 status: "open".to_string(),
126 banner: None,
127 });
128 }
129 }
130 Ok(Err(e)) => {
131 let status = match e.kind() {
132 io::ErrorKind::ConnectionRefused => "refused",
133 _ => "failed",
134 };
135 info!("Port {} {}", port, status);
136 }
137 Err(_) => {
138 info!("Port {} timed out", port);
139 }
140 }
141}
142
143pub async fn scan(args: Args) {
144 let ports = parse_ports(&args.port_range);
145 let target = Arc::new(args.target.trim().to_string());
146
147 println!("{}", "*".repeat(40));
148 println!("* Scanning: {} *", target);
149 println!("{}", "*".repeat(40));
150
151 let start = Instant::now();
152
153 let pool = ThreadPool::new(args.threads);
154 let results = Arc::new(AsyncMutex::new(Vec::new()));
155
156 for port in ports {
157 let results = Arc::clone(&results);
158 let target = Arc::clone(&target);
159 let timeout = args.timeout;
160 let probe = args.probe;
161 pool.execute(move || {
162 let rt = tokio::runtime::Runtime::new().unwrap();
163 rt.block_on(async move {
164 check_port(target, port, timeout, probe, results).await;
165 });
166 });
167 }
168
169 pool.join();
170
171 let end = Instant::now();
172 let duration = end.duration_since(start);
173
174 let results = results.lock();
175
176 println!();
177 for result in results.await.iter() {
178 println!(
179 "Port {} {}{}",
180 result.port,
181 result.status,
182 result
183 .banner
184 .as_ref()
185 .map(|b| format!(" - {}", b))
186 .unwrap_or_default()
187 );
188 }
189
190 println!(
191 "\nScanning completed in {:.2} seconds",
192 duration.as_secs_f64()
193 );
194}
195
196#[cfg(test)]
197mod tests {
198 use super::*;
199
200 #[tokio::test]
201 async fn test_open_port() {
202 let target = Arc::new("192.168.1.1".to_string());
204 let port = 80;
205 let results = Arc::new(AsyncMutex::new(Vec::new()));
206 let results_clone = Arc::clone(&results);
207
208 check_port(target, port, 100, false, results_clone).await;
209
210 let results = results.lock().await;
211 assert_eq!(results.len(), 1);
212 assert_eq!(results[0].port, port);
213 assert_eq!(results[0].status, "open");
214 }
215
216 #[tokio::test]
217 async fn test_closed_port() {
218 let target = Arc::new("192.168.1.1".to_string());
220 let port = 90;
221 let results = Arc::new(AsyncMutex::new(Vec::new()));
222 let results_clone = Arc::clone(&results);
223
224 check_port(target, port, 100, false, results_clone).await;
225
226 let results = results.lock().await;
227 assert_eq!(results.len(), 0);
228 }
229}