Skip to content

Commit 6f3c4b8

Browse files
committed
Merge branch 'dev'
2 parents 0457d13 + 126c501 commit 6f3c4b8

File tree

10 files changed

+144
-19
lines changed

10 files changed

+144
-19
lines changed

Cargo.lock

Lines changed: 33 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ For using `qscan` library put this dependency in your `Cargo.toml`:
3636

3737
```bash
3838
[dependencies]
39-
qscan = "0.4.1"
39+
qscan = "0.5.0"
4040
```
4141

4242
for more details and examples see [qscan README](./qscan/README.md).

qsc/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "qsc"
3-
version = "0.2.1"
3+
version = "0.3.0"
44
description = "Quick async network scanner CLI"
55
documentation = "https://github.com/0xor0ne/qscan"
66
readme = "README.md"
@@ -17,7 +17,7 @@ exclude = [
1717
]
1818

1919
[dependencies]
20-
qscan = { path = "../qscan", version = "0.4.1" }
20+
qscan = { path = "../qscan", version = "0.5.0" , features = ["serialize"] }
2121
tokio = { version = "1", features = ["rt"] }
2222
clap = { version = "3.1.18", features = ["derive"] }
2323

qsc/README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ Print the help message using `-h` option:
3737

3838
```bash
3939
>>> qsc -h
40-
qsc 0.2.1
40+
qsc 0.3.0
4141
0xor0ne
4242
Quick async network scanner CLI
4343

@@ -47,6 +47,7 @@ USAGE:
4747
OPTIONS:
4848
--batch <BATCH> Parallel scan [default: 5000]
4949
-h, --help Print help information
50+
--json <JSON> Path to file whre to save results in json format
5051
--ports <PORTS> Comma separate list of ports (or port ranges) to scan for each
5152
target. E.g., '80', '22,443', '1-1024,8080'
5253
--printlevel <PRINTLEVEL> Console output mode:
@@ -82,6 +83,9 @@ qsc --targets "www.google.com" --ports "80,443"
8283
# Use a file as target, the file must contain a target (IP, cidr or domain name)
8384
# for each line
8485
qsc --targets "/tmp/ips.txt" --ports "1-1024"
86+
# Print all the ports with OPEN/CLOSED indication and save results in json
87+
# format in file /tmp/res.json
88+
qsc --targets "8.8.8.8" --ports 80,443,111 --tries 1 --json /tmp/xxx.json --printlevel 4
8589
```
8690

8791
## Docker Image

qsc/src/main.rs

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@
2424
//!
2525
//! ## OPTIONS:
2626
//!
27+
//! ```text
2728
//! --batch <BATCH> Parallel scan [default: 5000]
2829
//! -h, --help Print help information
30+
//! --json <JSON> Path to file whre to save results in json format
2931
//! --ports <PORTS> Comma separate list of ports (or port ranges) to scan for each
3032
//! target. E.g., '80', '22,443', '1-1024,8080'
3133
//! --printlevel <PRINTLEVEL> Console output mode:
@@ -45,6 +47,11 @@
4547
//! --tries <TRIES> Number of maximum retries for each target:port pair [default:
4648
//! 1]
4749
//! -V, --version Print version information
50+
//! ```
51+
52+
use std::fs::File;
53+
use std::io::Write;
54+
use std::path::PathBuf;
4855

4956
use qscan::{QSPrintMode, QScanTcpConnectResult, QScanTcpConnectState, QScanType, QScanner};
5057

@@ -101,6 +108,9 @@ struct Args {
101108
"
102109
)]
103110
printlevel: u8,
111+
112+
#[clap(long, help = "Path to file whre to save results in json format")]
113+
json: Option<PathBuf>,
104114
}
105115

106116
/// Simple async tcp connect scanner
@@ -112,6 +122,18 @@ pub fn main() {
112122
let batch = args.batch;
113123
let timeout = args.timeout;
114124
let tries = args.tries;
125+
let mut jf: Option<File> = None;
126+
127+
if args.json.is_some() {
128+
jf = if let Ok(f) = File::create(&args.json.as_ref().unwrap().as_path()) {
129+
Some(f)
130+
} else {
131+
panic!(
132+
"Cannot create file {}",
133+
args.json.unwrap().to_str().unwrap()
134+
);
135+
}
136+
}
115137

116138
let mut scanner = QScanner::new(&addresses, &ports);
117139
scanner.set_scan_type(QScanType::TcpConnect);
@@ -132,11 +154,11 @@ pub fn main() {
132154
}
133155
}
134156

