NickChenyx's Blog

nickChenyx

马上订阅 NickChenyx's Blog RSS 更新: https://nickchenyx.github.io/atom.xml

记一次端口扫描工具实现

2020年11月17日 14:33

端口扫描工具

工具如其名,主要功能是扫描服务器端口是否开放。常用的方式是扫描服务器(S)在某一段端口(P)范围内,有哪些端口正在被监听。

如:扫描服务器 127.0.0.1,端口 [80, 2000)段内,查看被监听的端口。

实现方案

  • 和对端服务器端口尝试建立 socket 链接,如果链接成功建立,则表明该端口正在被监听。
  • 使用多线程方案,加快端口扫描速度
  • 使用协程方案,降低资源使用,加快扫描速度

以上就是大致的是实现步骤。本次实现了

  • Java 单线程扫描
  • Java 多线程扫描
  • Java 协程扫描
  • Rust 协程扫描

没错,>..< 实际上这是一次学习 Java Quasar Fiber 和 Rust 过程中的小练习。

实现代码

实现已放在 github 仓库,感兴趣的可以下载自行运行。

遇到的问题

Java Fiber 和 Rust 的实现过程中都遇到了关于协程使用上的问题,在此记录一下。

Java Fiber

首先需要在函数上标明 @Suspendable,协程内的阻塞函数需要标记此注解才能让协程正常服务。

其次协程中使用的阻塞函数,需要有自己的封装实现。线程方式中使用 java.net.Socket#connect 函数是调用的 JDK 原生实现 java.net.PlainSocketImpl#socketConnect
这个实现是阻塞的,此阻塞的实现,会影响阻塞住协程,使得协程的性能无法发挥。
Quasar 为此单独提供了 FiberSocketChannel 实现,此实现调用了 co.paralleluniverse.fibers.io.AsyncFiberSocketChannel#connect
底层是异步 IO 实现,使用 Fiber.park 接替了原有的阻塞实现。

使用过程中可以感受到,使用 Fiber 接替现有的代码,还是有需要改造的部分,无法零成本接入。(此处如果我有使用不合理的地方,请邮件我改进,谢谢

Rust

Rust 版本实现中,使用了三方依赖 may 作为 coroutine 的实现依赖。出现了同 Java 版本类似的问题。
原生的 std::net::TcpStream 依然会阻塞住协程,还是需要使用 may 提供的异步 IO 实现 may::net::TcpStream 才能匹配协程提升性能。

另外就是 Rust 的 TcpStream::connect_timeout 使用方式不能按照官方提供的文档使用。

use std::net::TcpStream;if let Ok(stream) = TcpStream::connect("127.0.0.1:8080") {    println!("Connected to the server!");} else {    println!("Couldn't connect to server...");}

按照此方法使用 TcpStream::connect_timeout,会导致 if 判断提前进入 else 逻辑,造成逻辑异常。
必须将链接建立的语句提前,才能逻辑正常。

// 正确实现let stream = TcpStream::connect_timeout(&addrs[0], timeout);if stream.is_ok() {    stream.unwrap().shutdown(Shutdown::Both).expect("shutdown tcp stream fail");    return true;} else {    return false;}

诡异的问题

实现 Rust 版本的测试结果中,出现了诡异的问题。问题描述如下:

使用 rdial --start-port 80 --end-port 9000 --hostname 127.0.0.1 --timeout 200 执行时,结果返回了 [1080, 4198] 两个接口,实际上 6394 接口也是在被监听,但没有被扫描出来。修改使用 rdial --start-port 6000 --end-port 9000 --hostname 127.0.0.1 --timeout 200 执行时,此时结果返回了 6394 接口。

经过 debug 确认了所有端口都有的的确确被扫描到,并没有漏掉 6394 端口。

继续尝试增加 timeout 超时时间,发现还是无法扫描到 6394 端口,但是有个特殊现象——6394 端口在建立链接语句执行后,立马返回了“close”日志,并没有经过设置好的等待时间。

最后发现问题是在操作系统限制的 fd...

剩余内容已隐藏

查看完整文章以阅读更多