Files
lan_toolkit/src/application.rs
2026-09-25 17:54:21 +08:00

353 lines
13 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
use crate::config::Config;
use crate::worker::WorkerState;
use iced::{Color, Element};
use iced::widget::{button, column, container, pick_list, row, text};
use pnet::packet::{MutablePacket, Packet};
use pnet::util::MacAddr;
use pnet_packet::arp::{ArpHardwareTypes, ArpOperations, MutableArpPacket};
use pnet_packet::ethernet::{EtherTypes, MutableEthernetPacket};
use std::fmt::Display;
use std::net::{IpAddr, Ipv4Addr};
pub const WINDOW_WIDTH: u32 = 750;
pub const WINDOW_HEIGHT: u32 = 500;
pub struct LANToolkit {
pub state: WorkerState,
pub cfg: Config,
pub ifaces: Vec<String>,
pub selected_iface: Option<String>,
pub selected_iface_idx: Option<usize>,
pub raw_packet_available: bool,
pub test_message: TestState,
pub spoof_state: SpoofState,
pub spoof_btn_text: String,
pub mode: Mode,
}
#[derive(Copy, Clone, Debug)]
pub enum Messages {
RefreshIfaces,
IfaceSelected(Option<usize>),
TestIface,
// Running, Random
StartSpoof(bool, bool),
ModeSwitch,
}
#[derive(Copy, Clone, Debug)]
pub enum TestState {
NotTested,
Unavailable,
Available,
InvalidSelection,
ListUpdated,
NotSelected,
}
#[derive(Copy, Clone, Debug)]
pub enum SpoofState {
Unchecked,
NotReady,
Ready,
Online,
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Mode {
Standard,
Random
}
impl Display for Mode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Standard => write!(f, "标准模式"),
Self::Random => write!(f, "随机模式"),
}
}
}
impl Display for SpoofState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SpoofState::Unchecked => write!(f, "点击启动按钮表示您愿意承担相应责任"),
SpoofState::NotReady => write!(f, "网卡未检测或不可用"),
SpoofState::Ready => write!(f, "准备就绪"),
SpoofState::Online => write!(f, "运行中"),
}
}
}
impl SpoofState {
fn get_color(self) -> Color {
match self {
SpoofState::Unchecked => Color::from_rgb(1.0f32, 0.0f32, 0.0f32),
SpoofState::NotReady => Color::from_rgb(0.0f32, 1.0f32, 1.0f32),
SpoofState::Ready => Color::from_rgb(0.0f32, 0.0f32, 1.0f32),
SpoofState::Online => Color::from_rgb(0.0f32, 1.0f32, 0.0f32),
}
}
fn get_btn_text(self) -> String {
match self {
SpoofState::Unchecked => "确认".to_string(),
SpoofState::NotReady => "启动".to_string(),
SpoofState::Ready => "启动".to_string(),
SpoofState::Online => "停止".to_string(),
}
}
}
impl Display for TestState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
TestState::NotTested => write!(f, "尚未测试"),
TestState::Unavailable => write!(f, "不可用"),
TestState::Available => write!(f, "可用"),
TestState::InvalidSelection => write!(f, "无效选项"),
TestState::ListUpdated => write!(f, "网卡列表已更新,需要刷新"),
TestState::NotSelected => write!(f, "请选择接口"),
}
}
}
impl LANToolkit {
pub fn boot(state: WorkerState, config: Config) -> impl Fn() -> Self {
move || {
LANToolkit {
state: state.clone(),
cfg: config.clone(),
ifaces: vec![],
selected_iface: None,
selected_iface_idx: None,
raw_packet_available: false,
test_message: TestState::NotSelected,
spoof_state: SpoofState::Unchecked,
spoof_btn_text: SpoofState::Unchecked.get_btn_text(),
mode: Mode::Standard,
}
}
}
pub fn view(&self) -> Element<'_, Messages> {
container(
column![
// 第一行
row![
text("可用的网络接口: ").width(130),
pick_list(
self.ifaces.clone(),
self.selected_iface.clone(),
|string: String| {
Messages::IfaceSelected(
self.ifaces.iter().position(|x| x == string.as_str())
)
}
).placeholder("Select a network interface")
.width(300),
button("重新加载").on_press(Messages::RefreshIfaces),
].padding(10),
// 第二行
row![
text("接口状态: ").width(130),
text(self.test_message.to_string()).width(300),
button("重新检测").on_press(Messages::TestIface),
].padding(10),
// 第三行
row![
button(self.spoof_btn_text.as_str())
.width(515)
.on_press(Messages::StartSpoof(false, false)),
].padding(10),
column![
text("警告: 在未经过配置的设备上使用该程序可能导致连接的网络瘫痪")
.color(Color::from_rgb(1.0f32,0.0f32,0.0f32)),
text("使用者需自行承担责任"),
].padding(10),
row![
text("当前状态: "),
text(self.spoof_state.to_string())
.width(300)
.color(self.spoof_state.get_color()),
].padding(10),
row![
text("当前模式: ").width(130),
text(self.mode.to_string()).width(300),
button("切换").on_press(Messages::ModeSwitch),
].padding(10),
column![
text("标准模式: 局域网所有流量发送至本机,由本机处理"),
text("随机模式: 报复社会用,开启后局域网所有流量发送至随机地址,相当于断网"),
text("(当然,如果你的目的是断网,那么随机模式不会泄露你的mac地址,这意味着溯源难度会更大)")
].padding(10),
row![
text("开发者: Starrynight @ StarryForge")
].padding(10)
].padding(20)
).width(WINDOW_WIDTH)
.height(WINDOW_HEIGHT)
.into()
}
fn test_iface(self: &LANToolkit) -> TestState {
// Test if the interface is available to send raw packets
// First We get the selected interface
let ifaces = pnet::datalink::interfaces();
if self.selected_iface_idx.is_none() {
return TestState::NotSelected;
}
if self.selected_iface_idx.unwrap() >= ifaces.len() {
return TestState::InvalidSelection;
}
let iface = ifaces[self.selected_iface_idx.unwrap()].clone();
if iface.name != self.selected_iface.clone().unwrap() {
return TestState::ListUpdated;
}
if iface.mac.is_none() {
println!("Test failed because selected interface has no mac address");
return TestState::Unavailable;
}
if iface.ips.len() == 0 {
println!("Test failed because selected interface has no IP address");
return TestState::Unavailable;
}
// Then we get its v4 address
let v4addr = iface.ips.iter().map(|x| -> Option<Ipv4Addr> {
if x.is_ipv4() {
let IpAddr::V4(v4) = x.ip() else { return None };
return Some(v4);
}
None
}).filter(|x| {
if x.is_some() {
return true;
}
false
}).nth(0);
if v4addr.is_none() {
println!("Test failed because selected interface has no IPv4 address");
return TestState::Unavailable;
}
let v4addr = v4addr.unwrap().unwrap();
// Now try to send an arp packet
let channel = pnet::datalink::channel(&iface, pnet::datalink::Config::default());
if channel.is_err() {
println!("Channel creation failed: {}", channel.err().unwrap());
return TestState::Unavailable;
}
let channel = channel.unwrap();
let pnet::datalink::Channel::Ethernet(mut tx, _rx) = channel else {
// This should not happen
return TestState::Unavailable;
};
let mut buf = [0u8; 42];
let epacket = MutableEthernetPacket::new(&mut buf);
if epacket.is_none() {
println!("Ethernet packet couldn't be constructed");
return TestState::Unavailable;
}
let mut epacket = epacket.unwrap();
epacket.set_destination(MacAddr::broadcast());
epacket.set_source(iface.mac.unwrap());
epacket.set_ethertype(EtherTypes::Arp);
let packet = MutableArpPacket::new(epacket.payload_mut());
if packet.is_none() {
println!("Test failed because error occurred while constructing ArpPacket");
return TestState::Unavailable;
}
let mut packet = packet.unwrap();
packet.set_hardware_type(ArpHardwareTypes::Ethernet);
packet.set_hw_addr_len(6);
packet.set_proto_addr_len(4);
packet.set_protocol_type(EtherTypes::Ipv4);
packet.set_operation(ArpOperations::Request);
packet.set_sender_hw_addr(iface.mac.unwrap());
packet.set_sender_proto_addr(v4addr);
packet.set_target_hw_addr(MacAddr::zero());
packet.set_target_proto_addr(Ipv4Addr::new(1,2,3,4));
// After construction we send it
let res = tx.send_to(epacket.packet(), None);
if res.is_none() {
// That means theres no sufficient capacity in the buffer
println!("Test failed because send_to() returns None");
return TestState::Unavailable;
}
if let Err(e) = res.unwrap() {
println!("Send arp packet failed because {}", e);
return TestState::Unavailable;
}
TestState::Available
}
fn reset(&mut self) {
self.selected_iface = None;
self.selected_iface_idx = None;
self.raw_packet_available = false;
if !matches!(self.spoof_state, SpoofState::Unchecked) {
self.spoof_state = SpoofState::NotReady;
}
self.state.tx.send(Messages::StartSpoof(false, false)).unwrap();
}
pub fn update(&mut self, message: Messages) {
let mut messages = message;
if let Messages::IfaceSelected(Some(index)) = messages {
self.reset();
self.selected_iface_idx = Some(index);
self.selected_iface = self.ifaces.get(index).cloned();
self.test_message = TestState::NotTested;
}
if matches!(messages, Messages::RefreshIfaces) {
// Refresh Interfaces
let ifaces = pnet::datalink::interfaces();
self.ifaces = ifaces.iter().map(|x| x.name.clone()).collect();
self.reset();
self.test_message = TestState::NotSelected;
}
if matches!(messages, Messages::TestIface) {
self.test_message = self.test_iface();
if matches!(self.test_message, TestState::Available) {
self.raw_packet_available = true;
if matches!(self.spoof_state, SpoofState::NotReady) {
self.spoof_state = SpoofState::Ready;
}
}
}
if matches!(messages, Messages::ModeSwitch) {
self.mode = match self.mode {
Mode::Standard => Mode::Random,
Mode::Random => Mode::Standard,
}
}
if let Messages::StartSpoof(_, _) = messages {
match self.spoof_state {
SpoofState::Unchecked => {
self.spoof_state = if self.raw_packet_available {
SpoofState::Ready
}else {
SpoofState::NotReady
}
},
SpoofState::NotReady => {},
SpoofState::Ready => {
messages = Messages::StartSpoof(true, self.mode == Mode::Random);
self.spoof_state = SpoofState::Online
},
SpoofState::Online => {
messages = Messages::StartSpoof(false, false);
self.spoof_state = SpoofState::Ready
}
}
}
self.spoof_btn_text = self.spoof_state.get_btn_text();
self.state.tx.send(messages).unwrap();
}
}