我现在需要通过python使用wireguard连接到远程服务器,测试服务器是否可以连接,目前并没有找到特别合适的库,现在找到的一些资料:
WireGuard 是一款现代的、简单的、快速的 VPN,旨在易于使用。它使用最新的加密算法,并针对速度和安全性进行了优化。与其他 VPN 协议(如 OpenVPN 和 IPSec)相比,WireGuard 的代码库更小、更易于理解和维护。 WireGuard-Go 是一个使用 Go 语言编写的 WireGuard VPN 服务器和客户端的开源实现。它是一个安全的、现代的 VPN,旨在易于使用、快速且高效。WireGuard-Go 由 Jason A. Donenfeld 创建,并在 GPLv2 许可下发布。
1、找到的资料
WireGuard使用noise 协议的Noise_IK握手,建立在CurveCP、NaCL、KEA+、SIGMA、FHMQV和HOMQV的基础上。所有数据包都通过UDP发送。
1、https://github.com/plizonczyk/noiseprotocol库:
在noiseprotocol库示例目录中,有一个此软件包与WireGuard VPN解决方案互操作的示例:
pip install noiseprotocol
https://github.com/plizonczyk/noiseprotocol/tree/master/examples/wireguard
2、scapy的WireGuard模块:
pip install scapy
根据白皮书:https://www.wireguard.com/papers/wireguard.pdf,实现的WireGuard网络隧道协议。
https://scapy.readthedocs.io/en/latest/api/scapy.contrib.wireguard.html
3、noise-protocol框架介绍 https://duo.com/labs/tech-notes/noise-protocol-framework-intro https://www.wireguard.com/protocol/
4、Cloudflare:-WARP BoxJs集成 持久化储存(有,但不是完全有,没有做反写功能) 通知(有,但不是完全有,有来自Cloudflare的错误和信息通知) 注册新账户 注册新账户(用自定义密钥对)并生成WireGuard配置文件 注册新设备(注册ID) 重绑定许可证(许可证 & 注册ID) 更换密钥对(用自定义密钥对)(注册ID & 令牌) 查询账户信息(创建日期/剩余流量/邀请人数等) 查询设备配置(设备名称/设备类型/创建日期/活动状态等) 自定义客户端设备类型 5、Xray-core WireGuard 使用笔记 https://github.com/XTLS/Xray-core/issues/1896 这篇东西不适合写在自己网站,毕竟太容易被查水表了,但是确实也很有必要写,没办法,那就写到这里!
关于在xray-core的WireGuard的配置,描述很少,而且很多人是用来套wgcf,其实这是一个非常好的应用层实现,这样OVZ也能很容易套上WireGuard隧道,甚至如果你有一些老外VPN(通常提供WireGuard),那么可以直接套上,把最后流量出口导向其他服务.
阅读代码可以知道,他涉及两个配置.
type WireGuardPeerConfig struct {
PublicKey string `json:"publicKey"`
PreSharedKey string `json:"preSharedKey"`
Endpoint string `json:"endpoint"`
KeepAlive int `json:"keepAlive"`
AllowedIPs []string `json:"allowedIPs,omitempty"`
}
type WireGuardConfig struct {
SecretKey string `json:"secretKey"`
Address []string `json:"address"`
Peers []*WireGuardPeerConfig `json:"peers"`
MTU int `json:"mtu"`
NumWorkers int `json:"workers"`
Reserved []byte `json:"reserved"`
}
所以实际在Outbound上就可以很轻松翻译信息,比如我的WireGuard原配置.
[Interface]
PrivateKey = <我的密钥>
ListenPort = 249
Address = 10.102.237.129/32
DNS = 10.100.0.1
[Peer]
PublicKey = <服务器公钥>
PresharedKey = <预共享密钥>
AllowedIPs = 0.0.0.0/0
Endpoint = 66.42.38.10:249
PersistentKeepalive = 25
翻译到outbounds中是这样的.
"outbounds": [
{
"protocol": "wireguard",
"settings": {
"secretKey": "<我的密钥>",
"address": [
"10.102.237.129/32"
],
"peers": [
{
"publicKey": "<服务器公钥>",
"preSharedKey": "<预共享密钥>",
"keepAlive": 25,
"allowedIPs": [
"0.0.0.0/0"
],
"endpoint": "66.42.38.10:249"
}
],
"reserved":[0, 0, 0],
"mtu": 1280
},
"tag": "wireguard"
}
],
如果没有哪个参数,就不要写哪个参数就可以了,效果测试.
2、noiseprotocol的WireGuard例子:
import base64
import datetime
from hashlib import blake2s
import socket
import struct
from scapy.layers.inet import IP, ICMP
from noise.connection import NoiseConnection, Keypair
"""
这段代码使用WireGuard协议建立一个安全的通信通道。以下是代码的步骤解释:
1. 导入所需的库和模块,包括base64、datetime、hashlib、socket、struct和scapy等。
2. 定义远程服务器的地址和端口号。
3. 使用base64解码我们的私钥、对方的公钥和预共享密钥。
4. 定义握手的前言。
5. 创建一个NoiseConnection对象,并设置其为发起方。
6. 使用我们的私钥和对方的公钥设置密钥对。
7. 设置预共享密钥和前言。
8. 开始握手。
9. 创建一个UDP套接字。
10. 准备并发送握手初始化数据包。
11. 接收握手响应数据包并进行验证。
12. 准备、加密和发送ping数据包。
13. 接收ping响应数据包并进行解密和验证。
14. 发送keepalive数据包。
该代码主要用于建立和维护一个基于WireGuard协议的加密通信通道。它通过握手协议进行密钥交换,并使用加密算法对通信数据进行加密和解密。最后,它还发送保持连接的数据包以确保通信通道的稳定性。
"""
# 定义远程服务器的地址和端口号
address = ("demo.wireguard.com", 12913)
# 使用base64解码我们的私钥、对方的公钥和预共享密
our_private = base64.b64decode("WAmgVYXkbT2bCtdcDwolI88/iVi/aV3/PHcUBTQSYmo=")
their_public = base64.b64decode("qRCwZSKInrMAq5sepfCdaCsRJaoLe5jhtzfiw7CjbwM=")
preshared = base64.b64decode("FpCyhws9cxwWoV4xELtfJvjJN+zQVRPISllRWgeopVE=")
prologue = b"WireGuard v1 zx2c4 [email protected]"
# 定义握手的前言
noise = NoiseConnection.from_name(b"Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s")
noise.set_as_initiator()
noise.set_keypair_from_private_bytes(Keypair.STATIC, our_private)
noise.set_keypair_from_public_bytes(Keypair.REMOTE_STATIC, their_public)
noise.set_psks(psk=preshared)
noise.set_prologue(prologue)
noise.start_handshake()
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 1. 准备并发送握手初始化数据包
now = datetime.datetime.now()
tai = struct.pack(
"!qi", 4611686018427387914 + int(now.timestamp()), int(now.microsecond * 1e3)
)
initiation_packet = b"\x01" # 类型:初始化
initiation_packet += b"\x00" * 3 # 保留字段
initiation_packet += struct.pack("<i", 28) # 发送方索引:28(任意值)
initiation_packet += noise.write_message(payload=tai)
mac_key = blake2s(b"mac1----" + their_public).digest()
initiation_packet += blake2s(initiation_packet, digest_size=16, key=mac_key).digest()
initiation_packet += b"\x00" * 16
sock.sendto(initiation_packet, address)
# 2. 接收握手响应数据包并进行验证
response_packet = sock.recv(92)
assert response_packet[0] == 2 # Type: response
assert response_packet[1:4] == b"\x00" * 3 # Reserved
their_index, our_index = struct.unpack("<ii", response_packet[4:12])
assert our_index == 28
payload = noise.read_message(response_packet[12:60])
assert payload == b""
assert noise.handshake_finished
# 3. 准备、加密和发送ping数据包
icmp_packet = ICMP(type=8, id=921, seq=438) / b"WireGuard"
ip_packet = (
IP(proto=1, ttl=20, src="10.189.129.2", dst="10.189.129.1", id=0) / icmp_packet
)
ping_packet = b"\x04" # # 类型:数据
ping_packet += b"\x00" * 3 # 保留字段
ping_packet += struct.pack("<iq", their_index, 0)
ping_packet += noise.encrypt(bytes(ip_packet))
sock.sendto(ping_packet, address)
# 4. 接收ping响应数据包并进行解密和验证
encrypted_response = sock.recv(80)
assert encrypted_response[0] == 4 # 类型:数据
assert encrypted_response[1:4] == b"\x00" * 3 # 保留字段
our_index, nonce = struct.unpack("<iq", encrypted_response[4:16])
assert our_index == 28
assert nonce == 0
ip = IP(noise.decrypt(encrypted_response[16:]))
icmp = ip[1]
payload = ip[2]
assert icmp.type == 0
assert icmp.code == 0
assert icmp.id == 921
assert icmp.seq == 438
assert payload.load == b"WireGuard"
# 5. 发送keepalive数据包
keepalive = b"\x04" # 类型:数据
keepalive += b"\x00" * 3 # 保留字段
keepalive += struct.pack("<iq", their_index, 1)
keepalive += noise.encrypt(b"")
sock.sendto(keepalive, address)
3、WireGuard 协议数据包的内容
WireGuard 协议数据包的类型有以下几种:
数据包类型 1:握手请求 握手请求用于启动 WireGuard 连接。当一个 WireGuard 对等体想要与另一个 WireGuard 对等体建立连接时,它会发送一个握手请求。
数据包类型 2:握手响应 握手响应用于回复握手请求。当一个 WireGuard 对等体收到握手请求后,它会回复一个握手响应。
数据包类型 3:数据包 数据包用于传输数据。当一个 WireGuard 对等体想要向另一个 WireGuard 对等体发送数据时,它会发送一个数据包。
数据包类型 4:Keepalive 请求 Keepalive 请求用于在两个 WireGuard 对等体之间保持连接。当一个 WireGuard 对等体想要向另一个 WireGuard 对等体发送 Keepalive 请求时,它会发送一个 Keepalive 请求。
数据包类型 5:Keepalive 响应 Keepalive 响应用于回复 Keepalive 请求。当一个 WireGuard 对等体收到 Keepalive 请求后,它会回复一个 Keepalive 响应。
数据包类型 6:重协商请求 重协商请求用于协商新的加密密钥。当一个 WireGuard 对等体想要与另一个 WireGuard 对等体协商新的加密密钥时,它会发送一个重协商请求。
数据包类型 7:重协商响应 重协商响应用于回复重协商请求。当一个 WireGuard 对等体收到重协商请求后,它会回复一个重协商响应。
其中,数据包类型 1、2、3、6 和 7 是必须的,数据包类型 4 和 5 是可选的。
如果数据包的内容为:
payload = (
"\x01\x00\x00\x00\xcb\x9c\xf0\xe7\x3b\x5c\xaa\xb2\x5b\x14\x62\x35"
"\x57\x90\x3c\xa1\x55\xa3\xb1\x55\x44\x66\x3f\x17\xc7\xf3\x93\x9e"
"\x6e\xfa\x95\x8d\xc9\xf2\x84\x57\x23\x88\xb6\x93\x1c\x0b\x74\xbb"
"\x11\x98\x37\x61\x2b\x54\xeb\xb9\x4e\x24\x5b\x90\xf7\xd0\x4c\xe8"
"\xcb\x50\xec\xda\x61\xa7\x3b\xc2\x77\xe6\x58\x76\x12\xaf\x2c\x0e"
"\x29\x0b\x01\x31\x6f\x75\x1f\x67\x3f\x33\x2b\x0b\xa5\x6e\x53\xf3"
"\x34\x82\x59\xec\xf7\x3c\xcb\x6e\x03\x1b\x6d\xa3\x12\x90\x34\xa3"
"\x4f\x89\x1f\x20\x1c\x3e\x7f\xe3\xd7\x21\x9f\xdc\x2f\x4d\xb0\xff"
"\x53\x13\xb3\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00"
)
这是一个十六进制字符串,它表示一个数据包的内容。这个数据包是使用 WireGuard 协议发送的 Ping 请求。
WireGuard 是一种新的 VPN 协议,它比传统的 VPN 协议更安全、更快速。WireGuard 使用一种叫做 Noise IK 的加密协议,这种协议可以抵御中间人攻击。WireGuard 还使用一种叫做 ChaCha20 的加密算法,这种算法比传统的 AES 加密算法更快。
这个十六进制字符串中的每个字节都有其特定的含义。
这个十六进制字符串中的数据包是一个 Ping 请求。它包含了以下信息:
数据包类型:第一个字节 \x01 表示数据包的类型。对于 Ping 请求,数据包类型为 1。 数据包长度:第二个字节 \x00 表示数据包的长度。对于 Ping 请求,数据包长度为 0。 源 IP 地址:第三个字节到第十个字节 \xcb\x9c\xf0\xe7\x3b\x5c\xaa\xb2 表示源 IP 地址。对于这个数据包,源 IP 地址是 195.156.240.231。 目标 IP 地址:第十一个字节到第十八个字节 \x5b\x14\x62\x35\x57\x90\x3c\xa1 表示目标 IP 地址。对于这个数据包,目标 IP 地址是 91.20.98.53。 Ping 序列号:第十九个字节到第二十六个字节 \x55\xa3\xb1\x55\x44\x66\x3f\x17 表示 Ping 序列号。对于这个数据包,Ping 序列号是 851987207。 Ping 数据:第二十七个字节到最后一个字节 \xc7\xf3\x93\x9e\x6e\xfa\x95\x8d\xc9\xf2\x84\x57\x23\x88\xb6\x93\x1c\x0b\x74\xbb\x11\x98\x37\x61\x2b\x54\xeb\xb9\x4e\x24\x5b\x90\xf7\xd0\x4c\xe8\xcb\x50\xec\xda\x61\xa7\x3b\xc2\x77\xe6\x58\x76\x12\xaf\x2c\x0e\x29\x0b\x01\x31\x6f\x75\x1f\x67\x3f\x33\x2b\x0b\xa5\x6e\x53\xf3\x34\x82\x59\xec\xf7\x3c\xcb\x6e\x03\x1b\x6d\xa3\x12\x90\x34\xa3\x4f\x89\x1f\x20\x1c\x3e\x7f\xe3\xd7\x21\x9f\xdc\x2f\x4d\xb0\xff\x53\x13\xb3\x0f 表示 Ping 数据。对于这个数据包,Ping 数据是 199.111.79.181。
这个十六进制字符串中的数据包是一个 Ping 请求。它包含了源 IP 地址、目标 IP 地址、Ping 序列号和 Ping 数据。当 WireGuard 客户端收到这个数据包时,它会将其转发给 WireGuard 服务端。WireGuard 服务端会将这个数据包转发给目标 IP 地址。目标 IP 地址收到这个数据包后,会将其转发给 WireGuard 服务端。WireGuard 服务端会将这个数据包转发给 WireGuard 客户端。WireGuard 客户端收到这个数据包后,会将其解析并提取出 Ping 序列号和 Ping 数据。然后,WireGuard 客户端会将 Ping 序列号和 Ping 数据发送给应用程序。应用程序收到 Ping 序列号和 Ping 数据后,会将其显示在用户界面上。
如果数据包的内容为(这部分可能有问题):
payload= "\x02\x00\x00\x00\x00\"\xb7\x00\x1c\x00\x00\x00\x16\x8a\x0c\xe0\xe6\xbbV\x80V\x9f\x1a\xc1d\xed\xb52\xb2\xbb\xa2\xe4\x12\x05\xa5\xfdf\x05\xeb\x00\x89\x02m1\x85\x8b\x15\x1fo<A`\xda=\xba\x02\xca\x1c\xdam\xcc\xb58\xa4\x19\xae\x00`\xff\x87\xae.JW\xbe\x13\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
这个十六进制字符串中的数据包是一个 Keepalive 请求。它包含了以下信息:
数据包类型:第一个字节 \x02 表示数据包的类型。对于 Keepalive 请求,数据包类型为 2。 数据包长度:第二个字节 \x00 表示数据包的长度。对于 Keepalive 请求,数据包长度为 0。 源 IP 地址:第三个字节到第十个字节 \x00\"\xb7\x00\x1c 表示源 IP 地址。对于这个数据包,源 IP 地址是 0.0.0.0。 目标 IP 地址:第十一个字节到第十八个字节 \x00\x00\x00\x16 表示目标 IP 地址。对于这个数据包,目标 IP 地址是 0.0.0.16。 Ping 序列号:第十九个字节到第二十六个字节 \x8a\x0c\xe0\xe6\xbb 表示 Ping 序列号。对于这个数据包,Ping 序列号是 -2008530867。 Ping 数据:第二十七个字节到最后一个字节 V\x80V\x9f\x1a\xc1d\xed\xb52\xb2\xbb\xa2\xe4\x12\x05\xa5\xfdf\x05\xeb\x00\x89\x02m1\x85\x8b\x15\x1fo<A\xda=\xba\x02\xca\x1c\xdam\xcc\xb58\xa4\x19\xae\x00\xff\x87\xae.JW\xbe\x13 表示 Ping 数据。对于这个数据包,Ping 数据是 VV\x1a\xc1d\xed\xb52\xb2\xbb\xa2\xe4\x12\x05\xa5\xfdf\x05\xeb\x00\x89\x02m1\x85\x8b\x15\x1fo<A\xda=\xba\x02\xca\x1c\xdam\xcc\xb58\xa4\x19\xae\x00\xff\x87\xae.JW\xbe\x13。 此外,这个数据包还包含了一个校验和。校验和用于确保数据包在传输过程中没有被损坏。校验和位于数据包的最后四个字节。
Keepalive 请求用于在两个 WireGuard 对等体之间保持连接。当一个 WireGuard 对等体收到 Keepalive 请求时,它会回复一个 Keepalive 响应。Keepalive 响应用于确认连接仍然有效。
4、使用ai提问获得答案:
WireGuard 协议数据包的握手请求,该如何解析,提供python示例,注释为中文
import struct
def parse_handshake_request(data):
"""解析 WireGuard 握手请求数据包。
Args:
data: 握手请求数据包的二进制数据。
Returns:
包含以下字段的元组:
* protocol_version: WireGuard 协议的版本。
* random: 一个 32 字节的随机数。
* public_key: 发送方的公钥。
* auth_data: 认证数据。
* checksum: 数据包的校验和。
"""
# 解析数据包头。
protocol_version, data_type, length, src_ip, dst_ip, src_port, dst_port, checksum = struct.unpack('!HBB16s16sHH4s', data[:38])
# 检查数据类型是否为握手请求。
if data_type != 1:
raise ValueError('无效的数据类型: {}'.format(data_type))
# 解析数据包数据。
random, public_key, auth_data = struct.unpack('!32s32s{}s'.format(length - 64), data[38:])
return protocol_version, random, public_key, auth_data, checksum
if __name__ == '__main__':
# 示例:
data = b'\x01\x00\x01\x00\x58\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x01\x00\x02\x45\x7b\xea\x6d\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x
5、MiSaturo的PyWarpScanner 项目--用于搜索和ping WireGuard服务器的基于python的脚本 https://github.com/MiSaturo/PyWarpScanner/blob/main/scan.py
MiSaturo的github项目被隐藏了,可以使用链接查看:
https://github.com/search?q=MiSaturo&type=code
https://github.com/MiSaturo/Go-Warp-Scanner
https://github.com/yebekhe/TVC
https://github.com/MiSaturo/PyWarpScanner
添加了中文注释的代码:
import socket
import time
from concurrent.futures import ThreadPoolExecutor
from concurrent.futures import as_completed
from functools import partial
import ipaddress
import statistics
# 获取子网中的 IP 地址列表
def get_ip_from_subnet(ip_subnet):
ips = ipaddress.ip_network(ip_subnet)
ip_list = [str(ip) for ip in ips]
return ip_list
# 使用 WireGuard Ping 工具发送 Ping 请求
def wg_ping(ip, port, timeout=1):
# print('Checking %s:%s' %(ip,port))
# print("Sending request...")
payload = (
"\x01\x00\x00\x00\xcb\x9c\xf0\xe7\x3b\x5c\xaa\xb2\x5b\x14\x62\x35"
"\x57\x90\x3c\xa1\x55\xa3\xb1\x55\x44\x66\x3f\x17\xc7\xf3\x93\x9e"
"\x6e\xfa\x95\x8d\xc9\xf2\x84\x57\x23\x88\xb6\x93\x1c\x0b\x74\xbb"
"\x11\x98\x37\x61\x2b\x54\xeb\xb9\x4e\x24\x5b\x90\xf7\xd0\x4c\xe8"
"\xcb\x50\xec\xda\x61\xa7\x3b\xc2\x77\xe6\x58\x76\x12\xaf\x2c\x0e"
"\x29\x0b\x01\x31\x6f\x75\x1f\x67\x3f\x33\x2b\x0b\xa5\x6e\x53\xf3"
"\x34\x82\x59\xec\xf7\x3c\xcb\x6e\x03\x1b\x6d\xa3\x12\x90\x34\xa3"
"\x4f\x89\x1f\x20\x1c\x3e\x7f\xe3\xd7\x21\x9f\xdc\x2f\x4d\xb0\xff"
"\x53\x13\xb3\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00"
)
ping = 0
start_time = time.time_ns()
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.settimeout(timeout) # in seconds
sock.connect((ip, int(port)))
sock.send(bytes(payload, "latin1"))
try:
dta = sock.recv(100)
ping = int((time.time_ns() - start_time) / 1000000)
# print("Server reply: %s" %(dta))
except:
# print("Server not responding")
pass
sock.close()
# print("###########################################################")
return ping
# 定义一个函数来进行 Ping 操作并返回 Ping 值
def do_ping(ip_port):
(ip, port) = ip_port
pings = []
ping = 0
for i in range(1, 3):
px = wg_ping(ip, port, timeout=1)
if px == 0:
# px=9999
return (ip, port, 0)
pings.append(px)
ping = statistics.mean(pings)
return (ip, port, ping)
# 定义待扫描的 CIDR 和端口范围
# cidrs = "188.114.99.0/24"
cidrs = "162.159.192.0/24 162.159.193.0/24 162.159.195.0/24 188.114.96.0/24 188.114.97.0/24 188.114.98.0/24 188.114.99.0/24"
# ports="500 854 859 864 878 880 890 891 894 903 908 928 934 939 942 943 945 946 955 968 987 988 1002 1010 1014 1018 1070 1074 1180 1387 1701 1843 2371 2408 2506 3138 3476 3581 3854 4177 4198 4233 4500 5279 5956 7103 7152 7156 7281 7559 8319 8742 8854 8886"
ports = "2408 1701 4500 988"
# 构建 IP 和端口的列表
ip_port_list = []
for cidr in cidrs.split(" "):
for ip in get_ip_from_subnet(cidr):
ip_port_list.extend([(ip, port) for port in ports.split(" ")])
print(f"{len(ip_port_list)} target has been generated.")
# 使用线程池来并行地进行 Ping 操作
progress = 0
print("Scanning...")
sorted_result = []
with ThreadPoolExecutor(64) as executor:
results_gen = executor.map(do_ping, ip_port_list)
for p in results_gen:
(ip, port, ping) = p
progress += 1
if ping > 0:
print(
f"{ip}:{port}\t-> {ping}ms \t ({progress/len(ip_port_list)*100:#05.2f}%)"
)
sorted_result.append(p)
# 对结果进行排序并输出
sorted_result.sort(key=lambda tup: tup[2], reverse=False)
print("\n--------------------------\n")
print("Sorted results:\n")
for p in sorted_result:
(ip, port, ping) = p
print(f"{ip}:{port}\t-> {ping}ms")