Rust小白自练手小项目 - IP端口嗅探器

序:我的第一个不知道咋写的博客

小白看着 rust 挺有趣,小白学习学习,小白练练手,小白第一次写博客,小白很随意

1. 什么是 IP 端口嗅探器

IP端口嗅探器,通常也被简称为嗅探器或协议分析器,是一种能够监视和分析网络数据运行的软件或硬件设备,特别关注于IP端口的数据传输。

2. 本小项目要实现的功能

通过输入命令,来实现嗅探某个IP的主机的端口是否开放。

3. 实现过程与方法

3.1 rust 获取环境参数

let args: Vec<String> = env::args().collect();
for arg in args {
    println!("arg = {}", arg);
}
cargo run -- -h    // 控制台输入

arg = target\debug\ip_sniffer.exe
arg = -h

输出两个参数,一个是程序本身,一个是第一个参数。小项目通过输入命令参数,来实现IP端口嗅探的功能。

cargo run -- -j 1000 127.0.0.1    // 示例命令

上述命令包含 3 个参数,-j表示需要启动多线程的flag ,1000为线程数,127.0.0.1IP 地址。因此可以设计参数的结构体如下:

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

我们在输入命令时,将数据放入到结构体中,因此我们为结构体实现一个关联函数。

impl Arguments {
    fn new(args: &Vec<String>) -> Result<Arguments, &'static str> {
    }
}

传入的参数是一个字符串vec to ref ,返回一个Result枚举,Ok的话将Argument放入,否则将错误消息&str放入 Err中 。

3.2 让我们来实现这个关联函数吧

if args.len() < 2 {
    return Err("not enough arguments");
} else if args.len() > 4 {
    return Err("too many arguments");
}

我们的 IP 端口嗅探器只允许最多输入4 个参数,最少输入2 个参数,不在这个范围内直接返回错误消息。

let f = args[1].clone();
if let Ok(ipaddr) = IpAddr::from_str(&f) {
    return Ok(Arguments {
        flag: String::from(""),
        ipaddr,
        threads: 4,
    });
} else {
	// other logic
}

如果第一个参数就是IP ,我们为其生成一个默认的命令格式的Arguments ,类似于如下命令

cargo run --  4 127.0.0.1

如果第一个参数不是IP ,我们来检查是不是-h,或者-help,如果是我们则给出一些提示,当然仅此而已。当然我们不允许此时有更多的参数了。

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 /*other condition*/ {
		// other logic
    }

接下来我们看其它条件

 else if flag.contains("-j") {
    let ipaddr = match IpAddr::from_str(&args[3]) {
        Ok(s) => s,
        Err(_) => return Err("not a valid IPADDR: must be IPv4 or IPv6"),
    };
    let threads = match args[2].parse::<u16>() {
        Ok(s) => s,
        Err(_) => return Err("failed to parse thread number"),
    };
    return Ok(Arguments {
        threads,
        flag,
        ipaddr,
    });
} else {
    return Err("invalid syntsx");
}

如果flag-j,说明我们接下来的一个参数是要启动的线程数量,最后一个参数仍然是IP 地址。同理,我们将获取到的线程数和IP 地址放入Arguments 中。

3.3 接下来我们看main函数的实现

/// ip_sniffer/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 {
        eprintln!("{} problem parsing arguments: {}", program, err);
        process::exit(0);
    }
});

我们获取环境参数args ,第0个参数就是程序本身,通过命令输入的参数来创建Arguments

let num_threads = arguments.threads;
let ip_addr = arguments.ipaddr;
let (tx, rx) = channel();

for i in 0..num_threads {
    let tx = tx.clone();
    thread::spawn(move || {
        scan(tx, i, ip_addr, num_threads);
    });
}

我们得到线程数量num_threadsIP地址ip_addr;然后创建channel返回发送端和接收端,启动线程向 ip:port 发送数据。

3.4 让我们看看scan的实现

const MAX = 65535;

fn scan(tx: Sender<u16>, start_port: u16, addr: IpAddr, num_threads: u16) {
    let mut port: u16 = 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开始,我们执行loop循环,向ip:port发送TCP请求,如果请求成功,将缓冲区数据写出到目标,tx并将此port通讯给rx

3.5 接下来我们返回到main函数中看看剩余的部分

let mut out = vec![];
drop(tx);
for p in rx {
    out.push(p);
}

println!("");
out.sort();
for v in out {
    println!("{} is open", v);
}

rx接收到的消息放入到vec中,并打印出来。

4. 代码输出效果

其结果如下

....................
135 is open
445 is open
5040 is open
7680 is open
8588 is open
8590 is open
8680 is open
11066 is open
13013 is open
15796 is open
18444 is open
18460 is open
18464 is open
19156 is open
49664 is open
49665 is open
49666 is open
49667 is open
49668 is open
49693 is open
  • 16
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值