通过例子学rust(端口嗅探器)

  1. 1. 简介
  2. 2. 获取参数
  3. 3. 建立结构体
  4. 4. main函数中的错误处理
  5. 5. main中执行多线程扫描的部分
  6. 6. scan 函数
  7. 7. 完整代码

简介

个人推荐使用rust的clap库实现命令行程序功能

非原创,原版在这

首先分析需求:

1
2
3
scanner -h # 帮助信息
scanner -j $THREAD_NUMBER $IP #指定线程
scanner $IP # 默认扫描

获取参数

我们使用std::env 来获取用户输入的参数

1
2
3
4
5
6
7
8
9
10
11
use std::env;

fn main() {
let args: Vec<String> = env::args().collect();
println!("------");
for i in &args {
println!("{}",i)
}
println!("{:?}",args)
}

测试一下得到的结果:

1
2
3
4
5
6
7
8
9
10
>cargo run  1 2 3
Compiling scanenr v0.1.0 (F:\mypro\port)
Finished dev [unoptimized + debuginfo] target(s) in 0.39s
Running `target\debug\scanenr.exe 1 2 3`
------
target\debug\scanenr.exe
1
2
3
["target\\debug\\scanenr.exe", "1", "2", "3"]

建立结构体

我们用结构体来这些参数并且将其实例化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
use std::net::IpAddr;

struct Arguments {
flag: String,
ipaddr: IpAddr,
threads: u16,
}
impl Arguments {
fn new(args: &[String]) -> Result<Arguments, &'static str> {
//使用&'static str 是为了保证返回的结果能在主函数中处理,保证生命周期
if args.len() < 2 {
return Err("not enough arguments");
} else if args.len() > 4 {
return Err("too many arguments");
}
// 判断参数数量是否正确

let f = args[1].clone();
if let Ok(ipaddr) = IpAddr::from_str(&f) {
return Ok(Arguments { flag: String::from(""), ipaddr, threads: 4 })
// scanner $IP 使用默认值直接扫描的情况


} else {
let flag = args[1].clone();
//判断flag类型
if flag.contains("-h") || flag.contains("-help") && args.len() == 2 {
println!("Usage: -j to select how many threads you want \
\r\n -h rot -help to show this help message");
return Err("help");
} else if flag.contains("-h") || flag.contains("-help") {
//参数数量不对的情况

return Err("too many arguments");

} else if flag.contains("-j") {
//带参数的情况
let ipaddr = match IpAddr::from_str(&args[3]) {
Ok(s) => s,
Err(_) => return Err("not a valid Ip address, must be IPV4 or IPV6")
};
let threads = match args[2].parse::<u16>() {
Ok(s) => s,
Err(_) => return Err("failed to pars thread number")
};
return Ok(Arguments { threads, flag, ipaddr })
} else {
return Err("invalid syntax")
}
}
}
}

main函数中的错误处理

让我们回到main函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
fn main() {
let args: Vec<String> = env::args().collect();

let program = args[0].clone();
let arguments = Arguments::new(&args).unwrap_or_else(|err| {
//使用闭包处理错误
if err.contains("help") {
process::exit(0);
} else {
println!("{} problem parsing arguments: {}", program, err);
process::exit(0);
}
}
);
}

main中执行多线程扫描的部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
let num_threads = arguments.threads;
let addr = arguments.ipaddr;
let (tx, rx) = channel();
for i in 0..num_threads {
let tx = tx.clone();
thread::spawn(move || {
scan(tx, i, addr, num_threads);
});
}
let mut out = vec![];
drop(tx);
//
for p in rx {
out.push(p);
}
println!("");
out.sort();
for v in out {
println!("{} is open", v);
}

scan 函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
fn scan(tx: Sender<u16>, start_port: u16, addr: IpAddr, num_threads: u16) {
let mut port = start_port + 1;
loop {
match TcpStream::connect((addr, port)) {
Ok(_) => {
print!(".");
//扫描到存活端口就打印一个“.”
io::stdout().flush().unwrap();
tx.send(port).unwrap();
//纪录端口
}
Err(_) => {}
}
if (MAX - port) <= num_threads {
break;
//避免扫描端口超过上限
}
port += num_threads
}
}
1
2
3
4
5
6
7
8
cargo run -j 10 172.16.1.25
Compiling scanenr v0.1.0 (F:\mypro\port)
Finished dev [unoptimized + debuginfo] target(s) in 0.49s
Running `target\debug\scanenr.exe 172.16.1.25`
..
22 is open
25 is open

完整代码

完整代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
use std::env;
use std::io::{self, Write};
use std::net::{IpAddr, TcpStream};
use std::process;
use std::sync::mpsc::{Sender, channel};
use std::thread;
use std::str::FromStr;


const MAX: u16 = 65535;

struct Arguments {
flag: String,
ipaddr: IpAddr,
threads: u16,
}

impl Arguments {
fn new(args: &[String]) -> Result<Arguments, &'static str> {
if args.len() < 2 {
return Err("not enough arguments");
} else if args.len() > 4 {
return Err("too many arguments");
}
let f = args[1].clone();
if let Ok(ipaddr) = IpAddr::from_str(&f) {
return Ok(Arguments { flag: String::from(""), ipaddr, threads: 4 });
} else {
let flag = args[1].clone();
if flag.contains("-h") || flag.contains("-help") && args.len() == 2 {
println!("Usage: -j to select how many threads you want \
\r\n -h or -help to show this help message");
return Err("help");
} else if flag.contains("-h") || flag.contains("-help") {
return Err("too many arguments");
} else if flag.contains("-j") {
let ipaddr = match IpAddr::from_str(&args[3]) {
Ok(s) => s,
Err(_) => return Err("not a valid Ip address, must be IPV4 or IPV6")
};
let threads = match args[2].parse::<u16>() {
Ok(s) => s,
Err(_) => return Err("failed to pars thread number")
};
return Ok(Arguments { threads, flag, ipaddr });
} else {
return Err("invalid syntax");
}
}
}
}

fn scan(tx: Sender<u16>, start_port: u16, addr: IpAddr, num_threads: u16) {
let mut port = start_port + 1;
loop {
match TcpStream::connect((addr, port)) {
Ok(_) => {
print!(".");
io::stdout().flush().unwrap();
tx.send(port).unwrap();
}
Err(_) => {}
}
if (MAX - port) <= num_threads {
break;
}
port += num_threads
}
}


fn main() {
let args: Vec<String> = env::args().collect();
let program = args[0].clone();
let arguments = Arguments::new(&args).unwrap_or_else(|err| {
if err.contains("help") {
process::exit(0);
} else {
println!("{} problem parsing arguments: {}", program, err);
process::exit(0);
}
}
);
let num_threads = arguments.threads;
let addr = arguments.ipaddr;
let (tx, rx) = channel();
for i in 0..num_threads {
let tx = tx.clone();
thread::spawn(move || {
scan(tx, i, addr, num_threads);
});
}
let mut out = vec![];
drop(tx);
for p in rx {
out.push(p);
}
println!("");
out.sort();
for v in out {
println!("{} is open", v);
}
}