diff --git a/apps/settings/tools/__init__.py b/apps/settings/tools/__init__.py index 09154d4b2..993b7bca2 100644 --- a/apps/settings/tools/__init__.py +++ b/apps/settings/tools/__init__.py @@ -4,3 +4,4 @@ from .ping import * from .telnet import * from .nmap import * from .tcpdump import * +from .traceroute import * diff --git a/apps/settings/tools/ping.py b/apps/settings/tools/ping.py index 8a40110e8..4af375a4f 100644 --- a/apps/settings/tools/ping.py +++ b/apps/settings/tools/ping.py @@ -143,7 +143,7 @@ async def verbose_ping(dest_ips, timeout=2, count=5, psize=64, display=None): for dest_ip in ips: await display(f'PING {dest_ip}: 56 data bytes') # 切换异步协程 - await asyncio.sleep(0.1) + await asyncio.sleep(0.01) error_count = 0 for i in range(count): try: diff --git a/apps/settings/tools/traceroute.py b/apps/settings/tools/traceroute.py new file mode 100644 index 000000000..726f3a07d --- /dev/null +++ b/apps/settings/tools/traceroute.py @@ -0,0 +1,73 @@ +# -*- coding: utf-8 -*- +# +import asyncio +import socket +import struct +import time + +from settings.utils import generate_ips + + +def calculate_checksum(data): + # 计算 ICMP 校验和 + checksum = 0 + if len(data) % 2 == 1: + data += b'\x00' + for i in range(0, len(data), 2): + w = (data[i] << 8) + data[i+1] + checksum += w + checksum = (checksum >> 16) + (checksum & 0xffff) + checksum = ~checksum & 0xffff + return checksum + + +async def once_traceroute(target, display, max_hops=30, timeout=3): + loop = asyncio.get_event_loop() + icmp = socket.getprotobyname('icmp') + sock_fd = socket.socket(socket.AF_INET, socket.SOCK_RAW, icmp) + for ttl in range(1, max_hops+1): + # 设置 TTL + sock_fd.setsockopt(socket.IPPROTO_IP, socket.IP_TTL, ttl) + # 设置超时时间 + sock_fd.settimeout(timeout) + # 发送 ICMP Echo 请求数据包 + packet = struct.pack('!BBHHH', 8, 0, 0, 12345, ttl) + checksum = calculate_checksum(packet) + packet = struct.pack('!BBHHH', 8, 0, checksum, 12345, ttl) + start_time = time.time() + sock_fd.sendto(packet, (target, 0)) + await asyncio.sleep(0.01) + try: + # 接收 ICMP Echo Reply 数据包 + recv_packet, addr = await loop.sock_recvfrom(sock_fd, 1024) + end_time = time.time() + # 解析目标地址 + dest_ip = addr[0] + # 获取跃点信息 + hop_info = f'{ttl} {dest_ip} ({dest_ip})' + # 获取延迟时间信息 + delay_info = f'{(end_time - start_time) * 1000:.3f} ms' + # 打印跃点信息和延迟时间 + await display(f'{hop_info:<4} {delay_info}') + if dest_ip == target: + return + except socket.timeout: + # 发生超时,跳出循环 + await display(f'{ttl} *') + sock_fd.close() + + +async def verbose_traceroute(dest_ips, timeout=10, display=None): + if not display: + return + + ips = generate_ips(dest_ips) + await display(f'Total valid address: {len(ips)}\r\n') + for dest_ip in ips: + await display(f'traceroute to {dest_ip}, 30 hops max, 60 byte packets') + msg = '' + try: + await once_traceroute(dest_ip, display) + except Exception as e: + msg = f'Error: {e}' + await display(msg) diff --git a/apps/settings/utils/common.py b/apps/settings/utils/common.py index 1e1f94f12..826b19cd8 100644 --- a/apps/settings/utils/common.py +++ b/apps/settings/utils/common.py @@ -18,20 +18,22 @@ def get_login_title(): def generate_ips(address_string): + def transform(_ip): + real_ip, err_msg = lookup_domain(_ip) + return _ip if err_msg else real_ip # 支持的格式 # 192.168.1.1,192.168.1.2 # 192.168.1.1-12 | 192.168.1.1-192.168.1.12 | 192.168.1.0/30 | 192.168.1.1 ips = [] ip_list = address_string.split(',') - if len(ip_list) > 1: + if len(ip_list) >= 1: for ip in ip_list: try: - ips.append(str(IP(ip))) + ips.append(str(IP(transform(ip)))) except ValueError: - ip, err = lookup_domain(ip) - if not err: - ips.append(ip) - return ips + pass + if ips: + return ips ip_list = address_string.split('-') try: @@ -58,6 +60,7 @@ def is_valid_port(port): valid = False return valid + def generate_ports(ports): port_list = [] if isinstance(ports, int): diff --git a/apps/settings/ws.py b/apps/settings/ws.py index 5fec6ebb4..a8ae2c398 100644 --- a/apps/settings/ws.py +++ b/apps/settings/ws.py @@ -6,7 +6,10 @@ from channels.generic.websocket import AsyncJsonWebsocketConsumer from common.db.utils import close_old_connections from common.utils import get_logger -from .tools import verbose_ping, verbose_telnet, verbose_nmap, verbose_tcpdump +from .tools import ( + verbose_ping, verbose_telnet, verbose_nmap, + verbose_tcpdump, verbose_traceroute +) logger = get_logger(__name__) @@ -57,6 +60,10 @@ class ToolsWebsocket(AsyncJsonWebsocketConsumer): logger.info(f'Receive request tcpdump: {params}') await verbose_tcpdump(display=self.send_msg, **params) + async def imitate_traceroute(self,dest_ips): + params = {'dest_ips': dest_ips} + await verbose_traceroute(display=self.send_msg, **params) + async def receive(self, text_data=None, bytes_data=None, **kwargs): data = json.loads(text_data) tool_type = data.pop('tool_type', 'Ping')