feat: display self info +tcp +udp

This commit is contained in:
fly6516 2025-01-06 14:44:58 +08:00
parent 2559eaf66c
commit 300f42fd8f

172
main.py
View File

@ -1,102 +1,172 @@
import tkinter as tk import tkinter as tk
from tkinter import messagebox from tkinter import ttk, messagebox
import socket import socket
import struct import struct
import re
import time import time
import uuid
import re
# 获取本机信息
def get_local_info():
hostname = socket.gethostname()
local_ips = socket.gethostbyname_ex(hostname)[-1]
mac = ':'.join(re.findall('..', '%012x' % uuid.getnode()))
return hostname, mac, local_ips
# 计算校验和 # 验证 IP 地址或域名
def calculate_checksum(data): def validate_ip_or_domain(value):
checksum = 0 ip_pattern = re.compile(r'^\d{1,3}(\.\d{1,3}){3}$')
n = len(data) domain_pattern = re.compile(r'^([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$')
return ip_pattern.match(value) or domain_pattern.match(value)
# 按 16 位块划分 # 验证端口号
for i in range(0, n - 1, 2): def validate_port(port):
chunk = (data[i] << 8) + data[i + 1] return port.isdigit() and 1 <= int(port) <= 65535
checksum += chunk
# 如果长度为奇数,补零 # 构造 IP 头部
if n % 2 == 1: def construct_ip_header(src_ip, dest_ip, payload_length, protocol):
checksum += data[-1] << 8 version = 4
ihl = 5
tos = 0
total_length = 20 + payload_length
identification = 54321
flags_offset = 0
ttl = 64
header_checksum = 0
# 将 32 位总和折叠为 16 位 src_ip_bytes = socket.inet_aton(src_ip)
checksum = (checksum >> 16) + (checksum & 0xFFFF) dest_ip_bytes = socket.inet_aton(dest_ip)
checksum += (checksum >> 16) ip_header = struct.pack('!BBHHHBBH4s4s',
(version << 4) + ihl, tos, total_length,
# 对总和取反 identification, flags_offset, ttl, protocol,
return ~checksum & 0xFFFF header_checksum, src_ip_bytes, dest_ip_bytes)
return ip_header
# 构造 ICMP 报文 # 构造 ICMP 报文
def construct_icmp(): def construct_icmp():
# ICMP 报文头部: 类型(8), 代码(0), 校验和(0), 标识符(1), 序列号(1)
icmp_header = struct.pack('!BBHHH', 8, 0, 0, 1, 1) icmp_header = struct.pack('!BBHHH', 8, 0, 0, 1, 1)
data = b'Ping' data = b'Ping'
# 计算校验和
checksum = calculate_checksum(icmp_header + data) checksum = calculate_checksum(icmp_header + data)
# 填充校验和字段
icmp_header = struct.pack('!BBHHH', 8, 0, checksum, 1, 1) icmp_header = struct.pack('!BBHHH', 8, 0, checksum, 1, 1)
return icmp_header + data return icmp_header + data
# 构造 UDP 报文 # 构造 UDP 报文
def construct_udp(src_port, dest_port): def construct_udp(src_port, dest_port):
udp_header = struct.pack('!HHHH', src_port, dest_port, 8, 0) udp_header = struct.pack('!HHHH', src_port, dest_port, 8, 0)
return udp_header return udp_header
# 构造 TCP 报文SYN
def construct_tcp(src_port, dest_port):
seq = 0
ack_seq = 0
offset = 5
reserved = 0
flags = 0b000010 # SYN flag
window = socket.htons(5840)
checksum = 0
urgent_ptr = 0
# 验证输入 tcp_header = struct.pack('!HHLLBBHHH',
def validate_ip(ip): src_port, dest_port, seq, ack_seq,
pattern = re.compile(r'^\d{1,3}(\.\d{1,3}){3}$') (offset << 4) + reserved, flags, window,
return pattern.match(ip) is not None checksum, urgent_ptr)
return tcp_header
# 计算校验和
def calculate_checksum(data):
checksum = 0
n = len(data)
for i in range(0, n - 1, 2):
chunk = (data[i] << 8) + data[i + 1]
checksum += chunk
if n % 2 == 1:
checksum += data[-1] << 8
checksum = (checksum >> 16) + (checksum & 0xFFFF)
checksum += (checksum >> 16)
return ~checksum & 0xFFFF
# 发送报文 # 发送报文
def send_packet(): def send_packet():
try: try:
target_ip = ip_entry.get() src_ip = ip_combobox.get()
target_port = int(port_entry.get()) dest_host = dest_entry.get()
dest_port = port_entry.get()
packet_type = var.get() packet_type = var.get()
if not validate_ip(target_ip):
raise ValueError("目标 IP 地址无效")
sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, # 验证目标地址
socket.IPPROTO_ICMP if packet_type == "ICMP" else socket.IPPROTO_UDP) if not validate_ip_or_domain(dest_host):
raise ValueError("目标地址无效,必须为有效的 IP 或域名")
# 验证端口号
if not validate_port(dest_port):
raise ValueError("端口号无效必须是有效的正整数1-65535")
dest_ip = socket.gethostbyname(dest_host)
src_port = 12345
dest_port = int(dest_port)
# 构造报文并发送
if packet_type == "ICMP": if packet_type == "ICMP":
sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)
packet = construct_icmp() packet = construct_icmp()
elif packet_type == "UDP": elif packet_type == "UDP":
packet = construct_udp(12345, target_port) sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
packet = construct_udp(src_port, dest_port)
elif packet_type == "TCP":
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((dest_ip, dest_port))
packet = b"Hello, TCP!" # TCP 是面向连接的,这里发送简单的字符串
sock.sendall(packet)
sock.close()
result.set(f"TCP 连接成功,数据已发送!")
return # 结束函数
start_time = time.time() # 发送报文
sock.sendto(packet, (target_ip, target_port)) sock.sendto(packet, (dest_ip, dest_port))
end_time = time.time() result.set(f"报文发送成功!类型: {packet_type}")
except socket.gaierror:
result.set(f"报文发送成功!类型: {packet_type}, 往返时间: {end_time - start_time:.2f}") messagebox.showerror("网络错误", "无法解析目标地址,请检查输入的 IP 或域名是否正确。")
except socket.error as e:
messagebox.showerror("网络错误", f"网络错误: {e.strerror}")
except ValueError as e:
messagebox.showerror("输入错误", f"输入无效: {e}")
except Exception as e: except Exception as e:
messagebox.showerror("错误", f"报文发送失败: {e}") messagebox.showerror("未知错误", f"发送报文时发生未知错误: {e}")
# GUI 界面设计 # GUI 界面设计
window = tk.Tk() window = tk.Tk()
window.title("网络通信软件") window.title("网络通信软件")
tk.Label(window, text="目标 IP 地址:").grid(row=0, column=0) hostname, mac, local_ips = get_local_info()
ip_entry = tk.Entry(window)
ip_entry.grid(row=0, column=1)
tk.Label(window, text="目标端口号:").grid(row=1, column=0) tk.Label(window, text="本机信息:").grid(row=0, column=0)
info_text = tk.Text(window, height=4, width=40)
info_text.grid(row=0, column=1, columnspan=2)
info_text.insert(tk.END, f"主机名: {hostname}\nMAC 地址: {mac}\n可用 IP: {', '.join(local_ips)}")
info_text.config(state=tk.DISABLED)
tk.Label(window, text="本机 IP 地址:").grid(row=1, column=0)
ip_combobox = ttk.Combobox(window, values=local_ips)
ip_combobox.grid(row=1, column=1)
ip_combobox.current(0)
tk.Label(window, text="目标地址:").grid(row=2, column=0)
dest_entry = tk.Entry(window)
dest_entry.grid(row=2, column=1)
tk.Label(window, text="目标端口:").grid(row=3, column=0)
port_entry = tk.Entry(window) port_entry = tk.Entry(window)
port_entry.grid(row=1, column=1) port_entry.grid(row=3, column=1)
tk.Label(window, text="报文类型:").grid(row=2, column=0) tk.Label(window, text="报文类型:").grid(row=4, column=0)
var = tk.StringVar(value="ICMP") var = tk.StringVar(value="ICMP")
tk.Radiobutton(window, text="ICMP", variable=var, value="ICMP").grid(row=2, column=1) tk.Radiobutton(window, text="ICMP", variable=var, value="ICMP").grid(row=4, column=1)
tk.Radiobutton(window, text="UDP", variable=var, value="UDP").grid(row=2, column=2) tk.Radiobutton(window, text="UDP", variable=var, value="UDP").grid(row=4, column=2)
tk.Radiobutton(window, text="TCP", variable=var, value="TCP").grid(row=4, column=3)
result = tk.StringVar() result = tk.StringVar()
tk.Label(window, textvariable=result).grid(row=4, column=0, columnspan=3) tk.Label(window, textvariable=result).grid(row=6, column=0, columnspan=3)
tk.Button(window, text="发送报文", command=send_packet).grid(row=3, column=1) tk.Button(window, text="发送报文", command=send_packet).grid(row=5, column=1)
window.mainloop() window.mainloop()