353 lines
13 KiB
Rust
353 lines
13 KiB
Rust
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();
|
||
}
|
||
} |