135-
let res: Vec<QScanTcpConnectResult> =
157+
let res: &Vec<QScanTcpConnectResult> =
136158
Runtime::new().unwrap().block_on(scanner.scan_tcp_connect());
137159

138160
if !no_output && (args.printlevel == 1 || args.printlevel == 2) {
139-
for sa in &res {
161+
for sa in res {
140162
if sa.state == QScanTcpConnectState::Open {
141163
if args.printlevel == 1 {
142164
println!("{}", sa.target);
@@ -148,4 +170,15 @@ pub fn main() {
148170
}
149171
}
150172
}
173+
174+
if let Some(mut f) = jf {
175+
let j = scanner.get_last_results_as_json_string().unwrap();
176+
if let Err(e) = f.write_all(j.as_bytes()) {
177+
eprintln!(
178+
"Error writing json results in {}: {}",
179+
args.json.unwrap().to_str().unwrap(),
180+
e
181+
);
182+
}
183+
}
151184
}

qscan/Cargo.toml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "qscan"
3-
version = "0.4.1"
3+
version = "0.5.0"
44
description = "Quick async network scan library"
55
documentation = "https://docs.rs/qscan"
66
readme = "README.md"
@@ -18,4 +18,10 @@ cidr-utils = "0.5.6"
1818
futures = "0.3"
1919
itertools = "0.10.3"
2020
trust-dns-resolver = { version = "0.21.2", features = ["dns-over-rustls"] }
21+
serde = { version = "1.0", optional = true }
22+
serde_json = { version = "1.0", optional = true }
23+
# serde = { version = "1.0" }
24+
# serde_json = { version = "1.0" }
2125

26+
[features]
27+
serialize = ["serde", "serde_json" ]

qscan/README.md

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,23 @@ Dependencies (`Cargo.toml`):
1919

2020
```bash
2121
[dependencies]
22-
qscan = "0.4.1"
22+
qscan = "0.5.0"
23+
tokio = { version = "1", features = ["rt-multi-thread"] }
24+
```
25+
26+
Alternatively, in order enable `qscan::QScanTcpConnectResult` serialization,
27+
activate `serialize` feature:
28+
29+
```bash
30+
[dependencies]
31+
qscan = { version = "0.5.0" , features = ["serialize"] }
2332
tokio = { version = "1", features = ["rt-multi-thread"] }
2433
```
2534
2635
and then (`src/main.rs`):
2736
2837
```rust
29-
use qscan::qscanner::{QSPrintMode, QScanTcpConnectState, QScanType, QScanner};
38+
use qscan::{QSPrintMode, QScanTcpConnectState, QScanType, QScanner};
3039
use tokio::runtime::Runtime;
3140

3241
pub fn main() {
@@ -39,12 +48,12 @@ pub fn main() {
3948

4049
let res = Runtime::new().unwrap().block_on(scanner.scan_tcp_connect());
4150

42-
for sa in &res {
51+
for sa in res {
4352
if sa.state == QScanTcpConnectState::Open {
4453
println!("{}", sa.target);
4554
}
4655
}
4756
}
4857
```
4958
50-
See also the [provided example](./examples/scan.rs).
59+
See also the [provided example](./examples/scan.rs) and [qsc utility](../qsc/).

qscan/examples/scan.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ pub fn main() {
2727

2828
let res = Runtime::new().unwrap().block_on(scanner.scan_tcp_connect());
2929

30-
for sa in &res {
30+
for sa in res {
3131
if sa.state == QScanTcpConnectState::Open {
3232
println!("{}", sa.target);
3333
}

qscan/src/lib.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,16 @@
1414
// this program. If not, see <https://www.gnu.org/licenses/>.
1515
//
1616

17+
//! # QSCAN
18+
//!
1719
//! Asynchronous network ports scanning library
20+
//!
21+
//! To enable `qscan::QScanTcpConnectResult` serialization:
22+
//!
23+
//! ```text
24+
//! [dependencies]
25+
//! qscan = { path = "../qscan", version = "0.5.0" , features = ["serialize"] }
26+
//! ```
1827
1928
pub use crate::qscanner::QSPrintMode;
2029
pub use crate::qscanner::QScanTcpConnectResult;

qscan/src/qscanner.rs

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@
1616

1717
use std::fmt;
1818

19+
#[cfg(feature = "serialize")]
20+
use serde::ser::{Serialize, SerializeStruct, Serializer};
21+
#[cfg(feature = "serialize")]
22+
use serde_json;
23+
1924
use std::net::IpAddr;
2025
use std::net::SocketAddr;
2126
use std::net::ToSocketAddrs;
@@ -104,6 +109,27 @@ impl fmt::Display for QScanError {
104109
}
105110
}
106111

112+
#[cfg(feature = "serialize")]
113+
impl Serialize for QScanTcpConnectResult {
114+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
115+
where
116+
S: Serializer,
117+
{
118+
let mut s = serializer.serialize_struct("QScanTcpConnectResult", 3)?;
119+
s.serialize_field("IP", &self.target.ip())?;
120+
s.serialize_field("port", &self.target.port())?;
121+
match self.state {
122+
QScanTcpConnectState::Open => {
123+
s.serialize_field("state", "OPEN")?;
124+
}
125+
QScanTcpConnectState::Close => {
126+
s.serialize_field("state", "CLOSED")?;
127+
}
128+
}
129+
s.end()
130+
}
131+
}
132+
107133
/// Defaults
108134
const SCAN_TYPE: QScanType = QScanType::TcpConnect;
109135
const PRINT_MODE: QSPrintMode = QSPrintMode::NonRealTime;
@@ -283,6 +309,11 @@ impl QScanner {
283309
.collect::<Vec<u16>>();
284310
}
285311

312+
#[cfg(feature = "serialize")]
313+
pub fn get_last_results_as_json_string(&self) -> serde_json::Result<String> {
314+
serde_json::to_string(&self.last_results)
315+
}
316+
286317
/// Async TCP connect scan
287318
///
288319
/// # Return
@@ -298,8 +329,8 @@ impl QScanner {
298329
/// let res = Runtime::new().unwrap().block_on(scanner.scan_tcp_connect());
299330
/// ```
300331
///
301-
pub async fn scan_tcp_connect(&self) -> Vec<QScanTcpConnectResult> {
302-
let mut open_soc: Vec<QScanTcpConnectResult> = Vec::new();
332+
pub async fn scan_tcp_connect(&mut self) -> &Vec<QScanTcpConnectResult> {
333+
let mut sock_res: Vec<QScanTcpConnectResult> = Vec::new();
303334
let mut sock_it: sockiter::SockIter = sockiter::SockIter::new(&self.ips, &self.ports);
304335
let mut ftrs = FuturesUnordered::new();
305336

@@ -328,7 +359,7 @@ impl QScanner {
328359
_ => {}
329360
}
330361

331-
open_soc.push(QScanTcpConnectResult {
362+
sock_res.push(QScanTcpConnectResult {
332363
target: socket,
333364
state: QScanTcpConnectState::Open,
334365
});
@@ -338,15 +369,17 @@ impl QScanner {
338369
println!("{}:{}:CLOSED", error.sock.ip(), error.sock.port());
339370
}
340371

341-
open_soc.push(QScanTcpConnectResult {
372+
sock_res.push(QScanTcpConnectResult {
342373
target: error.sock,
343374
state: QScanTcpConnectState::Close,
344375
});
345376
}
346377
}
347378
}
348379

349-
open_soc
380+
drop(ftrs);
381+
self.last_results = Some(sock_res);
382+
self.last_results.as_ref().unwrap()
350383
}
351384

352385
async fn scan_socket_tcp_connect(&self, socket: SocketAddr) -> Result<SocketAddr, QScanError> {

0 commit comments

Comments
 (0)