支持tap网卡,优化tun网卡配置

This commit is contained in:
lubeilin
2023-05-07 18:32:11 +08:00
parent 068580e036
commit 35ed7f7e45
68 changed files with 2810 additions and 2393 deletions
+12
View File
@@ -1,2 +1,14 @@
[workspace] [workspace]
members = ["switch","switch-desktop"] members = ["switch","switch-desktop"]
[profile.release]
opt-level = 'z'
debug = 0
debug-assertions = false
strip= "debuginfo"
overflow-checks = true
lto = true
panic = 'abort'
incremental = false
codegen-units = 1
rpath = false
+2
View File
@@ -42,6 +42,7 @@
2. ssh 2. ssh
<img width="506" alt="ssh" src="https://raw.githubusercontent.com/lbl8603/switch/dev/documents/img/ssh.jpg"> <img width="506" alt="ssh" src="https://raw.githubusercontent.com/lbl8603/switch/dev/documents/img/ssh.jpg">
5. 帮助,使用-h命令查看
### 更多玩法 ### 更多玩法
@@ -72,6 +73,7 @@
### 特性 ### 特性
- IP层数据转发 - IP层数据转发
- tun虚拟网卡 - tun虚拟网卡
- tap虚拟网卡
- NAT穿透 - NAT穿透
- 点对点穿透 - 点对点穿透
- 服务端中继转发 - 服务端中继转发
+30 -5
View File
@@ -13,6 +13,7 @@ use crate::StartArgs;
pub mod log_config; pub mod log_config;
pub struct StartConfig { pub struct StartConfig {
pub tap: bool,
pub name: String, pub name: String,
pub token: String, pub token: String,
pub server: SocketAddr, pub server: SocketAddr,
@@ -21,7 +22,20 @@ pub struct StartConfig {
} }
pub fn default_config(start_args: StartArgs) -> Result<StartConfig, String> { pub fn default_config(start_args: StartArgs) -> Result<StartConfig, String> {
println!("========参数配置========");
let args_config = read_config(); let args_config = read_config();
let tap = start_args.tap.unwrap_or_else(|| {
if let Some(c) = &args_config {
c.tap
} else {
false
}
});
if tap {
println!("use tap");
} else {
println!("use tun");
}
if args_config.is_none() && start_args.token.is_none() { if args_config.is_none() && start_args.token.is_none() {
return Err("找不到token(Token not found)".to_string()); return Err("找不到token(Token not found)".to_string());
} }
@@ -98,12 +112,14 @@ pub fn default_config(start_args: StartArgs) -> Result<StartConfig, String> {
} }
println!("NAT探测服务器:{:?}", nat_test_server); println!("NAT探测服务器:{:?}", nat_test_server);
let base_config = StartConfig { let base_config = StartConfig {
tap,
name, name,
token, token,
server, server,
nat_test_server, nat_test_server,
device_id, device_id,
}; };
println!("========参数配置========");
Ok(base_config) Ok(base_config)
} }
@@ -114,6 +130,8 @@ lazy_static! {
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ArgsConfig { pub struct ArgsConfig {
#[serde(default = "default_tap")]
pub tap: bool,
#[serde(default = "default_version")] #[serde(default = "default_version")]
pub version: String, pub version: String,
#[serde(default = "default_str")] #[serde(default = "default_str")]
@@ -123,7 +141,7 @@ pub struct ArgsConfig {
pub command_port: Option<u16>, pub command_port: Option<u16>,
#[serde(default = "default_str")] #[serde(default = "default_str")]
pub server: String, pub server: String,
#[serde(default = "default_resource_vec")] #[serde(default = "default_vec")]
pub nat_test_server: Vec<String>, pub nat_test_server: Vec<String>,
#[serde(default = "default_str")] #[serde(default = "default_str")]
pub device_id: String, pub device_id: String,
@@ -131,6 +149,10 @@ pub struct ArgsConfig {
pub pid: u32, pub pid: u32,
} }
fn default_tap() -> bool {
false
}
fn default_version() -> String { fn default_version() -> String {
"1.0".to_string() "1.0".to_string()
} }
@@ -139,7 +161,7 @@ fn default_str() -> String {
"".to_string() "".to_string()
} }
fn default_resource_vec() -> Vec<String> { fn default_vec() -> Vec<String> {
vec![] vec![]
} }
@@ -148,19 +170,22 @@ fn default_pid() -> u32 {
} }
impl ArgsConfig { impl ArgsConfig {
pub fn new(token: String, name: String, server: String, nat_test_server: Vec<String>, device_id: String) -> Self { pub fn new(tap: bool, token: String, name: String, server: SocketAddr,
nat_test_server: &Vec<SocketAddr>, device_id: String, ) -> Self {
Self { Self {
tap,
version: "1.0".to_string(), version: "1.0".to_string(),
token, token,
name, name,
command_port: None, command_port: None,
server, server: server.to_string(),
nat_test_server, nat_test_server: nat_test_server.iter().map(|v| v.to_string()).collect::<Vec<String>>(),
device_id, device_id,
pid: 0, pid: 0,
} }
} }
} }
pub fn lock_file() -> io::Result<File> { pub fn lock_file() -> io::Result<File> {
let path = SWITCH_HOME_PATH.lock().clone().unwrap().join(".lock"); let path = SWITCH_HOME_PATH.lock().clone().unwrap().join(".lock");
Ok(File::create(path)?) Ok(File::create(path)?)
+4 -1
View File
@@ -60,7 +60,7 @@ enum Commands {
Status, Status,
} }
#[derive(Parser, Debug)] #[derive(Parser, Debug,Default)]
pub struct StartArgs { pub struct StartArgs {
/// 不超过64个字符 /// 不超过64个字符
/// 相同token的设备之间才能通信。 /// 相同token的设备之间才能通信。
@@ -95,6 +95,9 @@ pub struct StartArgs {
/// Output the log in the "home/.switch_desktop" directory /// Output the log in the "home/.switch_desktop" directory
#[arg(long)] #[arg(long)]
log: bool, log: bool,
/// 使用tap网卡
#[arg(long)]
tap: Option<bool>,
} }
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
+5 -3
View File
@@ -24,18 +24,19 @@ pub fn main0(base_args: BaseArgs) {
} }
let config = Config::new( let config = Config::new(
start_config.tap,
start_config.token.clone(), start_config.token.clone(),
start_config.device_id.clone(), start_config.device_id.clone(),
start_config.name.clone(), start_config.name.clone(),
start_config.server, start_config.server,
start_config.nat_test_server.clone(), start_config.nat_test_server.clone(),
); );
let nat_test_server = start_config.nat_test_server.iter().map(|v| v.to_string()).collect::<Vec<String>>();
let args_config = config::ArgsConfig::new( let args_config = config::ArgsConfig::new(
start_config.tap,
start_config.token.clone(), start_config.token.clone(),
start_config.name.clone(), start_config.name.clone(),
start_config.server.to_string(), start_config.server,
nat_test_server, &start_config.nat_test_server,
start_config.device_id.clone(), start_config.device_id.clone(),
); );
let lock = match config::lock_file() { let lock = match config::lock_file() {
@@ -93,6 +94,7 @@ pub fn main0(base_args: BaseArgs) {
lock.unlock().unwrap(); lock.unlock().unwrap();
} }
Err(e) => { Err(e) => {
println!("{}", style(&e).red());
log::error!("{:?}", e); log::error!("{:?}", e);
} }
} }
+6 -4
View File
@@ -68,11 +68,11 @@ pub fn main0(base_args: BaseArgs) {
match service_state() { match service_state() {
Ok(state) => { Ok(state) => {
if state == ServiceState::Stopped { if state == ServiceState::Stopped {
if let Err(e) = config::save_config(config::ArgsConfig::new( if let Err(e) = config::save_config(config::ArgsConfig::new(start_config.tap,
start_config.token.clone(), start_config.token.clone(),
start_config.name.clone(), start_config.name.clone(),
start_config.server.to_string(), start_config.server,
start_config.nat_test_server.iter().map(|v| v.to_string()).collect::<Vec<String>>(), &start_config.nat_test_server,
start_config.device_id.clone(), start_config.device_id.clone(),
)) { )) {
log::error!("{:?}",e); log::error!("{:?}",e);
@@ -103,6 +103,7 @@ pub fn main0(base_args: BaseArgs) {
style("服务未安装,在当前进程启动(The service is not installed and started in the current process)").red() style("服务未安装,在当前进程启动(The service is not installed and started in the current process)").red()
); );
let config = Config::new( let config = Config::new(
start_config.tap,
start_config.token, start_config.token,
start_config.device_id, start_config.device_id,
start_config.name, start_config.name,
@@ -144,7 +145,8 @@ pub fn main0(base_args: BaseArgs) {
} }
} }
Err(e) => { Err(e) => {
println!("{}", style(e).red()); println!("{}", style(&e).red());
log::error!("{:?}", e);
} }
}; };
pause(); pause();
+14 -29
View File
@@ -2,7 +2,6 @@
// extern crate windows_service; // extern crate windows_service;
use std::ffi::OsString; use std::ffi::OsString;
use std::net::ToSocketAddrs;
use std::sync::Arc; use std::sync::Arc;
use std::thread; use std::thread;
use std::time::Duration; use std::time::Duration;
@@ -15,8 +14,7 @@ use windows_service::service_control_handler::ServiceControlHandlerResult;
use switch::core::{Config, Switch}; use switch::core::{Config, Switch};
use crate::config; use crate::{config, StartArgs};
use crate::windows::config::read_config;
use crate::windows::SERVICE_NAME; use crate::windows::SERVICE_NAME;
define_windows_service!(ffi_service_main, switch_service_main); define_windows_service!(ffi_service_main, switch_service_main);
@@ -93,31 +91,16 @@ fn service_main() -> windows_service::Result<()> {
} }
fn start_switch() -> switch::Result<Arc<Switch>> { fn start_switch() -> switch::Result<Arc<Switch>> {
if let Some(config) = read_config() { match config::default_config(StartArgs::default()) {
let device_id = config.device_id; Ok(start_config) => {
if device_id.trim().is_empty() {
return Err(switch::error::Error::Stop("Device id error".to_string()));
}
let server_address = if let Some(server_address) = config.server
.to_socket_addrs()?
.next() {
server_address
} else {
return Err(switch::error::Error::Stop("server address error".to_string()));
};
let nat_test_server = config.nat_test_server.iter()
.flat_map(|a| a.to_socket_addrs())
.flatten()
.collect::<Vec<_>>();
if nat_test_server.is_empty() {
return Err(switch::error::Error::Stop("nat test server address error".to_string()));
}
let config = Config::new( let config = Config::new(
config.token, start_config.tap,
device_id, start_config.token,
config.name, start_config.device_id,
server_address, start_config.name,
nat_test_server); start_config.server,
start_config.nat_test_server,
);
let switch = Switch::start(config)?; let switch = Switch::start(config)?;
log::info!("switch-service服务启动"); log::info!("switch-service服务启动");
let switch = Arc::new(switch); let switch = Arc::new(switch);
@@ -132,8 +115,10 @@ fn start_switch() -> switch::Result<Arc<Switch>> {
} }
}); });
Ok(switch) Ok(switch)
} else { }
Err(switch::error::Error::Stop("配置文件为空".to_string())) Err(e) => {
return Err(switch::error::Error::Stop(e));
}
} }
} }
+2 -1
View File
@@ -27,6 +27,7 @@ chrono = "0.4.23"
#moka = "0.9.6" #moka = "0.9.6"
protobuf = "3.2.0" protobuf = "3.2.0"
#local-ip-address = "0.4.9" #local-ip-address = "0.4.9"
socket2 ={ version = "0.5.2", features = ["all"] }
#mio = {version = "0.8.6",features = ["os-poll", "net"]} #mio = {version = "0.8.6",features = ["os-poll", "net"]}
#tokio = { version = "1.24.1", features = ["full"] } #tokio = { version = "1.24.1", features = ["full"] }
@@ -34,7 +35,7 @@ protobuf = "3.2.0"
tun = { path = "./rust-tun" } tun = { path = "./rust-tun" }
[target.'cfg(target_os = "windows")'.dependencies] [target.'cfg(target_os = "windows")'.dependencies]
wintun = { path = "./wintun" } win-tun-tap = {path = "./win-tun-tap"}
libloading = "0.7.4" libloading = "0.7.4"
[build-dependencies] [build-dependencies]
+123
View File
@@ -0,0 +1,123 @@
use std::fmt;
/// 地址解析协议,由IP地址找到MAC地址
/// https://www.ietf.org/rfc/rfc6747.txt
/*
0 2 4 5 6 8 10 (字节)
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 硬件类型|协议类型|硬件地址长度|协议地址长度|操作类型|
| 源MAC地址 | 源ip地址 |
| 目的MAC地址 | 目的ip地址 |
*/
use crate::error::*;
pub struct ArpPacket<B> {
buffer: B,
}
impl<B: AsRef<[u8]>> ArpPacket<B> {
pub fn unchecked(buffer: B) -> Self {
Self { buffer }
}
pub fn new(buffer: B) -> Result<Self> {
if buffer.as_ref().len() != 28 {
Err(Error::InvalidPacket)?
}
let packet = Self::unchecked(buffer);
Ok(packet)
}
}
impl<B: AsRef<[u8]>> ArpPacket<B> {
/// 硬件类型 以太网类型为1
pub fn hardware_type(&self) -> u16 {
u16::from_be_bytes(self.buffer.as_ref()[0..2].try_into().unwrap())
}
/// 上层协议类型,ipv4是0x0800
pub fn protocol_type(&self) -> u16 {
u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap())
}
/// 如果是MAC地址 则长度为6
pub fn hardware_size(&self) -> u8 {
self.buffer.as_ref()[4]
}
/// 如果是IPv4 则长度为4
pub fn protocol_size(&self) -> u8 {
self.buffer.as_ref()[5]
}
/// 操作类型,请求和响应 1:ARP请求,2:ARP响应,3RARP请求,4RARP响应
pub fn op_code(&self) -> u16 {
u16::from_be_bytes(self.buffer.as_ref()[6..8].try_into().unwrap())
}
/// 发送端硬件地址,仅支持以太网
pub fn sender_hardware_addr(&self) -> &[u8] {
&self.buffer.as_ref()[8..14]
}
/// 发送端协议地址,仅支持IPv4
pub fn sender_protocol_addr(&self) -> &[u8] {
&self.buffer.as_ref()[14..18]
}
/// 接收端硬件地址,仅支持以太网
pub fn target_hardware_addr(&self) -> &[u8] {
&self.buffer.as_ref()[18..24]
}
/// 接收端协议地址,仅支持IPv4
pub fn target_protocol_addr(&self) -> &[u8] {
&self.buffer.as_ref()[24..28]
}
}
impl<B: AsRef<[u8]> + AsMut<[u8]>> ArpPacket<B> {
/// 硬件类型 以太网类型为1
pub fn set_hardware_type(&mut self, value: u16) {
self.buffer.as_mut()[0..2].copy_from_slice(&value.to_be_bytes())
}
/// 上层协议类型,ipv4是0x0800
pub fn set_protocol_type(&mut self, value: u16) {
self.buffer.as_mut()[2..4].copy_from_slice(&value.to_be_bytes())
}
/// 如果是MAC地址 则长度为6
pub fn set_hardware_size(&mut self, value: u8) {
self.buffer.as_mut()[4] = value
}
/// 如果是IPv4 则长度为4
pub fn set_protocol_size(&mut self, value: u8) {
self.buffer.as_mut()[5] = value
}
/// 操作类型,请求和响应 1:ARP请求,2:ARP响应,3RARP请求,4RARP响应
pub fn set_op_code(&mut self, value: u16) {
self.buffer.as_mut()[6..8].copy_from_slice(&value.to_be_bytes())
}
/// 发送端硬件地址,仅支持以太网
pub fn set_sender_hardware_addr(&mut self, buf: &[u8]) {
self.buffer.as_mut()[8..14].copy_from_slice(buf)
}
/// 发送端协议地址,仅支持IPv4
pub fn set_sender_protocol_addr(&mut self, buf: &[u8]) {
self.buffer.as_mut()[14..18].copy_from_slice(buf)
}
/// 接收端硬件地址,仅支持以太网
pub fn set_target_hardware_addr(&mut self, buf: &[u8]) {
self.buffer.as_mut()[18..24].copy_from_slice(buf)
}
/// 接收端协议地址,仅支持IPv4
pub fn set_target_protocol_addr(&mut self, buf: &[u8]) {
self.buffer.as_mut()[24..28].copy_from_slice(buf)
}
}
impl<B: AsRef<[u8]>> fmt::Debug for ArpPacket<B> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ArpPacket")
.field("hardware_type", &self.hardware_type())
.field("protocol_type", &self.protocol_type())
.field("hardware_size", &self.hardware_size())
.field("protocol_size", &self.protocol_size())
.field("op_code", &self.op_code())
.field("sender_hardware_addr", &self.sender_hardware_addr())
.field("sender_protocol_addr", &self.sender_protocol_addr())
.field("target_hardware_addr", &self.target_hardware_addr())
.field("target_protocol_addr", &self.target_protocol_addr())
.finish()
}
}
+1
View File
@@ -0,0 +1 @@
pub mod arp;
+2
View File
@@ -0,0 +1,2 @@
pub mod packet;
pub mod protocol;
+78
View File
@@ -0,0 +1,78 @@
use std::fmt;
use crate::error::*;
use crate::ethernet::protocol::Protocol;
/// 以太网帧协议
/// https://www.ietf.org/rfc/rfc894.txt
/*
0 6 12 14 (字节)
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 目的地址 | 源地址 | 类型 |
*/
pub struct EthernetPacket<B> {
pub buffer: B,
}
impl<B: AsRef<[u8]>> EthernetPacket<B> {
pub fn unchecked(buffer: B) -> EthernetPacket<B> {
EthernetPacket { buffer }
}
pub fn new(buffer: B) -> Result<EthernetPacket<B>> {
let packet = EthernetPacket::unchecked(buffer);
//头部固定14位
if packet.buffer.as_ref().len() < 14 {
Err(Error::SmallBuffer)?
}
Ok(packet)
}
}
impl<B: AsRef<[u8]>> EthernetPacket<B> {
/// 目的MAC地址
pub fn destination(&self) -> &[u8] {
&self.buffer.as_ref()[0..6]
}
/// 源MAC地址
pub fn source(&self) -> &[u8] {
&self.buffer.as_ref()[6..12]
}
/// 3层协议
pub fn protocol(&self) -> Protocol {
u16::from_be_bytes(self.buffer.as_ref()[12..14].try_into().unwrap()).into()
}
/// 载荷
pub fn payload(&self) -> &[u8] {
&self.buffer.as_ref()[14..]
}
}
impl<B: AsRef<[u8]> + AsMut<[u8]>> EthernetPacket<B> {
pub fn set_destination(&mut self, value: &[u8]) {
self.buffer.as_mut()[0..6].copy_from_slice(value);
}
pub fn set_source(&mut self, value: &[u8]) {
self.buffer.as_mut()[6..12].copy_from_slice(value);
}
pub fn set_protocol(&mut self, value: Protocol) {
let p: u16 = value.into();
self.buffer.as_mut()[12..14].copy_from_slice(&p.to_be_bytes())
}
pub fn payload_mut(&mut self) -> &mut [u8] {
&mut self.buffer.as_mut()[14..]
}
}
impl<B: AsRef<[u8]>> fmt::Debug for EthernetPacket<B> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("EthernetPacket")
.field("destination", &self.destination())
.field("source", &self.source())
.field("protocol", &self.protocol())
.field("payload", &self.payload())
.finish()
}
}
+141
View File
@@ -0,0 +1,141 @@
/// 以太网帧协议
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
pub enum Protocol {
///
Ipv4,
///
Arp,
///
WakeOnLan,
///
Trill,
///
DecNet,
///
Rarp,
///
AppleTalk,
///
Aarp,
///
Ipx,
///
Qnx,
///
Ipv6,
///
FlowControl,
///
CobraNet,
///
Mpls,
///
MplsMulticast,
///
PppoeDiscovery,
///
PppoeSession,
///
Vlan,
///
PBridge,
///
Lldp,
///
Ptp,
///
Cfm,
///
QinQ,
///
Unknown(u16),
}
impl From<u16> for Protocol {
fn from(value: u16) -> Protocol {
use self::Protocol::*;
match value {
0x0800 => Ipv4,
0x0806 => Arp,
0x0842 => WakeOnLan,
0x22f3 => Trill,
0x6003 => DecNet,
0x8035 => Rarp,
0x809b => AppleTalk,
0x80f3 => Aarp,
0x8137 => Ipx,
0x8204 => Qnx,
0x86dd => Ipv6,
0x8808 => FlowControl,
0x8819 => CobraNet,
0x8847 => Mpls,
0x8848 => MplsMulticast,
0x8863 => PppoeDiscovery,
0x8864 => PppoeSession,
0x8100 => Vlan,
0x88a8 => PBridge,
0x88cc => Lldp,
0x88f7 => Ptp,
0x8902 => Cfm,
0x9100 => QinQ,
n => Unknown(n),
}
}
}
impl Into<u16> for Protocol {
fn into(self) -> u16 {
use self::Protocol::*;
match self {
Ipv4 => 0x0800,
Arp => 0x0806,
WakeOnLan => 0x0842,
Trill => 0x22f3,
DecNet => 0x6003,
Rarp => 0x8035,
AppleTalk => 0x809b,
Aarp => 0x80f3,
Ipx => 0x8137,
Qnx => 0x8204,
Ipv6 => 0x86dd,
FlowControl => 0x8808,
CobraNet => 0x8819,
Mpls => 0x8847,
MplsMulticast => 0x8848,
PppoeDiscovery => 0x8863,
PppoeSession => 0x8864,
Vlan => 0x8100,
PBridge => 0x88a8,
Lldp => 0x88cc,
Ptp => 0x88f7,
Cfm => 0x8902,
QinQ => 0x9100,
Unknown(n) => n,
}
}
}
+4 -14
View File
@@ -1,7 +1,6 @@
use std::fmt; use std::fmt;
use std::net::Ipv4Addr; use std::net::Ipv4Addr;
use byteorder::{BigEndian, ReadBytesExt};
use crate::cal_checksum; use crate::cal_checksum;
use crate::error::*; use crate::error::*;
@@ -141,16 +140,12 @@ impl<B: AsRef<[u8]>> IpV4Packet<B> {
/// ip报总字节数 /// ip报总字节数
pub fn length(&self) -> u16 { pub fn length(&self) -> u16 {
(&self.buffer.as_ref()[2..]) u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap())
.read_u16::<BigEndian>()
.unwrap()
} }
/// 标识. ip报文在数据链路层可能会被拆分,同一报文的不同分组标识字段相同 /// 标识. ip报文在数据链路层可能会被拆分,同一报文的不同分组标识字段相同
pub fn id(&self) -> u16 { pub fn id(&self) -> u16 {
(&self.buffer.as_ref()[4..]) u16::from_be_bytes(self.buffer.as_ref()[4..6].try_into().unwrap())
.read_u16::<BigEndian>()
.unwrap()
} }
/// 标志 3位. /// 标志 3位.
@@ -170,10 +165,7 @@ impl<B: AsRef<[u8]>> IpV4Packet<B> {
/// 以字节为单位,用于指明分段起始点相对于包头起始点的偏移量 /// 以字节为单位,用于指明分段起始点相对于包头起始点的偏移量
/// 由于分段到达时可能错序,所以分段的偏移字段可以使接收者按照正确的顺序重组数据包 /// 由于分段到达时可能错序,所以分段的偏移字段可以使接收者按照正确的顺序重组数据包
pub fn offset(&self) -> u16 { pub fn offset(&self) -> u16 {
(&self.buffer.as_ref()[6..]) u16::from_be_bytes(self.buffer.as_ref()[6..8].try_into().unwrap()) & 0x1fff
.read_u16::<BigEndian>()
.unwrap()
& 0x1fff
} }
/// 生存时间. /// 生存时间.
@@ -189,9 +181,7 @@ impl<B: AsRef<[u8]>> IpV4Packet<B> {
/// 首部校验和 /// 首部校验和
pub fn checksum(&self) -> u16 { pub fn checksum(&self) -> u16 {
(&self.buffer.as_ref()[10..]) u16::from_be_bytes(self.buffer.as_ref()[10..12].try_into().unwrap())
.read_u16::<BigEndian>()
.unwrap()
} }
/// 验证校验和 /// 验证校验和
/// ///
+2 -1
View File
@@ -8,7 +8,8 @@ pub mod icmp;
pub mod ip; pub mod ip;
pub mod tcp; pub mod tcp;
pub mod udp; pub mod udp;
pub mod ethernet;
pub mod arp;
// pub enum IpUpperLayer<B> { // pub enum IpUpperLayer<B> {
// UDP(UdpPacket<B>), // UDP(UdpPacket<B>),
// Unknown(B), // Unknown(B),
+11
View File
@@ -76,6 +76,17 @@ impl<B: AsRef<[u8]>> TcpPacket<B> {
} }
} }
impl<B: AsRef<[u8]> + AsMut<[u8]>> TcpPacket<B> {
fn set_checksum(&mut self, value: u16) {
self.buffer.as_mut()[16..18].copy_from_slice(&value.to_be_bytes())
}
/// 更新校验和
pub fn update_checksum(&mut self) {
//先将校验和置0
self.set_checksum(0);
self.set_checksum(self.cal_checksum())
}
}
impl<B: AsRef<[u8]>> TcpPacket<B> { impl<B: AsRef<[u8]>> TcpPacket<B> {
/// 源端口 /// 源端口
pub fn source_port(&self) -> u16 { pub fn source_port(&self) -> u16 {
+1 -1
View File
@@ -11,7 +11,7 @@
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION // TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
// //
// 0. You just DO WHAT THE FUCK YOU WANT TO. // 0. You just DO WHAT THE FUCK YOU WANT TO.
#![cfg(unix)]
mod error; mod error;
pub use crate::error::*; pub use crate::error::*;
@@ -112,6 +112,11 @@ impl AsRawFd for Reader {
self.0.as_raw_fd() self.0.as_raw_fd()
} }
} }
impl AsRawFd for Writer {
fn as_raw_fd(&self) -> RawFd {
self.0.as_raw_fd()
}
}
// //
// impl AsRawFd for Writer { // impl AsRawFd for Writer {
// fn as_raw_fd(&self) -> RawFd { // fn as_raw_fd(&self) -> RawFd {
+45 -17
View File
@@ -7,15 +7,17 @@ use parking_lot::Mutex;
use p2p_channel::boot::Boot; use p2p_channel::boot::Boot;
use p2p_channel::channel::{Channel, Route, RouteKey}; use p2p_channel::channel::{Channel, Route, RouteKey};
use p2p_channel::punch::NatInfo; use p2p_channel::punch::NatInfo;
use crate::handle::{ConnectStatus, CurrentDeviceInfo, heartbeat_handler, PeerDeviceInfo, punch_handler, recv_handler, registration_handler, tun_handler}; use crate::handle::{ConnectStatus, CurrentDeviceInfo, heartbeat_handler, PeerDeviceInfo, punch_handler, recv_handler, registration_handler, tap_handler, tun_handler};
use crate::nat::NatTest; use crate::nat::NatTest;
use crate::tun_device; use crate::{tap_device, tun_device};
use crate::tun_device::TunReader; use crate::tap_device::TapWriter;
use crate::tun_device::TunWriter;
pub struct Switch { pub struct Switch {
name: String, name: String,
current_device: Arc<AtomicCell<CurrentDeviceInfo>>, current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
tun_reader: TunReader, tun_writer: Option<TunWriter>,
tap_writer: Option<TapWriter>,
nat_channel: Channel<Ipv4Addr>, nat_channel: Channel<Ipv4Addr>,
/// 0. 机器纪元,每一次上线或者下线都会增1,用于感知网络中机器变化 /// 0. 机器纪元,每一次上线或者下线都会增1,用于感知网络中机器变化
/// 服务端和客户端的不一致,则服务端会推送新的设备列表 /// 服务端和客户端的不一致,则服务端会推送新的设备列表
@@ -38,14 +40,40 @@ impl Switch {
let virtual_ip = Ipv4Addr::from(response.virtual_ip); let virtual_ip = Ipv4Addr::from(response.virtual_ip);
let virtual_gateway = Ipv4Addr::from(response.virtual_gateway); let virtual_gateway = Ipv4Addr::from(response.virtual_gateway);
let virtual_netmask = Ipv4Addr::from(response.virtual_netmask); let virtual_netmask = Ipv4Addr::from(response.virtual_netmask);
let current_device = Arc::new(AtomicCell::new(CurrentDeviceInfo::new(virtual_ip, virtual_gateway, virtual_netmask, config.server_address)));
let local_ip = crate::nat::local_ip()?; let local_ip = crate::nat::local_ip()?;
let local_port = channel.local_addr()?.port(); let local_port = channel.local_addr()?.port();
// NAT检测 // NAT检测
let nat_test = NatTest::new(config.nat_test_server.clone(), Ipv4Addr::from(response.public_ip), response.public_port as u16, local_ip, local_port); let nat_test = NatTest::new(config.nat_test_server.clone(), Ipv4Addr::from(response.public_ip), response.public_port as u16, local_ip, local_port);
let (current_device, tun_writer, tap_writer) = if config.tap {
#[cfg(windows)]
{
//删除switch的tun网卡避免ip冲突,因为非正常退出会保留网卡
tun_device::delete_tun();
}
let (tap_writer, tap_reader, mac) = tap_device::create_tap(virtual_ip, virtual_netmask, virtual_gateway)?;
let current_device = Arc::new(AtomicCell::new(CurrentDeviceInfo::new(virtual_ip, virtual_gateway, virtual_netmask,
config.server_address, mac)));
//tap数据处理
tap_handler::start(channel.sender()?, tap_reader.clone(), tap_writer.clone(), current_device.clone());
(current_device, None, Some(tap_writer))
} else {
#[cfg(windows)]
{
//删除switch的tap网卡避免ip冲突,非正常退出会保留网卡
tap_device::delete_tap();
}
// tun通道 // tun通道
let (tun_writer, tun_reader) = tun_device::create_tun(virtual_ip, virtual_netmask, virtual_gateway)?; let (tun_writer, tun_reader) = tun_device::create_tun(virtual_ip, virtual_netmask, virtual_gateway)?;
let current_device = Arc::new(AtomicCell::new(CurrentDeviceInfo::new(virtual_ip, virtual_gateway, virtual_netmask, config.server_address, [0, 0, 0, 0, 0, 0])));
//tun数据接收处理
tun_handler::start(channel.sender()?, tun_reader.clone(), tun_writer.clone(), current_device.clone());
(current_device, Some(tun_writer), None)
};
//外部数据接收处理
let channel_recv_handler = recv_handler::RecvHandler::new(channel.try_clone()?, current_device.clone(), device_list.clone(), register.clone(),
nat_test.clone(), tun_writer.clone(), tap_writer.clone(), connect_status.clone(), peer_nat_info_map.clone());
recv_handler::start(channel_recv_handler);
// 定时心跳 // 定时心跳
heartbeat_handler::start_heartbeat(channel.sender()?, device_list.clone(), current_device.clone()); heartbeat_handler::start_heartbeat(channel.sender()?, device_list.clone(), current_device.clone());
// 空闲检查 // 空闲检查
@@ -54,19 +82,12 @@ impl Switch {
punch_handler::start_cone(punch.try_clone()?, current_device.clone()); punch_handler::start_cone(punch.try_clone()?, current_device.clone());
punch_handler::start_symmetric(punch, current_device.clone()); punch_handler::start_symmetric(punch, current_device.clone());
punch_handler::start_punch(nat_test.clone(), device_list.clone(), channel.sender()?, current_device.clone()); punch_handler::start_punch(nat_test.clone(), device_list.clone(), channel.sender()?, current_device.clone());
//tun数据接收处理
tun_handler::start(channel.sender()?, tun_reader.clone(), tun_writer.clone(), current_device.clone());
//外部数据接收处理
let channel_recv_handler = recv_handler::RecvHandler::new(channel.try_clone()?, current_device.clone(), device_list.clone(), register.clone(),
nat_test.clone(), tun_writer.clone(), connect_status.clone(), peer_nat_info_map.clone());
for _ in 0..2 {
recv_handler::start(channel_recv_handler.try_clone()?);
}
log::info!("switch启动成功"); log::info!("switch启动成功");
Ok(Switch { Ok(Switch {
name: config.name, name: config.name,
current_device, current_device,
tun_reader, tun_writer,
tap_writer,
nat_channel: channel, nat_channel: channel,
nat_test, nat_test,
device_list, device_list,
@@ -108,7 +129,12 @@ impl Switch {
self.nat_channel.route_table() self.nat_channel.route_table()
} }
pub fn stop(&self) -> io::Result<()> { pub fn stop(&self) -> io::Result<()> {
self.tun_reader.close(); if let Some(tap) = &self.tap_writer {
tap.close()?;
}
if let Some(tun) = &self.tun_writer {
tun.close()?;
}
self.nat_channel.close()?; self.nat_channel.close()?;
Ok(()) Ok(())
} }
@@ -116,6 +142,7 @@ impl Switch {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Config { pub struct Config {
pub tap: bool,
pub token: String, pub token: String,
pub device_id: String, pub device_id: String,
pub name: String, pub name: String,
@@ -124,12 +151,13 @@ pub struct Config {
} }
impl Config { impl Config {
pub fn new(token: String, pub fn new(tap: bool, token: String,
device_id: String, device_id: String,
name: String, name: String,
server_address: SocketAddr, server_address: SocketAddr,
nat_test_server: Vec<SocketAddr>, ) -> Self { nat_test_server: Vec<SocketAddr>, ) -> Self {
Self { Self {
tap,
token, token,
device_id, device_id,
name, name,
+40 -13
View File
@@ -1,45 +1,60 @@
use std::{io, thread};
use std::net::Ipv4Addr; use std::net::Ipv4Addr;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use std::{io, thread};
use chrono::Local; use chrono::Local;
use crossbeam::atomic::AtomicCell; use crossbeam::atomic::AtomicCell;
use parking_lot::Mutex; use parking_lot::Mutex;
use rand::prelude::SliceRandom; use rand::prelude::SliceRandom;
use p2p_channel::channel::Route;
use p2p_channel::channel::sender::Sender; use p2p_channel::channel::sender::Sender;
use p2p_channel::channel::Route;
use p2p_channel::idle::Idle; use p2p_channel::idle::Idle;
use crate::handle::{CurrentDeviceInfo, PeerDeviceInfo}; use crate::handle::{CurrentDeviceInfo, PeerDeviceInfo};
use crate::protocol::{control_packet, MAX_TTL, NetPacket, Protocol, Version};
use crate::protocol::control_packet::PingPacket; use crate::protocol::control_packet::PingPacket;
use crate::protocol::{control_packet, NetPacket, Protocol, Version, MAX_TTL};
pub fn start_idle(idle: Idle<Ipv4Addr>, sender: Sender<Ipv4Addr>) { pub fn start_idle(idle: Idle<Ipv4Addr>, sender: Sender<Ipv4Addr>) {
thread::Builder::new().name("idle".into()).spawn(move || { thread::Builder::new()
.name("idle".into())
.spawn(move || {
if let Err(e) = start_idle_(idle, sender) { if let Err(e) = start_idle_(idle, sender) {
log::info!("空闲检测线程停止:{:?}", e); log::info!("空闲检测线程停止:{:?}", e);
} }
}).unwrap(); })
.unwrap();
} }
fn start_idle_(idle: Idle<Ipv4Addr>, sender: Sender<Ipv4Addr>) -> io::Result<()> { fn start_idle_(idle: Idle<Ipv4Addr>, sender: Sender<Ipv4Addr>) -> io::Result<()> {
loop { loop {
let (idle_status, peer_ips, route) = idle.next_idle()?; let (idle_status, peer_ips, route) = idle.next_idle()?;
log::warn!("peer_ip:{:?},route:{:?},idle_status:{:?}",peer_ips,route,idle_status); log::warn!(
"peer_ip:{:?},route:{:?},idle_status:{:?}",
peer_ips,
route,
idle_status
);
for peer_ip in peer_ips { for peer_ip in peer_ips {
sender.remove_route(&peer_ip); sender.remove_route(&peer_ip);
} }
} }
} }
pub fn start_heartbeat(sender: Sender<Ipv4Addr>, device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>, current_device: Arc<AtomicCell<CurrentDeviceInfo>>) { pub fn start_heartbeat(
thread::Builder::new().name("heartbeat".into()).spawn(move || { sender: Sender<Ipv4Addr>,
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
) {
thread::Builder::new()
.name("heartbeat".into())
.spawn(move || {
if let Err(e) = start_heartbeat_(sender, device_list, current_device) { if let Err(e) = start_heartbeat_(sender, device_list, current_device) {
log::info!("空闲检测线程停止:{:?}", e); log::info!("空闲检测线程停止:{:?}", e);
} }
}).unwrap(); })
.unwrap();
} }
fn set_now_time(packet: &mut NetPacket<[u8; 16]>) -> io::Result<()> { fn set_now_time(packet: &mut NetPacket<[u8; 16]>) -> io::Result<()> {
@@ -49,7 +64,11 @@ fn set_now_time(packet: &mut NetPacket<[u8; 16]>) -> io::Result<()> {
Ok(()) Ok(())
} }
fn start_heartbeat_(sender: Sender<Ipv4Addr>, device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>, current_device: Arc<AtomicCell<CurrentDeviceInfo>>) -> io::Result<()> { fn start_heartbeat_(
sender: Sender<Ipv4Addr>,
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
) -> io::Result<()> {
let mut net_packet = NetPacket::new([0u8; 16])?; let mut net_packet = NetPacket::new([0u8; 16])?;
net_packet.set_version(Version::V1); net_packet.set_version(Version::V1);
net_packet.set_protocol(Protocol::Control); net_packet.set_protocol(Protocol::Control);
@@ -71,7 +90,10 @@ fn start_heartbeat_(sender: Sender<Ipv4Addr>, device_list: Arc<Mutex<(u16, Vec<P
set_now_time(&mut net_packet)?; set_now_time(&mut net_packet)?;
net_packet.first_set_ttl(MAX_TTL); net_packet.first_set_ttl(MAX_TTL);
net_packet.set_destination(peer.virtual_ip); net_packet.set_destination(peer.virtual_ip);
if sender.send_to_id(net_packet.buffer(), &peer.virtual_ip).is_err() { if sender
.send_to_id(net_packet.buffer(), &peer.virtual_ip)
.is_err()
{
//没有路由则发送到网关 //没有路由则发送到网关
let _ = sender.send_to_addr(net_packet.buffer(), current_device.connect_server); let _ = sender.send_to_addr(net_packet.buffer(), current_device.connect_server);
//再随机发送到其他地址,看有没有客户端符合转发条件 //再随机发送到其他地址,看有没有客户端符合转发条件
@@ -97,8 +119,13 @@ fn start_heartbeat_(sender: Sender<Ipv4Addr>, device_list: Arc<Mutex<(u16, Vec<P
} }
set_now_time(&mut net_packet)?; set_now_time(&mut net_packet)?;
net_packet.set_destination(current_device.virtual_gateway()); net_packet.set_destination(current_device.virtual_gateway());
if let Err(e) = sender.send_to_addr(net_packet.buffer(), current_device.connect_server) { if let Err(e) = sender.send_to_addr(net_packet.buffer(), current_device.connect_server)
log::warn!("connect_server:{:?},e:{:?}",current_device.connect_server,e); {
log::warn!(
"connect_server:{:?},e:{:?}",
current_device.connect_server,
e
);
} }
} else { } else {
for (peer_ip, route) in sender.route_table().iter() { for (peer_ip, route) in sender.route_table().iter() {
+6 -6
View File
@@ -1,10 +1,11 @@
use std::net::{Ipv4Addr, SocketAddr}; use std::net::{Ipv4Addr, SocketAddr};
pub mod heartbeat_handler; pub mod heartbeat_handler;
pub mod punch_handler;
pub mod registration_handler;
pub mod tun_handler; pub mod tun_handler;
pub mod tap_handler;
pub mod punch_handler;
pub mod recv_handler; pub mod recv_handler;
pub mod registration_handler;
/// 是否在一个网段 /// 是否在一个网段
fn check_dest(dest: Ipv4Addr, virtual_netmask: Ipv4Addr, virtual_network: Ipv4Addr) -> bool { fn check_dest(dest: Ipv4Addr, virtual_netmask: Ipv4Addr, virtual_network: Ipv4Addr) -> bool {
@@ -70,6 +71,7 @@ pub struct CurrentDeviceInfo {
pub broadcast_address: Ipv4Addr, pub broadcast_address: Ipv4Addr,
//链接的服务器地址 //链接的服务器地址
pub connect_server: SocketAddr, pub connect_server: SocketAddr,
pub mac:[u8;6]
} }
impl CurrentDeviceInfo { impl CurrentDeviceInfo {
@@ -78,6 +80,7 @@ impl CurrentDeviceInfo {
virtual_gateway: Ipv4Addr, virtual_gateway: Ipv4Addr,
virtual_netmask: Ipv4Addr, virtual_netmask: Ipv4Addr,
connect_server: SocketAddr, connect_server: SocketAddr,
mac:[u8;6],
) -> Self { ) -> Self {
let broadcast_address = (!u32::from_be_bytes(virtual_netmask.octets())) let broadcast_address = (!u32::from_be_bytes(virtual_netmask.octets()))
| u32::from_be_bytes(virtual_gateway.octets()); | u32::from_be_bytes(virtual_gateway.octets());
@@ -92,6 +95,7 @@ impl CurrentDeviceInfo {
virtual_network, virtual_network,
broadcast_address, broadcast_address,
connect_server, connect_server,
mac
} }
} }
#[inline] #[inline]
@@ -103,7 +107,3 @@ impl CurrentDeviceInfo {
self.virtual_gateway self.virtual_gateway
} }
} }
+55 -28
View File
@@ -1,35 +1,45 @@
use std::{io, thread};
use std::net::{IpAddr, Ipv4Addr};
use std::sync::Arc;
use std::time::Duration;
use crossbeam::atomic::AtomicCell;
use parking_lot::Mutex;
use protobuf::Message;
use rand::prelude::SliceRandom;
use p2p_channel::channel::sender::Sender;
use p2p_channel::punch::{NatInfo, NatType, Punch};
use crate::handle::{CurrentDeviceInfo, PeerDeviceInfo}; use crate::handle::{CurrentDeviceInfo, PeerDeviceInfo};
use crate::nat::NatTest; use crate::nat::NatTest;
use crate::proto::message::{PunchInfo, PunchNatType}; use crate::proto::message::{PunchInfo, PunchNatType};
use crate::protocol::{control_packet, MAX_TTL, NetPacket, Protocol, turn_packet, Version}; use crate::protocol::{control_packet, turn_packet, NetPacket, Protocol, Version, MAX_TTL};
use crossbeam::atomic::AtomicCell;
use p2p_channel::channel::sender::Sender;
use p2p_channel::punch::{NatInfo, NatType, Punch};
use parking_lot::Mutex;
use protobuf::Message;
use rand::prelude::SliceRandom;
use std::net::{IpAddr, Ipv4Addr};
use std::sync::Arc;
use std::time::Duration;
use std::{io, thread};
pub fn start_cone(punch: Punch<Ipv4Addr>, current_device: Arc<AtomicCell<CurrentDeviceInfo>>) { pub fn start_cone(punch: Punch<Ipv4Addr>, current_device: Arc<AtomicCell<CurrentDeviceInfo>>) {
thread::Builder::new().name("punch-cone".into()).spawn(move || { thread::Builder::new()
.name("punch-cone".into())
.spawn(move || {
if let Err(e) = start_(true, punch, current_device) { if let Err(e) = start_(true, punch, current_device) {
log::warn!("锥形网络打洞处理线程停止 {:?}", e); log::warn!("锥形网络打洞处理线程停止 {:?}", e);
} }
}).unwrap(); })
.unwrap();
} }
pub fn start_symmetric(punch: Punch<Ipv4Addr>, current_device: Arc<AtomicCell<CurrentDeviceInfo>>) { pub fn start_symmetric(punch: Punch<Ipv4Addr>, current_device: Arc<AtomicCell<CurrentDeviceInfo>>) {
thread::Builder::new().name("punch-symmetric".into()).spawn(move || { thread::Builder::new()
.name("punch-symmetric".into())
.spawn(move || {
if let Err(e) = start_(false, punch, current_device) { if let Err(e) = start_(false, punch, current_device) {
log::warn!("对称网络打洞处理线程停止 {:?}", e); log::warn!("对称网络打洞处理线程停止 {:?}", e);
} }
}).unwrap(); })
.unwrap();
} }
fn start_(is_cone: bool, mut punch: Punch<Ipv4Addr>, current_device: Arc<AtomicCell<CurrentDeviceInfo>>) -> io::Result<()> { fn start_(
is_cone: bool,
mut punch: Punch<Ipv4Addr>,
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
) -> io::Result<()> {
let mut packet = NetPacket::new([0u8; 12])?; let mut packet = NetPacket::new([0u8; 12])?;
packet.set_version(Version::V1); packet.set_version(Version::V1);
packet.first_set_ttl(1); packet.first_set_ttl(1);
@@ -56,15 +66,28 @@ fn start_(is_cone: bool, mut punch: Punch<Ipv4Addr>, current_device: Arc<AtomicC
} }
} }
pub fn start_punch(nat_test: NatTest, device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>, sender: Sender<Ipv4Addr>, current_device: Arc<AtomicCell<CurrentDeviceInfo>>) { pub fn start_punch(
thread::Builder::new().name("punch-send-request".into()).spawn(move || { nat_test: NatTest,
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
sender: Sender<Ipv4Addr>,
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
) {
thread::Builder::new()
.name("punch-send-request".into())
.spawn(move || {
if let Err(e) = start_punch_(nat_test, device_list, sender, current_device) { if let Err(e) = start_punch_(nat_test, device_list, sender, current_device) {
log::warn!("对称网络打洞处理线程停止 {:?}", e); log::warn!("对称网络打洞处理线程停止 {:?}", e);
} }
}).unwrap(); })
.unwrap();
} }
fn start_punch_(nat_test: NatTest, device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>, sender: Sender<Ipv4Addr>, current_device: Arc<AtomicCell<CurrentDeviceInfo>>) -> crate::Result<()> { fn start_punch_(
nat_test: NatTest,
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
sender: Sender<Ipv4Addr>,
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
) -> crate::Result<()> {
loop { loop {
if sender.is_close() { if sender.is_close() {
return Ok(()); return Ok(());
@@ -104,19 +127,23 @@ fn start_punch_(nat_test: NatTest, device_list: Arc<Mutex<(u16, Vec<PeerDeviceIn
} }
} }
pub fn punch_packet(virtual_ip: Ipv4Addr, nat_info: &NatInfo, dest: Ipv4Addr) -> crate::Result<Vec<u8>> { pub fn punch_packet(
virtual_ip: Ipv4Addr,
nat_info: &NatInfo,
dest: Ipv4Addr,
) -> crate::Result<Vec<u8>> {
let mut punch_reply = PunchInfo::new(); let mut punch_reply = PunchInfo::new();
punch_reply.reply = false; punch_reply.reply = false;
punch_reply.public_ip_list = nat_info.public_ips.iter().map(|i| { punch_reply.public_ip_list = nat_info
match i { .public_ips
IpAddr::V4(ip) => { .iter()
u32::from_be_bytes(ip.octets()) .map(|i| match i {
} IpAddr::V4(ip) => u32::from_be_bytes(ip.octets()),
IpAddr::V6(_) => { IpAddr::V6(_) => {
panic!() panic!()
} }
} })
}).collect(); .collect();
punch_reply.public_port = nat_info.public_port as u32; punch_reply.public_port = nat_info.public_port as u32;
punch_reply.public_port_range = nat_info.public_port_range as u32; punch_reply.public_port_range = nat_info.public_port_range as u32;
punch_reply.local_ip = match nat_info.local_ip { punch_reply.local_ip = match nat_info.local_ip {
+34 -6
View File
@@ -10,6 +10,7 @@ use protobuf::Message;
use p2p_channel::channel::{Channel, Route, RouteKey}; use p2p_channel::channel::{Channel, Route, RouteKey};
use p2p_channel::punch::NatInfo; use p2p_channel::punch::NatInfo;
use packet::ethernet;
use packet::icmp::{icmp, Kind}; use packet::icmp::{icmp, Kind};
use packet::ip::ipv4; use packet::ip::ipv4;
use packet::ip::ipv4::packet::IpV4Packet; use packet::ip::ipv4::packet::IpV4Packet;
@@ -23,6 +24,7 @@ use crate::proto::message::{DeviceList, PunchInfo, PunchNatType, RegistrationRes
use crate::protocol::{control_packet, MAX_TTL, NetPacket, Protocol, service_packet, turn_packet, Version}; use crate::protocol::{control_packet, MAX_TTL, NetPacket, Protocol, service_packet, turn_packet, Version};
use crate::protocol::control_packet::ControlPacket; use crate::protocol::control_packet::ControlPacket;
use crate::protocol::error_packet::InErrorPacket; use crate::protocol::error_packet::InErrorPacket;
use crate::tap_device::TapWriter;
use crate::tun_device::TunWriter; use crate::tun_device::TunWriter;
pub fn start(mut handler: RecvHandler) { pub fn start(mut handler: RecvHandler) {
@@ -57,7 +59,8 @@ pub struct RecvHandler {
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>, device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
register: Arc<Register>, register: Arc<Register>,
nat_test: NatTest, nat_test: NatTest,
tun_writer: TunWriter, tun_writer: Option<TunWriter>,
tap_writer: Option<TapWriter>,
connect_status: Arc<AtomicCell<ConnectStatus>>, connect_status: Arc<AtomicCell<ConnectStatus>>,
peer_nat_info_map: Arc<SkipMap<Ipv4Addr, NatInfo>>, peer_nat_info_map: Arc<SkipMap<Ipv4Addr, NatInfo>>,
} }
@@ -68,7 +71,8 @@ impl RecvHandler {
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>, device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
register: Arc<Register>, register: Arc<Register>,
nat_test: NatTest, nat_test: NatTest,
tun_writer: TunWriter, tun_writer: Option<TunWriter>,
tap_writer: Option<TapWriter>,
connect_status: Arc<AtomicCell<ConnectStatus>>, connect_status: Arc<AtomicCell<ConnectStatus>>,
peer_nat_info_map: Arc<SkipMap<Ipv4Addr, NatInfo>>, peer_nat_info_map: Arc<SkipMap<Ipv4Addr, NatInfo>>,
) -> Self { ) -> Self {
@@ -79,6 +83,7 @@ impl RecvHandler {
register, register,
nat_test, nat_test,
tun_writer, tun_writer,
tap_writer,
connect_status, connect_status,
peer_nat_info_map, peer_nat_info_map,
} }
@@ -91,6 +96,7 @@ impl RecvHandler {
register: self.register.clone(), register: self.register.clone(),
nat_test: self.nat_test.clone(), nat_test: self.nat_test.clone(),
tun_writer: self.tun_writer.clone(), tun_writer: self.tun_writer.clone(),
tap_writer: self.tap_writer.clone(),
connect_status: self.connect_status.clone(), connect_status: self.connect_status.clone(),
peer_nat_info_map: self.peer_nat_info_map.clone(), peer_nat_info_map: self.peer_nat_info_map.clone(),
}) })
@@ -103,6 +109,7 @@ impl RecvHandler {
if net_packet.ttl() == 0 { if net_packet.ttl() == 0 {
return Ok(()); return Ok(());
} }
net_packet.set_ttl(net_packet.ttl() - 1);
let source = net_packet.source(); let source = net_packet.source();
let current_device = self.current_device.load(); let current_device = self.current_device.load();
if source == current_device.virtual_ip() { if source == current_device.virtual_ip() {
@@ -122,7 +129,6 @@ impl RecvHandler {
let ttl = net_packet.ttl(); let ttl = net_packet.ttl();
if ttl > 1 { if ttl > 1 {
// 转发 // 转发
net_packet.set_ttl(ttl - 1);
if let Some(route) = self.channel.route(&destination) { if let Some(route) = self.channel.route(&destination) {
if route.metric <= net_packet.ttl() { if route.metric <= net_packet.ttl() {
self.channel.send_to_route(net_packet.buffer(), &route.route_key())?; self.channel.send_to_route(net_packet.buffer(), &route.route_key())?;
@@ -138,6 +144,9 @@ impl RecvHandler {
match net_packet.protocol() { match net_packet.protocol() {
Protocol::Ipv4Turn => { Protocol::Ipv4Turn => {
let mut ipv4 = IpV4Packet::new(net_packet.payload_mut())?; let mut ipv4 = IpV4Packet::new(net_packet.payload_mut())?;
if ipv4.destination_ip() != destination {
//todo 外部数据转发
} else {
if ipv4.protocol() == ipv4::protocol::Protocol::Icmp { if ipv4.protocol() == ipv4::protocol::Protocol::Icmp {
let mut icmp_packet = icmp::IcmpPacket::new(ipv4.payload_mut())?; let mut icmp_packet = icmp::IcmpPacket::new(ipv4.payload_mut())?;
if icmp_packet.kind() == Kind::EchoRequest { if icmp_packet.kind() == Kind::EchoRequest {
@@ -153,7 +162,20 @@ impl RecvHandler {
return Ok(()); return Ok(());
} }
} }
self.tun_writer.write(net_packet.payload())?; if let Some(tun_writer) = &self.tun_writer {
tun_writer.write(net_packet.payload())?;
} else {
if let Some(tap_writer) = &self.tap_writer {
let mut ethernet_packet = ethernet::packet::EthernetPacket::unchecked(vec![0; 14 + ipv4.buffer.len()]);
let source = source.octets();
ethernet_packet.set_source(&[source[0], source[1], source[2], source[3], 123, 234]);
ethernet_packet.set_destination(&current_device.mac);
ethernet_packet.set_protocol(ethernet::protocol::Protocol::Ipv4);
ethernet_packet.payload_mut().copy_from_slice(ipv4.buffer);
tap_writer.write(&ethernet_packet.buffer)?;
}
}
}
} }
Protocol::Service => { Protocol::Service => {
self.service(current_device, source, net_packet, route_key)?; self.service(current_device, source, net_packet, route_key)?;
@@ -195,9 +217,15 @@ impl RecvHandler {
let virtual_ip = Ipv4Addr::from(response.virtual_ip); let virtual_ip = Ipv4Addr::from(response.virtual_ip);
let virtual_gateway = Ipv4Addr::from(response.virtual_gateway); let virtual_gateway = Ipv4Addr::from(response.virtual_gateway);
let virtual_netmask = Ipv4Addr::from(response.virtual_netmask); let virtual_netmask = Ipv4Addr::from(response.virtual_netmask);
self.tun_writer.change_ip(virtual_ip, virtual_netmask, virtual_gateway, old_netmask, old_gateway)?; if let Some(tun_writer) = &self.tun_writer {
tun_writer.change_ip(virtual_ip, virtual_netmask, virtual_gateway, old_netmask, old_gateway)?;
} else {
if let Some(tap_writer) = &self.tap_writer {
tap_writer.change_ip(virtual_ip, virtual_netmask, virtual_gateway, old_netmask, old_gateway)?;
}
}
let new_current_device = CurrentDeviceInfo::new(virtual_ip, virtual_gateway, let new_current_device = CurrentDeviceInfo::new(virtual_ip, virtual_gateway,
virtual_netmask, current_device.connect_server); virtual_netmask, current_device.connect_server, current_device.mac);
if let Err(e) = self.current_device.compare_exchange(current_device, new_current_device) { if let Err(e) = self.current_device.compare_exchange(current_device, new_current_device) {
log::warn!("替换失败:{:?}",e); log::warn!("替换失败:{:?}",e);
} }
+18 -20
View File
@@ -4,9 +4,9 @@ use std::sync::atomic::{AtomicI64, Ordering};
use std::time::Duration; use std::time::Duration;
use chrono::Local; use chrono::Local;
use protobuf::Message;
use p2p_channel::channel::Channel;
use p2p_channel::channel::sender::Sender; use p2p_channel::channel::sender::Sender;
use p2p_channel::channel::Channel;
use protobuf::Message;
use crate::error::*; use crate::error::*;
use crate::proto::message::{RegistrationRequest, RegistrationResponse}; use crate::proto::message::{RegistrationRequest, RegistrationResponse};
@@ -35,20 +35,14 @@ pub fn registration(
Protocol::Service => { Protocol::Service => {
match service_packet::Protocol::from(net_packet.transport_protocol()) { match service_packet::Protocol::from(net_packet.transport_protocol()) {
service_packet::Protocol::RegistrationResponse => { service_packet::Protocol::RegistrationResponse => {
let response = let response = RegistrationResponse::parse_from_bytes(net_packet.payload())?;
RegistrationResponse::parse_from_bytes(net_packet.payload())?;
Ok(response) Ok(response)
} }
_ => { _ => Err(Error::Warn(format!("数据错误:{:?}", net_packet))),
Err(Error::Warn(format!("数据错误:{:?}", net_packet)))
}
} }
} }
Protocol::Error => { Protocol::Error => {
match InErrorPacket::new( match InErrorPacket::new(net_packet.transport_protocol(), net_packet.payload()) {
net_packet.transport_protocol(),
net_packet.payload(),
) {
Ok(e) => match e { Ok(e) => match e {
InErrorPacket::TokenError => Err(Error::Stop("token错误".to_string())), InErrorPacket::TokenError => Err(Error::Stop("token错误".to_string())),
InErrorPacket::Disconnect => Err(Error::Warn("断开连接".to_string())), InErrorPacket::Disconnect => Err(Error::Warn("断开连接".to_string())),
@@ -61,9 +55,7 @@ pub fn registration(
Err(e) => Err(Error::Warn(format!("{:?}", e))), Err(e) => Err(Error::Warn(format!("{:?}", e))),
} }
} }
_ => { _ => Err(Error::Warn(format!("数据错误:{:?}", net_packet))),
Err(Error::Warn(format!("数据错误:{:?}", net_packet)))
}
}; };
} }
@@ -99,11 +91,13 @@ pub struct Register {
} }
impl Register { impl Register {
pub fn new(sender: Sender<Ipv4Addr>, pub fn new(
sender: Sender<Ipv4Addr>,
server_address: SocketAddr, server_address: SocketAddr,
token: String, token: String,
device_id: String, device_id: String,
name: String, ) -> Self { name: String,
) -> Self {
Self { Self {
sender, sender,
server_address, server_address,
@@ -117,7 +111,8 @@ impl Register {
let last = self.time.load(Ordering::Relaxed); let last = self.time.load(Ordering::Relaxed);
let new = Local::now().timestamp_millis(); let new = Local::now().timestamp_millis();
if new - last < 1000 if new - last < 1000
|| self.time || self
.time
.compare_exchange(last, new, Ordering::Relaxed, Ordering::Relaxed) .compare_exchange(last, new, Ordering::Relaxed, Ordering::Relaxed)
.is_err() .is_err()
{ {
@@ -125,10 +120,13 @@ impl Register {
return Ok(()); return Ok(());
} }
log::info!("重新连接"); log::info!("重新连接");
let request_packet = let request_packet = registration_request_packet(
registration_request_packet(self.token.clone(), self.token.clone(),
self.device_id.clone(), self.device_id.clone(),
self.name.clone(), false).unwrap(); self.name.clone(),
false,
)
.unwrap();
let buf = request_packet.buffer(); let buf = request_packet.buffer();
self.sender.send_to_addr(buf, self.server_address)?; self.sender.send_to_addr(buf, self.server_address)?;
Ok(()) Ok(())
+112
View File
@@ -0,0 +1,112 @@
use std::net::Ipv4Addr;
use std::sync::Arc;
use std::{io, thread};
use crossbeam::atomic::AtomicCell;
use p2p_channel::channel::sender::Sender;
use packet::arp::arp::ArpPacket;
use packet::ethernet;
use packet::ethernet::packet::EthernetPacket;
use packet::icmp::icmp::IcmpPacket;
use packet::icmp::Kind;
use packet::ip::ipv4;
use packet::ip::ipv4::packet::IpV4Packet;
use crate::handle::{check_dest, CurrentDeviceInfo};
use crate::protocol::{MAX_TTL, NetPacket, Protocol, Version};
use crate::tap_device::{TapReader, TapWriter};
pub fn start(sender: Sender<Ipv4Addr>,
tap_reader: TapReader,
tap_writer: TapWriter,
current_device: Arc<AtomicCell<CurrentDeviceInfo>>, ) {
thread::Builder::new().name("tap-handler".into()).spawn(move || {
if let Err(e) = start_(sender, tap_reader, tap_writer, current_device) {
log::warn!("{:?}",e);
}
}).unwrap();
}
fn start_(sender: Sender<Ipv4Addr>,
tap_reader: TapReader,
tap_writer: TapWriter,
current_device: Arc<AtomicCell<CurrentDeviceInfo>>, ) -> io::Result<()> {
let mut net_packet = NetPacket::new(vec![0u8; 4 + 8 + 1500]).unwrap();
net_packet.set_version(Version::V1);
net_packet.set_protocol(Protocol::Ipv4Turn);
net_packet.set_transport_protocol(ipv4::protocol::Protocol::Ipv4.into());
net_packet.set_ttl(MAX_TTL);
let mut buf = [0; 2048];
loop {
let len = tap_reader.read(&mut buf)?;
if len == 0 {
continue;
}
let mut ethernet_packet = EthernetPacket::unchecked(&mut buf[..len]);
if let Err(e) = handle(&mut net_packet, &current_device, &tap_writer, &mut ethernet_packet, &sender) {
log::error!("tap handle{:?}",e);
}
}
}
fn handle(net_packet: &mut NetPacket<Vec<u8>>, current_device: &AtomicCell<CurrentDeviceInfo>, tap_writer: &TapWriter, ethernet_packet: &mut EthernetPacket<&mut [u8]>, sender: &Sender<Ipv4Addr>) -> io::Result<()> {
let current_device = current_device.load();
match ethernet_packet.protocol() {
ethernet::protocol::Protocol::Arp => {
let mut out_ethernet_packet = ethernet::packet::EthernetPacket::unchecked(ethernet_packet.buffer.to_vec());
let arp_packet = ArpPacket::unchecked(ethernet_packet.payload());
let mut out_arp_packet = ArpPacket::unchecked(out_ethernet_packet.payload_mut());
let sender_h = arp_packet.sender_hardware_addr();
let sender_p = arp_packet.sender_protocol_addr();
let target_p = arp_packet.target_protocol_addr();
if target_p == &[0, 0, 0, 0] || sender_p == &[0, 0, 0, 0] || target_p == sender_p {
return Ok(());
}
//回复一个虚假的MAC地址
out_arp_packet.set_sender_hardware_addr(&[target_p[0], target_p[1], target_p[2], target_p[3], 123, 234]);
out_arp_packet.set_sender_protocol_addr(target_p);
out_arp_packet.set_target_hardware_addr(sender_h);
out_arp_packet.set_target_protocol_addr(sender_p);
out_arp_packet.set_op_code(2);
out_ethernet_packet.set_source(&[target_p[0], target_p[1], target_p[2], target_p[3], 123, 234]);
out_ethernet_packet.set_destination(sender_h);
tap_writer.write(&out_ethernet_packet.buffer)?;
}
ethernet::protocol::Protocol::Ipv4 => {
// println!("in ethernet_packet {:?}", ethernet_packet);
let mut ipv4_packet = IpV4Packet::unchecked(ethernet_packet.payload_mut());
let src_ip = ipv4_packet.source_ip();
let dest_ip = ipv4_packet.destination_ip();
if src_ip != current_device.virtual_ip() || (!check_dest(dest_ip, current_device.virtual_netmask, current_device.virtual_network) && !dest_ip.is_broadcast()) {
return Ok(());
}
if src_ip == dest_ip {
if ipv4_packet.protocol() == ipv4::protocol::Protocol::Icmp {
let mut icmp = IcmpPacket::unchecked(ipv4_packet.payload_mut());
if icmp.kind() == Kind::EchoRequest {
icmp.set_kind(Kind::EchoReply);
icmp.update_checksum();
let src = ipv4_packet.source_ip();
ipv4_packet.set_source_ip(ipv4_packet.destination_ip());
ipv4_packet.set_destination_ip(src);
ipv4_packet.update_checksum();
tap_writer.write(ethernet_packet.buffer)?;
return Ok(());
}
}
}
net_packet.set_source(src_ip);
net_packet.set_destination(dest_ip);
let data_len = ipv4_packet.buffer.len();
net_packet.set_payload(ipv4_packet.buffer);
//优先发到直连到地址
if sender.send_to_id(&net_packet.buffer()[..(12 + data_len)], &dest_ip).is_err() {
sender.send_to_addr(&net_packet.buffer()[..(12 + data_len)], current_device.connect_server)?;
}
}
p => {
log::warn!("不支持的二层协议:{:?}",p)
}
}
Ok(())
}
+6 -6
View File
@@ -1,7 +1,7 @@
use std::{io, thread}; use std::{io, thread};
/// 接收tun数据,并且转发到udp上
use std::net::Ipv4Addr; use std::net::Ipv4Addr;
use std::sync::Arc; use std::sync::Arc;
use crossbeam::atomic::AtomicCell; use crossbeam::atomic::AtomicCell;
use p2p_channel::channel::sender::Sender; use p2p_channel::channel::sender::Sender;
@@ -15,7 +15,6 @@ use crate::handle::{check_dest, CurrentDeviceInfo};
use crate::protocol::{MAX_TTL, NetPacket, Protocol, Version}; use crate::protocol::{MAX_TTL, NetPacket, Protocol, Version};
use crate::tun_device::{TunReader, TunWriter}; use crate::tun_device::{TunReader, TunWriter};
fn icmp(tun_writer: &TunWriter, mut ipv4_packet: IpV4Packet<&mut [u8]>) -> Result<()> { fn icmp(tun_writer: &TunWriter, mut ipv4_packet: IpV4Packet<&mut [u8]>) -> Result<()> {
if ipv4_packet.protocol() == ipv4::protocol::Protocol::Icmp { if ipv4_packet.protocol() == ipv4::protocol::Protocol::Icmp {
let mut icmp = IcmpPacket::new(ipv4_packet.payload_mut())?; let mut icmp = IcmpPacket::new(ipv4_packet.payload_mut())?;
@@ -32,6 +31,7 @@ fn icmp(tun_writer: &TunWriter, mut ipv4_packet: IpV4Packet<&mut [u8]>) -> Resul
Ok(()) Ok(())
} }
/// 接收tun数据,并且转发到udp上
#[inline] #[inline]
fn handle(sender: &Sender<Ipv4Addr>, data: &mut [u8], tun_writer: &TunWriter, current_device: CurrentDeviceInfo, net_packet: &mut NetPacket<Vec<u8>>) -> Result<()> { fn handle(sender: &Sender<Ipv4Addr>, data: &mut [u8], tun_writer: &TunWriter, current_device: CurrentDeviceInfo, net_packet: &mut NetPacket<Vec<u8>>) -> Result<()> {
let data_len = data.len(); let data_len = data.len();
@@ -68,7 +68,7 @@ fn handle(sender: &Sender<Ipv4Addr>, data: &mut [u8], tun_writer: &TunWriter, cu
pub fn start(sender: Sender<Ipv4Addr>, pub fn start(sender: Sender<Ipv4Addr>,
tun_reader: TunReader, tun_reader: TunReader,
tun_writer: TunWriter, tun_writer: TunWriter,
current_device: Arc<AtomicCell<CurrentDeviceInfo>>, ) { current_device: Arc<AtomicCell<CurrentDeviceInfo>>) {
thread::Builder::new().name("tun-handler".into()).spawn(move || { thread::Builder::new().name("tun-handler".into()).spawn(move || {
if let Err(e) = start_(sender, tun_reader, tun_writer, current_device) { if let Err(e) = start_(sender, tun_reader, tun_writer, current_device) {
log::warn!("{:?}",e); log::warn!("{:?}",e);
@@ -80,7 +80,7 @@ pub fn start(sender: Sender<Ipv4Addr>,
fn start_(sender: Sender<Ipv4Addr>, fn start_(sender: Sender<Ipv4Addr>,
tun_reader: TunReader, tun_reader: TunReader,
tun_writer: TunWriter, tun_writer: TunWriter,
current_device: Arc<AtomicCell<CurrentDeviceInfo>>, ) -> io::Result<()> { current_device: Arc<AtomicCell<CurrentDeviceInfo>>) -> io::Result<()> {
let mut net_packet = NetPacket::new(vec![0u8; 4 + 8 + 1500])?; let mut net_packet = NetPacket::new(vec![0u8; 4 + 8 + 1500])?;
net_packet.set_version(Version::V1); net_packet.set_version(Version::V1);
net_packet.set_protocol(Protocol::Ipv4Turn); net_packet.set_protocol(Protocol::Ipv4Turn);
@@ -109,8 +109,8 @@ fn start_(sender: Sender<Ipv4Addr>,
net_packet.set_ttl(MAX_TTL); net_packet.set_ttl(MAX_TTL);
let mut buf = [0; 4096]; let mut buf = [0; 4096];
loop { loop {
let data = tun_reader.read(&mut buf)?; let len = tun_reader.read(&mut buf)?;
match handle(&sender, data, &tun_writer, current_device.load(), &mut net_packet) { match handle(&sender, &mut buf[..len], &tun_writer, current_device.load(), &mut net_packet) {
Ok(_) => {} Ok(_) => {}
Err(e) => { Err(e) => {
log::warn!("{:?}", e) log::warn!("{:?}", e)
+1 -1
View File
@@ -1,6 +1,5 @@
use crate::error::Error; use crate::error::Error;
pub use p2p_channel::channel::{Route, RouteKey}; pub use p2p_channel::channel::{Route, RouteKey};
pub type Result<T> = std::result::Result<T, Error>; pub type Result<T> = std::result::Result<T, Error>;
@@ -11,4 +10,5 @@ pub mod nat;
pub mod proto; pub mod proto;
pub mod protocol; pub mod protocol;
pub mod tun_device; pub mod tun_device;
pub mod tap_device;
pub mod core; pub mod core;
+1 -2
View File
@@ -1,9 +1,8 @@
use p2p_channel::punch::NatType;
use std::collections::HashSet; use std::collections::HashSet;
use std::net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket}; use std::net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket};
use std::time::Duration; use std::time::Duration;
use std::{io, thread}; use std::{io, thread};
use p2p_channel::punch::NatType;
// #[derive(Debug, Copy, Clone, PartialEq)] // #[derive(Debug, Copy, Clone, PartialEq)]
// pub enum NatType { // pub enum NatType {
+48 -14
View File
@@ -1,9 +1,9 @@
use crate::proto::message::PunchNatType;
use p2p_channel::punch::{NatInfo, NatType};
use parking_lot::Mutex;
use std::io; use std::io;
use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::sync::Arc; use std::sync::Arc;
use parking_lot::Mutex;
use p2p_channel::punch::{NatInfo, NatType};
use crate::proto::message::PunchNatType;
pub mod check; pub mod check;
@@ -26,7 +26,7 @@ impl From<NatType> for PunchNatType {
fn from(value: NatType) -> Self { fn from(value: NatType) -> Self {
match value { match value {
NatType::Symmetric => PunchNatType::Symmetric, NatType::Symmetric => PunchNatType::Symmetric,
NatType::Cone => PunchNatType::Cone NatType::Cone => PunchNatType::Cone,
} }
} }
} }
@@ -35,14 +35,26 @@ impl Into<NatType> for PunchNatType {
fn into(self) -> NatType { fn into(self) -> NatType {
match self { match self {
PunchNatType::Symmetric => NatType::Symmetric, PunchNatType::Symmetric => NatType::Symmetric,
PunchNatType::Cone => NatType::Cone PunchNatType::Cone => NatType::Cone,
} }
} }
} }
impl NatTest { impl NatTest {
pub fn new(nat_test_server: Vec<SocketAddr>, public_ip: Ipv4Addr, public_port: u16, local_ip: IpAddr, local_port: u16) -> NatTest { pub fn new(
let info = NatTest::re_test_(&nat_test_server, public_ip, public_port, local_ip, local_port); nat_test_server: Vec<SocketAddr>,
public_ip: Ipv4Addr,
public_port: u16,
local_ip: IpAddr,
local_port: u16,
) -> NatTest {
let info = NatTest::re_test_(
&nat_test_server,
public_ip,
public_port,
local_ip,
local_port,
);
NatTest { NatTest {
nat_test_server: Arc::new(nat_test_server), nat_test_server: Arc::new(nat_test_server),
info: Arc::new(Mutex::new(info)), info: Arc::new(Mutex::new(info)),
@@ -51,12 +63,30 @@ impl NatTest {
pub fn nat_info(&self) -> NatInfo { pub fn nat_info(&self) -> NatInfo {
self.info.lock().clone() self.info.lock().clone()
} }
pub fn re_test(&self, public_ip: Ipv4Addr, public_port: u16, local_ip: IpAddr, local_port: u16) -> NatInfo { pub fn re_test(
let info = NatTest::re_test_(&self.nat_test_server, public_ip, public_port, local_ip, local_port); &self,
public_ip: Ipv4Addr,
public_port: u16,
local_ip: IpAddr,
local_port: u16,
) -> NatInfo {
let info = NatTest::re_test_(
&self.nat_test_server,
public_ip,
public_port,
local_ip,
local_port,
);
*self.info.lock() = info.clone(); *self.info.lock() = info.clone();
info info
} }
fn re_test_(nat_test_server: &Vec<SocketAddr>, public_ip: Ipv4Addr, public_port: u16, local_ip: IpAddr, local_port: u16) -> NatInfo { fn re_test_(
nat_test_server: &Vec<SocketAddr>,
public_ip: Ipv4Addr,
public_port: u16,
local_ip: IpAddr,
local_port: u16,
) -> NatInfo {
return match check::public_ip_list(nat_test_server) { return match check::public_ip_list(nat_test_server) {
Ok((nat_type, ips, port_range)) => { Ok((nat_type, ips, port_range)) => {
let mut public_ips = Vec::new(); let mut public_ips = Vec::new();
@@ -66,11 +96,14 @@ impl NatTest {
public_ips.push(IpAddr::from(ip)); public_ips.push(IpAddr::from(ip));
} }
} }
NatInfo::new(public_ips, NatInfo::new(
public_ips,
public_port, public_port,
port_range, port_range,
local_ip, local_port, local_ip,
nat_type, ) local_port,
nat_type,
)
} }
Err(e) => { Err(e) => {
log::warn!("{:?}", e); log::warn!("{:?}", e);
@@ -78,7 +111,8 @@ impl NatTest {
vec![IpAddr::from(public_ip)], vec![IpAddr::from(public_ip)],
public_port, public_port,
0, 0,
local_ip, local_port, local_ip,
local_port,
NatType::Cone, NatType::Cone,
) )
} }
-1
View File
@@ -1,6 +1,5 @@
use std::{fmt, io}; use std::{fmt, io};
#[derive(Eq, PartialEq, Copy, Clone, Debug)] #[derive(Eq, PartialEq, Copy, Clone, Debug)]
pub enum Protocol { pub enum Protocol {
/// ping请求 /// ping请求
+5 -2
View File
@@ -1,5 +1,5 @@
use std::{fmt, io};
use std::net::Ipv4Addr; use std::net::Ipv4Addr;
use std::{fmt, io};
/* /*
0 15 31 0 15 31
@@ -98,7 +98,10 @@ impl<B: AsRef<[u8]>> NetPacket<B> {
let len = buffer.as_ref().len(); let len = buffer.as_ref().len();
// 不能大于udp最大载荷长度 // 不能大于udp最大载荷长度
if len < 12 || len > 65535 - 20 - 8 { if len < 12 || len > 65535 - 20 - 8 {
return Err(io::Error::new(io::ErrorKind::InvalidData, "length overflow")); return Err(io::Error::new(
io::ErrorKind::InvalidData,
"length overflow",
));
} }
Ok(NetPacket { buffer }) Ok(NetPacket { buffer })
} }
-2
View File
@@ -1,5 +1,3 @@
#[derive(Copy, Clone, Eq, PartialEq, Debug)] #[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum Protocol { pub enum Protocol {
Punch, Punch,
+58
View File
@@ -0,0 +1,58 @@
use crate::tun_device::{TunReader, TunWriter};
pub type TapReader = TunReader;
pub type TapWriter = TunWriter;
use std::net::Ipv4Addr;
use std::sync::Arc;
use tun::Device;
use parking_lot::Mutex;
use std::io;
pub fn create_tap(
address: Ipv4Addr,
netmask: Ipv4Addr,
gateway: Ipv4Addr,
) -> io::Result<(TunWriter, TunReader, [u8; 6])> {
println!("========TAP网卡配置========");
let mut config = tun::Configuration::default();
config
.destination(gateway)
.address(address)
.netmask(netmask)
.mtu(1420)
.layer(tun::Layer::L2)
// .queues(2) 用多个队列有兼容性问题
.up();
let dev = tun::create(&config).unwrap();
let name = dev.name();
println!("name:{:?}", name);
let packet_information = dev.has_packet_information();
let queue = dev.queue(0).unwrap();
let reader = queue.reader();
let writer = queue.writer();
let get_mac_cmd = format!("cat /sys/class/net/{}/address", name);
let mac_out = std::process::Command::new("sh")
.arg("-c")
.arg(get_mac_cmd)
.output()
.expect("sh exec error!");
if !mac_out.status.success() {
return Err(io::Error::new(io::ErrorKind::Other, format!("获取mac地址错误: {:?}", mac_out)));
}
let mac_str = String::from_utf8(mac_out.stdout).unwrap();
let mut mac = [0; 6];
let mut split = mac_str.split(":");
for i in 0..6 {
mac[i] = u8::from_str_radix(&split.next().unwrap()[..2], 16).unwrap();
}
println!("mac:{:?}", mac);
println!("========TAP网卡配置========");
Ok((
TunWriter(writer, packet_information, Arc::new(Mutex::new(dev))),
TunReader(reader, packet_information),
mac
))
}
+13
View File
@@ -0,0 +1,13 @@
use crate::tun_device::{TunReader, TunWriter};
pub type TapReader = TunReader;
pub type TapWriter = TunWriter;
use std::net::Ipv4Addr;
pub fn create_tap(
address: Ipv4Addr,
netmask: Ipv4Addr,
gateway: Ipv4Addr,
) -> crate::error::Result<(TapWriter, TapReader, [u8; 6])> {
unimplemented!()
}
+21
View File
@@ -0,0 +1,21 @@
#[cfg(target_os = "windows")]
mod windows;
#[cfg(any(target_os = "linux", target_os = "android"))]
mod linux;
#[cfg(target_os = "macos")]
mod mac;
#[cfg(target_os = "macos")]
pub use mac::{TapWriter, TapReader};
#[cfg(target_os = "macos")]
pub use mac::create_tap;
#[cfg(any(target_os = "linux", target_os = "android"))]
pub use linux::{TapWriter, TapReader};
#[cfg(any(target_os = "linux", target_os = "android"))]
pub use linux::create_tap;
#[cfg(target_os = "windows")]
pub use windows::create_tap;
#[cfg(target_os = "windows")]
pub use windows::delete_tap;
#[cfg(target_os = "windows")]
pub use windows::{TapReader, TapWriter};
+100
View File
@@ -0,0 +1,100 @@
use std::io;
use std::net::Ipv4Addr;
use std::sync::Arc;
use parking_lot::Mutex;
use win_tun_tap::{IFace, TapDevice};
#[derive(Clone)]
pub struct TapWriter(Arc<TapDevice>, Arc<Mutex<()>>);
impl TapWriter {
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
self.0.write(buf)
}
pub fn change_ip(
&self,
address: Ipv4Addr,
netmask: Ipv4Addr,
gateway: Ipv4Addr,
old_netmask: Ipv4Addr,
old_gateway: Ipv4Addr,
) -> io::Result<()> {
if let Err(e) =
self.0.delete_route(dest(old_gateway, old_gateway), old_netmask, old_gateway)
{
log::warn!("{:?}", e);
}
self.0.set_ip(address, netmask)?;
self.0.add_route(dest(gateway, netmask), netmask, gateway)
}
pub fn close(&self) -> io::Result<()> {
self.0.shutdown()
}
}
fn dest(ip: Ipv4Addr, mask: Ipv4Addr) -> Ipv4Addr {
let ip = ip.octets();
let mask = mask.octets();
Ipv4Addr::from([
ip[0] & mask[0],
ip[1] & mask[1],
ip[2] & mask[2],
ip[3] & mask[3],
])
}
#[derive(Clone)]
pub struct TapReader(Arc<TapDevice>);
impl TapReader {
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
self.0.read(buf)
}
}
pub const TAP_INTERFACE_NAME: &str = "Switch-Tap-V1";
pub fn create_tap(
address: Ipv4Addr,
netmask: Ipv4Addr,
gateway: Ipv4Addr,
) -> io::Result<(TapWriter, TapReader, [u8; 6])> {
println!("========TAP网卡配置========");
let tap_device = match TapDevice::open(TAP_INTERFACE_NAME) {
Ok(tap_device) => tap_device,
Err(e) => {
log::warn!("{:?}", e);
let tap_device = TapDevice::create()?;
tap_device.set_name(TAP_INTERFACE_NAME)?;
tap_device
}
};
let mac = tap_device.get_mac()?;
println!("name:{:?}", tap_device.get_name()?);
println!("version:{:x?}", tap_device.get_version()?);
println!("mac:{:x?}", mac);
tap_device.set_ip(address, netmask)?;
tap_device.set_mtu(1420)?;
tap_device.set_status(true)?;
tap_device.add_route(address, netmask, gateway)?;
let tap = Arc::new(tap_device);
println!("========TAP网卡配置========");
Ok((
TapWriter(tap.clone(), Arc::default()),
TapReader(tap),
mac
))
}
pub fn delete_tap() {
let tap_device = match TapDevice::open(TAP_INTERFACE_NAME) {
Ok(tap_device) => tap_device,
Err(_) => {
return;
}
};
let _ = tap_device.delete();
}
+3
View File
@@ -9,6 +9,7 @@ pub fn create_tun(
netmask: Ipv4Addr, netmask: Ipv4Addr,
gateway: Ipv4Addr, gateway: Ipv4Addr,
) -> crate::error::Result<(TunWriter, TunReader)> { ) -> crate::error::Result<(TunWriter, TunReader)> {
println!("========TUN网卡配置========");
let mut config = tun::Configuration::default(); let mut config = tun::Configuration::default();
config config
@@ -28,6 +29,8 @@ pub fn create_tun(
let queue = dev.queue(0).unwrap(); let queue = dev.queue(0).unwrap();
let reader = queue.reader(); let reader = queue.reader();
let writer = queue.writer(); let writer = queue.writer();
println!("name:{:?}", dev.name());
println!("========TUN网卡配置========");
Ok(( Ok((
TunWriter(writer, packet_information, Arc::new(Mutex::new(dev))), TunWriter(writer, packet_information, Arc::new(Mutex::new(dev))),
TunReader(reader, packet_information), TunReader(reader, packet_information),
+4 -12
View File
@@ -12,6 +12,7 @@ pub fn create_tun(
netmask: Ipv4Addr, netmask: Ipv4Addr,
gateway: Ipv4Addr, gateway: Ipv4Addr,
) -> crate::error::Result<(TunWriter, TunReader)> { ) -> crate::error::Result<(TunWriter, TunReader)> {
println!("========TUN网卡配置========");
let mut config = tun::Configuration::default(); let mut config = tun::Configuration::default();
config config
@@ -23,22 +24,13 @@ pub fn create_tun(
let dev = tun::create(&config).unwrap(); let dev = tun::create(&config).unwrap();
config_ip(dev.name(), address, netmask, gateway)?; config_ip(dev.name(), address, netmask, gateway)?;
// println!("{:?}", if_config_out);
// let cmd_str: String = " ifconfig|grep flags=8051|awk -F ':' '{print $1}'|tail -1".to_string();
//
// let cmd_str_out = Command::new("sh")
// .arg("-c")
// .arg(cmd_str)
// .output()
// .expect("sh exec error!");
// if !cmd_str_out.status.success(){
// return Err(Error::Stop(format!("设置路由失败:{:?}", cmd_str_out)));
// }
// println!("{:?}", cmd_str_out);
let packet_information = dev.has_packet_information(); let packet_information = dev.has_packet_information();
let queue = dev.queue(0).unwrap(); let queue = dev.queue(0).unwrap();
let reader = queue.reader(); let reader = queue.reader();
let writer = queue.writer(); let writer = queue.writer();
println!("name:{:?}", dev.name());
println!("========TUN网卡配置========");
Ok(( Ok((
TunWriter(writer, packet_information, Arc::new(Mutex::new(dev))), TunWriter(writer, packet_information, Arc::new(Mutex::new(dev))),
TunReader(reader, packet_information), TunReader(reader, packet_information),
+2
View File
@@ -7,6 +7,8 @@ pub use unix::{TunReader, TunWriter};
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
pub use windows::create_tun; pub use windows::create_tun;
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
pub use windows::delete_tun;
#[cfg(target_os = "windows")]
pub use windows::{TunReader, TunWriter}; pub use windows::{TunReader, TunWriter};
#[cfg(any(target_os = "linux", target_os = "android"))] #[cfg(any(target_os = "linux", target_os = "android"))]
+11 -15
View File
@@ -15,21 +15,8 @@ use parking_lot::Mutex;
pub struct TunReader(pub(crate) Reader, pub(crate) bool); pub struct TunReader(pub(crate) Reader, pub(crate) bool);
impl TunReader { impl TunReader {
pub fn read<'a>(&'a self, buf: &'a mut [u8]) -> io::Result<&mut [u8]> { pub fn read(&self, buf: & mut [u8]) -> io::Result<usize> {
let len = self.0.read(buf)?; self.0.read(buf)
if self.1 {
Ok(&mut buf[4..len])
} else {
Ok(&mut buf[..len])
}
}
pub fn close(&self) {
unsafe {
let raw = self.0.as_raw_fd();
if raw >= 0 {
libc::close(raw);
}
}
} }
} }
@@ -52,6 +39,15 @@ impl TunWriter {
self.0.write_all(packet) self.0.write_all(packet)
} }
} }
pub fn close(&self) -> io::Result<()>{
unsafe {
let raw = self.0.as_raw_fd();
if raw >= 0 {
libc::close(raw);
}
}
Ok(())
}
pub fn change_ip(&self, address: Ipv4Addr, netmask: Ipv4Addr, pub fn change_ip(&self, address: Ipv4Addr, netmask: Ipv4Addr,
gateway: Ipv4Addr, _old_netmask: Ipv4Addr, _old_gateway: Ipv4Addr) -> io::Result<()> { gateway: Ipv4Addr, _old_netmask: Ipv4Addr, _old_gateway: Ipv4Addr) -> io::Result<()> {
let mut config = tun::Configuration::default(); let mut config = tun::Configuration::default();
+82 -164
View File
@@ -4,52 +4,62 @@ use std::sync::Arc;
use libloading::Library; use libloading::Library;
use parking_lot::Mutex; use parking_lot::Mutex;
use wintun::{Adapter, Packet, Session};
pub const INTERFACE_NAME: &str = "Switch-V1"; use win_tun_tap::{IFace, TunDevice};
pub const POOL_NAME: &str = "Switch-V1"; use win_tun_tap::packet::TunPacket;
pub const TUN_INTERFACE_NAME: &str = "Switch-V1";
pub const TUN_POOL_NAME: &str = "Switch-V1";
#[derive(Clone)] #[derive(Clone)]
pub struct TunWriter(Arc<Session>, Arc<Mutex<u32>>); pub struct TunWriter(Arc<TunDevice>, Arc<Mutex<()>>);
impl TunWriter { impl TunWriter {
pub fn write(&self, buf: &[u8]) -> io::Result<()> { pub fn write(&self, buf: &[u8]) -> io::Result<()> {
match self.0.allocate_send_packet(buf.len() as u16) { let mut packet = self.0.allocate_send_packet(buf.len() as u16)?;
Ok(mut packet) => {
packet.bytes_mut().copy_from_slice(buf); packet.bytes_mut().copy_from_slice(buf);
self.0.send_packet(packet); self.0.send_packet(packet);
return Ok(()); return Ok(());
} }
Err(_) => {} pub fn change_ip(
} &self,
return Err(io::Error::new(io::ErrorKind::Other, "send err")); address: Ipv4Addr,
} netmask: Ipv4Addr,
pub fn change_ip(&self, address: Ipv4Addr, netmask: Ipv4Addr, gateway: Ipv4Addr,
gateway: Ipv4Addr, old_netmask: Ipv4Addr, old_gateway: Ipv4Addr) -> io::Result<()> { old_netmask: Ipv4Addr,
let index = self.1.lock(); old_gateway: Ipv4Addr,
if let Err(e) = delete_route(*index, old_netmask, old_gateway) { ) -> io::Result<()> {
if let Err(e) =
self.0.delete_route(dest(old_gateway, old_gateway), old_netmask, old_gateway)
{
log::warn!("{:?}", e); log::warn!("{:?}", e);
} }
config_ip(*index, address, netmask, gateway) self.0.set_ip(address, netmask)?;
self.0.add_route(dest(gateway, netmask), netmask, gateway)
} }
pub fn close(&self) -> io::Result<()> {
self.0.shutdown()
}
}
fn dest(ip: Ipv4Addr, mask: Ipv4Addr) -> Ipv4Addr {
let ip = ip.octets();
let mask = mask.octets();
Ipv4Addr::from([
ip[0] & mask[0],
ip[1] & mask[1],
ip[2] & mask[2],
ip[3] & mask[3],
])
} }
#[derive(Clone)] #[derive(Clone)]
pub struct TunReader(pub(crate) Arc<Session>); pub struct TunReader(Arc<TunDevice>);
impl TunReader { impl TunReader {
pub fn next(&self) -> io::Result<Packet> { pub fn next(&self) -> io::Result<TunPacket> {
match self.0.receive_blocking() { self.0.receive_blocking()
Ok(packet) => {
return Ok(packet);
}
Err(_) => {}
}
return Err(io::Error::new(io::ErrorKind::Other, "read err"));
}
pub fn close(&self) {
self.0.shutdown()
} }
} }
@@ -58,158 +68,66 @@ pub fn create_tun(
netmask: Ipv4Addr, netmask: Ipv4Addr,
gateway: Ipv4Addr, gateway: Ipv4Addr,
) -> io::Result<(TunWriter, TunReader)> { ) -> io::Result<(TunWriter, TunReader)> {
let win_tun = unsafe { unsafe {
println!("========TUN网卡配置========");
match Library::new("wintun.dll") { match Library::new("wintun.dll") {
Ok(library) => match wintun::load_from_library(library) { Ok(lib) => match TunDevice::open(lib, TUN_INTERFACE_NAME) {
Ok(win_tun) => win_tun, Ok(tun_device) => {
Err(e) => { let _ = tun_device.delete();
return Err(io::Error::new(io::ErrorKind::Other, format!("{:?}", e)));
} }
Err(_) => {}
}, },
Err(e) => { Err(e) => {
log::error!("wintun.dll not found"); log::error!("wintun.dll not found");
return Err(io::Error::new(io::ErrorKind::Other, format!("wintun.dll not found {:?}", e))); return Err(io::Error::new(
io::ErrorKind::Other,
format!("wintun.dll not found {:?}", e),
));
} }
} }
}; let tun_device = match TunDevice::create(
if let Ok(adapter) = Adapter::open(&win_tun, INTERFACE_NAME) { Library::new("wintun.dll").unwrap(),
log::warn!("Switch-V1 未正常退出"); TUN_POOL_NAME,
drop(adapter); TUN_INTERFACE_NAME,
std::thread::sleep(std::time::Duration::from_secs(1)); ) {
}; Ok(tun_device) => tun_device,
let adapter = match Adapter::create(&win_tun, POOL_NAME, INTERFACE_NAME, None) {
Ok(adapter) => adapter,
Err(e) => return Err(io::Error::new(io::ErrorKind::Other, format!("{:?}", e))),
};
let session = Arc::new(adapter.start_session(wintun::MAX_RING_CAPACITY).unwrap());
let index = match adapter.get_adapter_index() {
Ok(index) => {
index
}
Err(e) => { Err(e) => {
log::error!("get_adapter_index err {:?}",e); return Err(io::Error::new(
get_if_index() io::ErrorKind::Other,
format!("{:?}", e),
));
} }
}; };
config_ip(index, address, netmask, gateway)?; println!("name:{:?}", tun_device.get_name()?);
let reader_session = session.clone(); println!("version:{:?}", tun_device.version()?);
Ok((TunWriter(session.clone(), Arc::new(Mutex::new(index))), TunReader(reader_session))) log::error!("创建tun成功 {:?}",tun_device.get_name()?);
tun_device.set_ip(address, netmask)?;
tun_device.set_mtu(1420)?;
tun_device.add_route(address, netmask, gateway)?;
let device = Arc::new(tun_device);
println!("========TUN网卡配置========");
Ok((
TunWriter(device.clone(), Arc::default()),
TunReader(device),
))
}
} }
fn get_if_index() -> u32 { pub fn delete_tun() {
let cmd = format!("netsh int ipv4 show interfaces {} |findstr IfIndex", INTERFACE_NAME); unsafe {
let out = std::process::Command::new("cmd") match Library::new("wintun.dll") {
.arg("/C") Ok(lib) => match TunDevice::open(lib, TUN_INTERFACE_NAME) {
.arg(&cmd) Ok(tun_device) => {
.output() let _ = tun_device.delete();
.unwrap();
if !out.status.success() {
log::warn!("1获取网络接口索引失败:cmd={:?},out={:?}",cmd,out);
return 0;
} }
if let Ok(stdout) = String::from_utf8(out.stdout) { Err(_) => {}
if let Some(start) = stdout.find(":") { },
if let Some(end) = stdout.find("\r\n") { Err(_) => {}
if let Ok(index) = stdout[start + 1..end].trim().parse::<u32>() {
return index;
} }
} }
} }
}
log::warn!("2获取网络接口索引失败:cmd={:?}",cmd);
0
}
fn config_ip(index: u32, address: Ipv4Addr, netmask: Ipv4Addr, gateway: Ipv4Addr) -> io::Result<()> {
if index == 0 {
return Err(io::Error::new(io::ErrorKind::Other, format!("网络接口索引错误: {:?}", index)));
}
let set_mtu = format!(
"netsh interface ipv4 set subinterface {} mtu=1420 store=persistent",
index
);
let set_metric = format!("netsh interface ip set interface {} metric=1", index);
let set_address = format!(
"netsh interface ip set address {} static {:?} {:?} ", // gateway={:?}
index, address, netmask,
);
// 执行网卡初始化命令
let out = std::process::Command::new("cmd")
.arg("/C")
.arg(set_mtu)
.output()
.unwrap();
if !out.status.success() {
return Err(io::Error::new(io::ErrorKind::Other, format!("设置mtu失败: {:?}", out)));
}
let out = std::process::Command::new("cmd")
.arg("/C")
.arg(set_metric)
.output()
.unwrap();
if !out.status.success() {
return Err(io::Error::new(io::ErrorKind::Other, format!("设置接口跃点失败: {:?}", out)));
}
let out = std::process::Command::new("cmd")
.arg("/C")
.arg(&set_address)
.output()
.unwrap();
if !out.status.success() {
log::error!("cmd={:?},out={:?}",set_address,out);
return Err(io::Error::new(io::ErrorKind::Other, format!("设置网络地址失败: {:?}", out)));
}
let dest = {
let ip = address.octets();
let mask = netmask.octets();
Ipv4Addr::from([
ip[0] & mask[0],
ip[1] & mask[1],
ip[2] & mask[2],
ip[3] & mask[3],
])
};
let set_route = format!(
"route add {:?} mask {:?} {:?} if {}",
dest, netmask, gateway, index
);
// 执行添加路由命令
let out = std::process::Command::new("cmd")
.arg("/C")
.arg(&set_route)
.output()
.unwrap();
if !out.status.success() {
log::error!("cmd={:?},out={:?}",set_route,out);
return Err(io::Error::new(io::ErrorKind::Other, format!("添加路由失败: {:?}", out)));
}
Ok(())
}
fn delete_route(index: u32, netmask: Ipv4Addr, gateway: Ipv4Addr) -> io::Result<()> {
if index == 0 {
return Err(io::Error::new(io::ErrorKind::Other, format!("网络接口索引错误: {:?}", index)));
}
let mask = netmask.octets();
let ip = gateway.octets();
let dest = Ipv4Addr::from([
ip[0] & mask[0],
ip[1] & mask[1],
ip[2] & mask[2],
ip[3] & mask[3],
]);
let delete_route = format!(
"route delete {:?} mask {:?} {:?} if {}",
dest, netmask, gateway, index
);
// 删除路由
let out = std::process::Command::new("cmd")
.arg("/C")
.arg(delete_route)
.output()
.unwrap();
if !out.status.success() {
return Err(io::Error::new(io::ErrorKind::Other, format!("删除路由失败: {:?}", out)));
}
Ok(())
}
+33
View File
@@ -0,0 +1,33 @@
[package]
name = "win-tun-tap"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
log = "0.4.17"
winreg = "0.7"
scopeguard = "1.1"
libloading = "0.7"
widestring = "0.4"
once_cell = "1.8"
itertools = "0.10.1"
[dependencies.winapi]
version = "0.3"
features = [
"errhandlingapi",
"combaseapi",
"ioapiset",
"winioctl",
"setupapi",
"synchapi",
"netioapi",
"fileapi",
"winbase",
"winerror",
"ipexport",
"iphlpapi",
"handleapi"
]
+534
View File
@@ -0,0 +1,534 @@
// Many things will be used in the future
#![allow(unused)]
//! Module holding safe wrappers over winapi functions
use winapi::shared::basetsd::*;
use winapi::shared::guiddef::GUID;
use winapi::shared::ifdef::*;
use winapi::shared::minwindef::*;
use winapi::shared::netioapi::*;
use winapi::shared::winerror::*;
use winapi::um::combaseapi::*;
use winapi::um::errhandlingapi::*;
use winapi::um::fileapi::*;
use winapi::um::handleapi::*;
use winapi::um::ioapiset::*;
use winapi::um::setupapi::*;
use winapi::um::synchapi::*;
use winapi::um::winioctl::*;
use winapi::um::winnt::*;
use winapi::um::winreg::*;
use std::{io, mem, ptr};
use std::error::Error;
use winapi::um::minwinbase::OVERLAPPED_u;
#[allow(non_camel_case_types)]
#[allow(non_snake_case)]
#[repr(C)]
#[derive(Clone, Copy)]
/// Custom type to handle variable size SP_DRVINFO_DETAIL_DATA_W
pub struct SP_DRVINFO_DETAIL_DATA_W2 {
pub cbSize: DWORD,
pub InfDate: FILETIME,
pub CompatIDsOffset: DWORD,
pub CompatIDsLength: DWORD,
pub Reserved: ULONG_PTR,
pub SectionName: [WCHAR; 256],
pub InfFileName: [WCHAR; 260],
pub DrvDescription: [WCHAR; 256],
pub HardwareID: [WCHAR; 512],
}
pub fn string_from_guid(guid: &GUID) -> io::Result<Vec<WCHAR>> {
// GUID_STRING_CHARACTERS + 1
let mut string = vec![0; 39];
match unsafe {
StringFromGUID2(guid, string.as_mut_ptr(), string.len() as _)
} {
0 => Err(io::Error::new(io::ErrorKind::Other, "Insufficent buffer")),
_ => Ok(string),
}
}
pub fn alias_to_luid(alias: &[WCHAR]) -> io::Result<NET_LUID> {
let mut luid = unsafe { mem::zeroed() };
match unsafe { ConvertInterfaceAliasToLuid(alias.as_ptr(), &mut luid) } {
0 => Ok(luid),
err => Err(io::Error::from_raw_os_error(err as _)),
}
}
pub fn luid_to_index(luid: &NET_LUID) -> io::Result<NET_IFINDEX> {
let mut index = 0;
match unsafe { ConvertInterfaceLuidToIndex(luid, &mut index) } {
0 => Ok(index),
err => Err(io::Error::from_raw_os_error(err as _)),
}
}
pub fn luid_to_guid(luid: &NET_LUID) -> io::Result<GUID> {
let mut guid = unsafe { mem::zeroed() };
match unsafe { ConvertInterfaceLuidToGuid(luid, &mut guid) } {
0 => Ok(guid),
err => Err(io::Error::from_raw_os_error(err as _)),
}
}
pub fn luid_to_alias(luid: &NET_LUID) -> io::Result<Vec<WCHAR>> {
// IF_MAX_STRING_SIZE + 1
let mut alias = vec![0; 257];
match unsafe {
ConvertInterfaceLuidToAlias(luid, alias.as_mut_ptr(), alias.len())
} {
0 => {
Ok(alias)
}
err => Err(io::Error::from_raw_os_error(err as _)),
}
}
pub fn close_handle(handle: HANDLE) -> io::Result<()> {
match unsafe { CloseHandle(handle) } {
0 => Err(io::Error::last_os_error()),
_ => Ok(()),
}
}
pub fn create_file(
file_name: &[WCHAR],
desired_access: DWORD,
share_mode: DWORD,
creation_disposition: DWORD,
flags_and_attributes: DWORD,
) -> io::Result<HANDLE> {
match unsafe {
CreateFileW(
file_name.as_ptr(),
desired_access,
share_mode,
ptr::null_mut(),
creation_disposition,
flags_and_attributes,
ptr::null_mut(),
)
} {
INVALID_HANDLE_VALUE => Err(io::Error::last_os_error()),
handle => Ok(handle),
}
}
pub fn read_file(handle: HANDLE, buffer: &mut [u8]) -> io::Result<DWORD> {
let mut ret = 0;
//https://www.cnblogs.com/linyilong3/archive/2012/05/03/2480451.html
unsafe {
let mut ip_overlapped = winapi::um::minwinbase::OVERLAPPED {
Internal: 0,
InternalHigh: 0,
u: Default::default(),
hEvent: ptr::null_mut(),
};
if 0 == ReadFile(
handle,
buffer.as_mut_ptr() as _,
buffer.len() as _,
&mut ret,
&mut ip_overlapped, ) {
let e = io::Error::last_os_error();
if e.raw_os_error().unwrap_or(0) == 997 {
if 0 == GetOverlappedResult(handle, &mut ip_overlapped, &mut ret, 1) {
return Err(e);
}
} else {
return Err(e);
}
}
Ok(ret)
}
}
pub fn write_file(handle: HANDLE, buffer: &[u8]) -> io::Result<DWORD> {
let mut ret = 0;
let mut ip_overlapped = winapi::um::minwinbase::OVERLAPPED {
Internal: 0,
InternalHigh: 0,
u: Default::default(),
hEvent: ptr::null_mut(),
};
unsafe {
if 0 == WriteFile(
handle,
buffer.as_ptr() as _,
buffer.len() as _,
&mut ret,
&mut ip_overlapped,
) {
let e = io::Error::last_os_error();
if e.raw_os_error().unwrap_or(0) == 997 {
if 0 == GetOverlappedResult(handle, &mut ip_overlapped, &mut ret, 1) {
return Err(e);
}
} else {
return Err(e);
}
}
Ok(ret)
}
}
pub fn create_device_info_list(guid: &GUID) -> io::Result<HDEVINFO> {
match unsafe { SetupDiCreateDeviceInfoList(guid, ptr::null_mut()) } {
INVALID_HANDLE_VALUE => Err(io::Error::last_os_error()),
devinfo => Ok(devinfo),
}
}
pub fn get_class_devs(guid: &GUID, flags: DWORD) -> io::Result<HDEVINFO> {
match unsafe {
SetupDiGetClassDevsW(guid, ptr::null(), ptr::null_mut(), flags)
} {
INVALID_HANDLE_VALUE => Err(io::Error::last_os_error()),
devinfo => Ok(devinfo),
}
}
pub fn destroy_device_info_list(devinfo: HDEVINFO) -> io::Result<()> {
match unsafe { SetupDiDestroyDeviceInfoList(devinfo) } {
0 => Err(io::Error::last_os_error()),
_ => Ok(()),
}
}
pub fn class_name_from_guid(guid: &GUID) -> io::Result<Vec<WCHAR>> {
let mut class_name = vec![0; 32];
match unsafe {
SetupDiClassNameFromGuidW(
guid,
class_name.as_mut_ptr(),
class_name.len() as _,
ptr::null_mut(),
)
} {
0 => Err(io::Error::last_os_error()),
_ => Ok(class_name),
}
}
pub fn create_device_info(
devinfo: HDEVINFO,
device_name: &[WCHAR],
guid: &GUID,
device_description: &[WCHAR],
creation_flags: DWORD,
) -> io::Result<SP_DEVINFO_DATA> {
let mut devinfo_data: SP_DEVINFO_DATA = unsafe { mem::zeroed() };
devinfo_data.cbSize = mem::size_of_val(&devinfo_data) as _;
match unsafe {
SetupDiCreateDeviceInfoW(
devinfo,
device_name.as_ptr(),
guid,
device_description.as_ptr(),
ptr::null_mut(),
creation_flags,
&mut devinfo_data,
)
} {
0 => Err(io::Error::last_os_error()),
_ => Ok(devinfo_data),
}
}
pub fn set_selected_device(
devinfo: HDEVINFO,
devinfo_data: &SP_DEVINFO_DATA,
) -> io::Result<()> {
match unsafe {
SetupDiSetSelectedDevice(devinfo, devinfo_data as *const _ as _)
} {
0 => Err(io::Error::last_os_error()),
_ => Ok(()),
}
}
pub fn set_device_registry_property(
devinfo: HDEVINFO,
devinfo_data: &SP_DEVINFO_DATA,
property: DWORD,
value: &[WCHAR],
) -> io::Result<()> {
match unsafe {
SetupDiSetDeviceRegistryPropertyW(
devinfo,
devinfo_data as *const _ as _,
property,
value.as_ptr() as _,
(value.len() * 2) as _,
)
} {
0 => Err(io::Error::last_os_error()),
_ => Ok(()),
}
}
pub fn get_device_registry_property(
devinfo: HDEVINFO,
devinfo_data: &SP_DEVINFO_DATA,
property: DWORD,
) -> io::Result<Vec<WCHAR>> {
let mut value = vec![0; 32];
match unsafe {
SetupDiGetDeviceRegistryPropertyW(
devinfo,
devinfo_data as *const _ as _,
property,
ptr::null_mut(),
value.as_mut_ptr() as _,
(value.len() * 2) as _,
ptr::null_mut(),
)
} {
0 => Err(io::Error::last_os_error()),
_ => Ok(value),
}
}
pub fn build_driver_info_list(
devinfo: HDEVINFO,
devinfo_data: &SP_DEVINFO_DATA,
driver_type: DWORD,
) -> io::Result<()> {
match unsafe {
SetupDiBuildDriverInfoList(
devinfo,
devinfo_data as *const _ as _,
driver_type,
)
} {
0 => Err(io::Error::last_os_error()),
_ => Ok(()),
}
}
pub fn destroy_driver_info_list(
devinfo: HDEVINFO,
devinfo_data: &SP_DEVINFO_DATA,
driver_type: DWORD,
) -> io::Result<()> {
match unsafe {
SetupDiDestroyDriverInfoList(
devinfo,
devinfo_data as *const _ as _,
driver_type,
)
} {
0 => Err(io::Error::last_os_error()),
_ => Ok(()),
}
}
pub fn get_driver_info_detail(
devinfo: HDEVINFO,
devinfo_data: &SP_DEVINFO_DATA,
drvinfo_data: &SP_DRVINFO_DATA_W,
) -> io::Result<SP_DRVINFO_DETAIL_DATA_W2> {
let mut drvinfo_detail: SP_DRVINFO_DETAIL_DATA_W2 =
unsafe { mem::zeroed() };
drvinfo_detail.cbSize = mem::size_of::<SP_DRVINFO_DETAIL_DATA_W>() as _;
match unsafe {
SetupDiGetDriverInfoDetailW(
devinfo,
devinfo_data as *const _ as _,
drvinfo_data as *const _ as _,
&mut drvinfo_detail as *mut _ as _,
mem::size_of_val(&drvinfo_detail) as _,
ptr::null_mut(),
)
} {
0 => Err(io::Error::last_os_error()),
_ => Ok(drvinfo_detail),
}
}
pub fn set_selected_driver(
devinfo: HDEVINFO,
devinfo_data: &SP_DEVINFO_DATA,
drvinfo_data: &SP_DRVINFO_DATA_W,
) -> io::Result<()> {
match unsafe {
SetupDiSetSelectedDriverW(
devinfo,
devinfo_data as *const _ as _,
drvinfo_data as *const _ as _,
)
} {
0 => Err(io::Error::last_os_error()),
_ => Ok(()),
}
}
pub fn set_class_install_params(
devinfo: HDEVINFO,
devinfo_data: &SP_DEVINFO_DATA,
params: &impl Copy,
) -> io::Result<()> {
match unsafe {
SetupDiSetClassInstallParamsW(
devinfo,
devinfo_data as *const _ as _,
params as *const _ as _,
mem::size_of_val(params) as _,
)
} {
0 => Err(io::Error::last_os_error()),
_ => Ok(()),
}
}
pub fn call_class_installer(
devinfo: HDEVINFO,
devinfo_data: &SP_DEVINFO_DATA,
install_function: DI_FUNCTION,
) -> io::Result<()> {
match unsafe {
SetupDiCallClassInstaller(
install_function,
devinfo,
devinfo_data as *const _ as _,
)
} {
0 => Err(io::Error::last_os_error()),
_ => Ok(()),
}
}
pub fn open_dev_reg_key(
devinfo: HDEVINFO,
devinfo_data: &SP_DEVINFO_DATA,
scope: DWORD,
hw_profile: DWORD,
key_type: DWORD,
sam_desired: REGSAM,
) -> io::Result<HKEY> {
const INVALID_KEY_VALUE: HKEY = INVALID_HANDLE_VALUE as _;
match unsafe {
SetupDiOpenDevRegKey(
devinfo,
devinfo_data as *const _ as _,
scope,
hw_profile,
key_type,
sam_desired,
)
} {
INVALID_KEY_VALUE => Err(io::Error::last_os_error()),
key => Ok(key),
}
}
pub fn notify_change_key_value(
key: HKEY,
watch_subtree: BOOL,
notify_filter: DWORD,
milliseconds: DWORD,
) -> io::Result<()> {
let event = match unsafe {
CreateEventW(ptr::null_mut(), FALSE, FALSE, ptr::null())
} {
INVALID_HANDLE_VALUE => Err(io::Error::last_os_error()),
event => Ok(event),
}?;
match unsafe {
RegNotifyChangeKeyValue(key, watch_subtree, notify_filter, event, TRUE)
} {
0 => Ok(()),
err => Err(io::Error::from_raw_os_error(err)),
}?;
match unsafe { WaitForSingleObject(event, milliseconds) } {
0 => Ok(()),
0x102 => Err(io::Error::new(
io::ErrorKind::TimedOut,
"Registry timed out",
)),
_ => Err(io::Error::last_os_error()),
}
}
pub fn enum_driver_info(
devinfo: HDEVINFO,
devinfo_data: &SP_DEVINFO_DATA,
driver_type: DWORD,
member_index: DWORD,
) -> Option<io::Result<SP_DRVINFO_DATA_W>> {
let mut drvinfo_data: SP_DRVINFO_DATA_W = unsafe { mem::zeroed() };
drvinfo_data.cbSize = mem::size_of_val(&drvinfo_data) as _;
match unsafe {
SetupDiEnumDriverInfoW(
devinfo,
devinfo_data as *const _ as _,
driver_type,
member_index,
&mut drvinfo_data,
)
} {
0 if unsafe { GetLastError() == ERROR_NO_MORE_ITEMS } => None,
0 => Some(Err(io::Error::last_os_error())),
_ => Some(Ok(drvinfo_data)),
}
}
pub fn enum_device_info(
devinfo: HDEVINFO,
member_index: DWORD,
) -> Option<io::Result<SP_DEVINFO_DATA>> {
let mut devinfo_data: SP_DEVINFO_DATA = unsafe { mem::zeroed() };
devinfo_data.cbSize = mem::size_of_val(&devinfo_data) as _;
match unsafe {
SetupDiEnumDeviceInfo(devinfo, member_index, &mut devinfo_data)
} {
0 if unsafe { GetLastError() == ERROR_NO_MORE_ITEMS } => None,
0 => Some(Err(io::Error::last_os_error())),
_ => Some(Ok(devinfo_data)),
}
}
pub fn device_io_control(
handle: HANDLE,
io_control_code: DWORD,
in_buffer: &impl Copy,
out_buffer: &mut impl Copy,
) -> io::Result<()> {
let mut junk = 0;
match unsafe {
DeviceIoControl(
handle,
io_control_code,
in_buffer as *const _ as _,
mem::size_of_val(in_buffer) as _,
out_buffer as *mut _ as _,
mem::size_of_val(out_buffer) as _,
&mut junk,
ptr::null_mut(),
)
} {
0 => Err(io::Error::last_os_error()),
_ => Ok(()),
}
}
+48
View File
@@ -0,0 +1,48 @@
#![cfg(windows)]
mod tap;
mod tun;
mod ffi;
mod netsh;
mod route;
use std::{io, net};
pub use tap::TapDevice;
pub use tun::*;
/// Encode a string as a utf16 buffer
fn encode_utf16(string: &str) -> Vec<u16> {
use std::iter::once;
string.encode_utf16().chain(once(0)).collect()
}
/// Decode a string from a utf16 buffer
fn decode_utf16(string: &[u16]) -> String {
let end = string.iter().position(|b| *b == 0).unwrap_or(string.len());
String::from_utf16_lossy(&string[..end])
}
pub trait IFace {
fn shutdown(&self)->io::Result<()>;
/// 获取接口索引
fn get_index(&self) -> io::Result<u32>;
/// 获取名称
fn get_name(&self) -> io::Result<String>;
/// 设置名称
fn set_name(&self, new_name: &str) -> io::Result<()>;
/// 设置ip
fn set_ip<IP>(&self, address: IP, mask: IP) -> io::Result<()>
where IP: Into<net::Ipv4Addr>;
/// 设置路由
fn add_route<IP>(&self, dest: IP,
netmask: IP,
gateway: IP, ) -> io::Result<()>
where IP: Into<net::Ipv4Addr>;
/// 删除路由
fn delete_route<IP>(&self, dest: IP,
netmask: IP,
gateway: IP, ) -> io::Result<()>
where IP: Into<net::Ipv4Addr>;
/// 设置最大传输单元
fn set_mtu(&self, mtu: u16) -> io::Result<()>;
}
+48
View File
@@ -0,0 +1,48 @@
use std::io;
use std::net::Ipv4Addr;
/// 设置网卡名称
pub fn set_interface_name(old_name: &str, new_name: &str) -> io::Result<()> {
let cmd = format!(" netsh interface set interface name={:?} newname={:?}", old_name, new_name);
let out = std::process::Command::new("cmd")
.arg("/C")
.arg(&cmd)
.output()?;
if !out.status.success() {
log::warn!("修改网卡名称失败:cmd={:?},out={:?}",cmd,out);
return Err(io::Error::new(io::ErrorKind::Other, "修改网卡名称失败"));
}
Ok(())
}
/// 设置网卡ip
pub fn set_interface_ip(index: u32, address: &Ipv4Addr, netmask: &Ipv4Addr) -> io::Result<()> {
let set_address = format!(
"netsh interface ip set address {} static {:?} {:?} ",
index, address, netmask,
);
let out = std::process::Command::new("cmd")
.arg("/C")
.arg(&set_address)
.output()?;
if !out.status.success() {
log::error!("cmd={:?},out={:?}",set_address,out);
return Err(io::Error::new(io::ErrorKind::Other, format!("设置网络地址失败: {:?}", out)));
}
Ok(())
}
pub fn set_interface_mtu(index: u32, mtu: u16) -> io::Result<()> {
let set_mtu = format!(
"netsh interface ipv4 set subinterface {} mtu={} store=persistent",
index, mtu
);
let out = std::process::Command::new("cmd")
.arg("/C")
.arg(&set_mtu)
.output()?;
if !out.status.success() {
log::error!("cmd={:?},out={:?}",set_mtu,out);
return Err(io::Error::new(io::ErrorKind::Other, format!("设置mtu失败: {:?}", out)));
}
Ok(())
}
+44
View File
@@ -0,0 +1,44 @@
use std::io;
use std::net::Ipv4Addr;
/// 添加路由
pub fn add_route(index: u32, dest: Ipv4Addr,
netmask: Ipv4Addr,
gateway: Ipv4Addr, ) -> io::Result<()> {
let set_route = format!(
"route add {:?} mask {:?} {:?} if {}",
dest, netmask, gateway, index
);
// 执行添加路由命令
let out = std::process::Command::new("cmd")
.arg("/C")
.arg(&set_route)
.output()
.unwrap();
if !out.status.success() {
log::error!("cmd={:?},out={:?}",set_route,out);
return Err(io::Error::new(io::ErrorKind::Other, format!("添加路由失败: {:?}", out)));
}
Ok(())
}
/// 删除路由
pub fn delete_route(index: u32, dest: Ipv4Addr,netmask: Ipv4Addr, gateway: Ipv4Addr) -> io::Result<()> {
if index == 0 {
return Err(io::Error::new(io::ErrorKind::Other, format!("网络接口索引错误: {:?}", index)));
}
let delete_route = format!(
"route delete {:?} mask {:?} {:?} if {}",
dest, netmask, gateway, index
);
// 删除路由
let out = std::process::Command::new("cmd")
.arg("/C")
.arg(delete_route)
.output()
.unwrap();
if !out.status.success() {
return Err(io::Error::new(io::ErrorKind::Other, format!("删除路由失败: {:?}", out)));
}
Ok(())
}
+328
View File
@@ -0,0 +1,328 @@
use winapi::shared::ifdef::NET_LUID;
use winapi::shared::minwindef::*;
use winapi::um::fileapi::*;
use winapi::um::setupapi::*;
use winapi::um::winnt::*;
use scopeguard::{guard, ScopeGuard};
use winreg::RegKey;
use std::io;
use winapi::um::winbase::FILE_FLAG_OVERLAPPED;
use crate::{decode_utf16, encode_utf16, ffi};
/// tap-windows hardware ID
const HARDWARE_ID: &str = "tap0901";
winapi::DEFINE_GUID! {
GUID_NETWORK_ADAPTER,
0x4d36e972, 0xe325, 0x11ce,
0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18
}
/// Create a new interface and returns its NET_LUID
pub fn create_interface() -> io::Result<NET_LUID> {
let devinfo = ffi::create_device_info_list(&GUID_NETWORK_ADAPTER)?;
let _guard = guard((), |_| {
let _ = ffi::destroy_device_info_list(devinfo);
});
let class_name = ffi::class_name_from_guid(&GUID_NETWORK_ADAPTER)?;
let devinfo_data = ffi::create_device_info(
devinfo,
&class_name,
&GUID_NETWORK_ADAPTER,
&encode_utf16(""),
DICD_GENERATE_ID,
)?;
ffi::set_selected_device(devinfo, &devinfo_data)?;
ffi::set_device_registry_property(
devinfo,
&devinfo_data,
SPDRP_HARDWAREID,
&encode_utf16(HARDWARE_ID),
)?;
ffi::build_driver_info_list(devinfo, &devinfo_data, SPDIT_COMPATDRIVER)?;
let _guard = guard((), |_| {
let _ = ffi::destroy_driver_info_list(
devinfo,
&devinfo_data,
SPDIT_COMPATDRIVER,
);
});
let mut driver_version = 0;
let mut member_index = 0;
while let Some(drvinfo_data) = ffi::enum_driver_info(
devinfo,
&devinfo_data,
SPDIT_COMPATDRIVER,
member_index,
) {
member_index += 1;
let drvinfo_data = match drvinfo_data {
Ok(drvinfo_data) => drvinfo_data,
_ => continue,
};
if drvinfo_data.DriverVersion <= driver_version {
continue;
}
let drvinfo_detail = match ffi::get_driver_info_detail(
devinfo,
&devinfo_data,
&drvinfo_data,
) {
Ok(drvinfo_detail) => drvinfo_detail,
_ => continue,
};
let is_compatible = drvinfo_detail
.HardwareID
.split(|b| *b == 0)
.map(|id| decode_utf16(id))
.any(|id| id.eq_ignore_ascii_case(HARDWARE_ID));
if !is_compatible {
continue;
}
match ffi::set_selected_driver(devinfo, &devinfo_data, &drvinfo_data) {
Ok(_) => (),
_ => continue,
}
driver_version = drvinfo_data.DriverVersion;
}
if driver_version == 0 {
return Err(io::Error::new(io::ErrorKind::NotFound, "No driver found"));
}
let uninstaller = guard((), |_| {
let _ = ffi::call_class_installer(devinfo, &devinfo_data, DIF_REMOVE);
});
ffi::call_class_installer(devinfo, &devinfo_data, DIF_REGISTERDEVICE)?;
let _ = ffi::call_class_installer(
devinfo,
&devinfo_data,
DIF_REGISTER_COINSTALLERS,
);
let _ = ffi::call_class_installer(
devinfo,
&devinfo_data,
DIF_INSTALLINTERFACES,
);
ffi::call_class_installer(devinfo, &devinfo_data, DIF_INSTALLDEVICE)?;
let key = ffi::open_dev_reg_key(
devinfo,
&devinfo_data,
DICS_FLAG_GLOBAL,
0,
DIREG_DRV,
KEY_QUERY_VALUE | KEY_NOTIFY,
)?;
let key = RegKey::predef(key);
while let Err(_) = key.get_value::<DWORD, &str>("*IfType") {
ffi::notify_change_key_value(
key.raw_handle(),
TRUE,
REG_NOTIFY_CHANGE_NAME,
2000,
)?;
}
while let Err(_) = key.get_value::<DWORD, &str>("NetLuidIndex") {
ffi::notify_change_key_value(
key.raw_handle(),
TRUE,
REG_NOTIFY_CHANGE_NAME,
2000,
)?;
}
let if_type: DWORD = key.get_value("*IfType")?;
let luid_index: DWORD = key.get_value("NetLuidIndex")?;
// Defuse the uninstaller
ScopeGuard::into_inner(uninstaller);
let mut luid = NET_LUID { Value: 0 };
luid.set_IfType(if_type as _);
luid.set_NetLuidIndex(luid_index as _);
Ok(luid)
}
/// Check if the given interface exists and is a valid tap-windows device
pub fn check_interface(luid: &NET_LUID) -> io::Result<()> {
let devinfo = ffi::get_class_devs(&GUID_NETWORK_ADAPTER, DIGCF_PRESENT)?;
let _guard = guard((), |_| {
let _ = ffi::destroy_device_info_list(devinfo);
});
let mut member_index = 0;
while let Some(devinfo_data) = ffi::enum_device_info(devinfo, member_index)
{
member_index += 1;
let devinfo_data = match devinfo_data {
Ok(devinfo_data) => devinfo_data,
Err(_) => continue,
};
let hardware_id = match ffi::get_device_registry_property(
devinfo,
&devinfo_data,
SPDRP_HARDWAREID,
) {
Ok(hardware_id) => hardware_id,
Err(_) => continue,
};
if !decode_utf16(&hardware_id).eq_ignore_ascii_case(HARDWARE_ID) {
continue;
}
let key = match ffi::open_dev_reg_key(
devinfo,
&devinfo_data,
DICS_FLAG_GLOBAL,
0,
DIREG_DRV,
KEY_QUERY_VALUE | KEY_NOTIFY,
) {
Ok(key) => RegKey::predef(key),
Err(_) => continue,
};
let if_type: DWORD = match key.get_value("*IfType") {
Ok(if_type) => if_type,
Err(_) => continue,
};
let luid_index: DWORD = match key.get_value("NetLuidIndex") {
Ok(luid_index) => luid_index,
Err(_) => continue,
};
let mut luid2 = NET_LUID { Value: 0 };
luid2.set_IfType(if_type as _);
luid2.set_NetLuidIndex(luid_index as _);
if luid.Value != luid2.Value {
continue;
}
// Found it!
return Ok(());
}
Err(io::Error::new(io::ErrorKind::NotFound, "TAP Device not found"))
}
/// Deletes an existing interface
pub fn delete_interface(luid: &NET_LUID) -> io::Result<()> {
let devinfo = ffi::get_class_devs(&GUID_NETWORK_ADAPTER, DIGCF_PRESENT)?;
let _guard = guard((), |_| {
let _ = ffi::destroy_device_info_list(devinfo);
});
let mut member_index = 0;
while let Some(devinfo_data) = ffi::enum_device_info(devinfo, member_index)
{
member_index += 1;
let devinfo_data = match devinfo_data {
Ok(devinfo_data) => devinfo_data,
Err(_) => continue,
};
let hardware_id = match ffi::get_device_registry_property(
devinfo,
&devinfo_data,
SPDRP_HARDWAREID,
) {
Ok(hardware_id) => hardware_id,
Err(_) => continue,
};
if !decode_utf16(&hardware_id).eq_ignore_ascii_case(HARDWARE_ID) {
continue;
}
let key = match ffi::open_dev_reg_key(
devinfo,
&devinfo_data,
DICS_FLAG_GLOBAL,
0,
DIREG_DRV,
KEY_QUERY_VALUE | KEY_NOTIFY,
) {
Ok(key) => RegKey::predef(key),
Err(_) => continue,
};
let if_type: DWORD = match key.get_value("*IfType") {
Ok(if_type) => if_type,
Err(_) => continue,
};
let luid_index: DWORD = match key.get_value("NetLuidIndex") {
Ok(luid_index) => luid_index,
Err(_) => continue,
};
let mut luid2 = NET_LUID { Value: 0 };
luid2.set_IfType(if_type as _);
luid2.set_NetLuidIndex(luid_index as _);
if luid.Value != luid2.Value {
continue;
}
// Found it!
return ffi::call_class_installer(devinfo, &devinfo_data, DIF_REMOVE);
}
Err(io::Error::new(io::ErrorKind::NotFound, "TAP Device not found"))
}
/// Open an handle to an interface
pub fn open_interface(luid: &NET_LUID) -> io::Result<HANDLE> {
let guid = ffi::luid_to_guid(luid)
.and_then(|guid| ffi::string_from_guid(&guid))?;
let path = format!(r"\\.\Global\{}.tap", &decode_utf16(&guid));
ffi::create_file(
&encode_utf16(&path),
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
OPEN_EXISTING,
FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED,//FILE_ATTRIBUTE_SYSTEM,
)
}
+173
View File
@@ -0,0 +1,173 @@
use std::{io, net, time};
use std::net::Ipv4Addr;
use winapi::shared::ifdef::NET_LUID;
use winapi::shared::minwindef::*;
use winapi::um::winioctl::*;
use winapi::um::winnt::HANDLE;
use crate::{decode_utf16, encode_utf16, ffi, IFace, netsh, route};
mod iface;
pub struct TapDevice {
luid: NET_LUID,
handle: HANDLE,
}
unsafe impl Send for TapDevice{}
unsafe impl Sync for TapDevice{}
impl TapDevice {
/// Retieve the mac of the interface
pub fn get_mac(&self) -> io::Result<[u8; 6]> {
let mut mac = [0; 6];
ffi::device_io_control(
self.handle,
CTL_CODE(FILE_DEVICE_UNKNOWN, 1, METHOD_BUFFERED, FILE_ANY_ACCESS),
&(),
&mut mac,
)
.map(|_| mac)
}
/// Retrieve the version of the driver
pub fn get_version(&self) -> io::Result<[u32; 3]> {
let mut version = [0; 3];
ffi::device_io_control(
self.handle,
CTL_CODE(FILE_DEVICE_UNKNOWN, 2, METHOD_BUFFERED, FILE_ANY_ACCESS),
&(),
&mut version,
)
.map(|_| version)
}
/// Retieve the mtu of the interface
pub fn get_mtu(&self) -> io::Result<u32> {
let mut mtu = 0;
ffi::device_io_control(
self.handle,
CTL_CODE(FILE_DEVICE_UNKNOWN, 3, METHOD_BUFFERED, FILE_ANY_ACCESS),
&(),
&mut mtu,
)
.map(|_| mtu)
}
/// Set the status of the interface, true for connected,
/// false for disconnected.
pub fn set_status(&self, status: bool) -> io::Result<()> {
let status: u32 = if status { 1 } else { 0 };
ffi::device_io_control(
self.handle,
CTL_CODE(FILE_DEVICE_UNKNOWN, 6, METHOD_BUFFERED, FILE_ANY_ACCESS),
&status,
&mut (),
)
}
}
impl TapDevice {
pub fn create() -> io::Result<Self> {
let luid = iface::create_interface()?;
// Even after retrieving the luid, we might need to wait
let start = time::Instant::now();
let handle = loop {
// If we surpassed 2 seconds just return
let now = time::Instant::now();
if now - start > time::Duration::from_secs(3) {
return Err(io::Error::new(
io::ErrorKind::TimedOut,
"Interface timed out",
));
}
match iface::open_interface(&luid) {
Err(_) => {
std::thread::yield_now();
continue;
}
Ok(handle) => break handle,
};
};
Ok(Self { luid, handle })
}
pub fn open(name: &str) -> io::Result<Self> {
let name = encode_utf16(name);
let luid = ffi::alias_to_luid(&name)?;
iface::check_interface(&luid)?;
let handle = iface::open_interface(&luid)?;
Ok(Self { luid, handle })
}
pub fn delete(self) -> io::Result<()> {
iface::delete_interface(&self.luid)
}
}
impl IFace for TapDevice {
fn shutdown(&self) -> io::Result<()> {
self.set_status(false)
}
fn get_index(&self) -> io::Result<u32> {
ffi::luid_to_index(&self.luid).map(|index| index as u32)
}
fn get_name(&self) -> io::Result<String> {
ffi::luid_to_alias(&self.luid).map(|name| decode_utf16(&name))
}
fn set_name(&self, new_name: &str) -> io::Result<()> {
let name = self.get_name()?;
netsh::set_interface_name(&name, new_name)
}
fn set_ip<IP>(&self, address: IP, mask: IP) -> io::Result<()> where IP: Into<Ipv4Addr> {
let index = self.get_index()?;
netsh::set_interface_ip(index, &address.into(), &mask.into())
}
fn add_route<IP>(&self, dest: IP, netmask: IP, gateway: IP) -> io::Result<()> where IP: Into<Ipv4Addr> {
let index = self.get_index()?;
route::add_route(index, dest.into(), netmask.into(), gateway.into())
}
fn delete_route<IP>(&self, dest: IP, netmask: IP, gateway: IP) -> io::Result<()> where IP: Into<Ipv4Addr> {
let index = self.get_index()?;
route::delete_route(index, dest.into(), netmask.into(), gateway.into())
}
fn set_mtu(&self, mtu: u16) -> io::Result<()> {
let index = self.get_index()?;
netsh::set_interface_mtu(index, mtu)
}
}
impl TapDevice {
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
ffi::read_file(self.handle, buf).map(|res| res as _)
}
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
ffi::write_file(self.handle, buf).map(|res| res as _)
}
}
impl Drop for TapDevice {
fn drop(&mut self) {
let _ = ffi::close_handle(self.handle);
let _ = iface::delete_interface(&self.luid);
}
}
@@ -1,17 +1,16 @@
use crate::wintun_raw;
use crate::Wintun;
use log::*; use log::*;
use widestring::U16CStr;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use widestring::U16CStr;
use crate::tun::wintun_raw;
/// Sets the logger wintun will use when logging. Maps to the WintunSetLogger C function /// Sets the logger wintun will use when logging. Maps to the WintunSetLogger C function
pub fn set_logger(wintun: &Wintun, f: wintun_raw::WINTUN_LOGGER_CALLBACK) { pub fn set_logger(win_tun: &wintun_raw::wintun, f: wintun_raw::WINTUN_LOGGER_CALLBACK) {
unsafe { wintun.WintunSetLogger(f) }; unsafe { win_tun.WintunSetLogger(f) };
} }
pub fn reset_logger(wintun: &Wintun) { pub fn reset_logger(win_tun: &wintun_raw::wintun) {
set_logger(wintun, None); set_logger(win_tun, None);
} }
static SET_LOGGER: AtomicBool = AtomicBool::new(false); static SET_LOGGER: AtomicBool = AtomicBool::new(false);
@@ -38,11 +37,11 @@ pub unsafe extern "C" fn default_logger(
} }
} }
pub(crate) fn set_default_logger_if_unset(wintun: &Wintun) { pub(crate) fn set_default_logger_if_unset(win_tun: &wintun_raw::wintun) {
if SET_LOGGER if SET_LOGGER
.compare_exchange(false, true, Ordering::SeqCst, Ordering::Relaxed) .compare_exchange(false, true, Ordering::SeqCst, Ordering::Relaxed)
.is_ok() .is_ok()
{ {
set_logger(wintun, Some(default_logger)); set_logger(win_tun, Some(default_logger));
} }
} }
+301
View File
@@ -0,0 +1,301 @@
use std::io;
use std::net::Ipv4Addr;
use winapi::um::{handleapi, synchapi, winbase, winnt};
use crate::{decode_utf16, encode_utf16, ffi, IFace, netsh, route};
mod wintun_raw;
mod log;
pub mod packet;
/// The maximum size of wintun's internal ring buffer (in bytes)
pub const MAX_RING_CAPACITY: u32 = 0x400_0000;
/// The minimum size of wintun's internal ring buffer (in bytes)
pub const MIN_RING_CAPACITY: u32 = 0x2_0000;
/// Maximum pool name length including zero terminator
pub const MAX_POOL: usize = 256;
pub struct TunDevice {
/// The session handle given to us by WintunStartSession
pub(crate) session: wintun_raw::WINTUN_SESSION_HANDLE,
/// Shared dll for required wintun driver functions
pub(crate) win_tun: wintun_raw::wintun,
/// Windows event handle that is signaled by the wintun driver when data becomes available to
/// read
pub(crate) read_event: winnt::HANDLE,
/// Windows event handle that is signaled when [`TunSession::shutdown`] is called force blocking
/// readers to exit
pub(crate) shutdown_event: winnt::HANDLE,
/// The adapter that owns this session
pub(crate) adapter: wintun_raw::WINTUN_ADAPTER_HANDLE,
}
unsafe impl Send for TunDevice {}
unsafe impl Sync for TunDevice {}
winapi::DEFINE_GUID! {
GUID_NETWORK_ADAPTER,
0x4d36e972, 0xe325, 0x11ce,
0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18
}
impl TunDevice {
pub unsafe fn create<L>(library: L, pool: &str, name: &str) -> io::Result<Self>
where L: Into<libloading::Library>, {
let win_tun = match wintun_raw::wintun::from_library(library) {
Ok(win_tun) => { win_tun }
Err(e) => {
return Err(io::Error::new(io::ErrorKind::Other, format!("library error {:?} ", e)));
}
};
let pool_utf16 = encode_utf16(pool);
if pool_utf16.len() > MAX_POOL {
return Err(io::Error::new(io::ErrorKind::Other, format!("长度大于{}:{:?}", MAX_POOL, pool)));
}
let name_utf16 = encode_utf16(name);
if name_utf16.len() > MAX_POOL {
return Err(io::Error::new(io::ErrorKind::Other, format!("长度大于{}:{:?}", MAX_POOL, pool)));
}
//SAFETY: guid is a unique integer so transmuting either all zeroes or the user's preferred
//guid to the winapi guid type is safe and will allow the windows kernel to see our GUID
let guid_struct: wintun_raw::GUID = unsafe { std::mem::transmute(GUID_NETWORK_ADAPTER) };
let guid_ptr = &guid_struct as *const wintun_raw::GUID;
log::set_default_logger_if_unset(&win_tun);
//SAFETY: the function is loaded from the wintun dll properly, we are providing valid
//pointers, and all the strings are correct null terminated UTF-16. This safety rationale
//applies for all Wintun* functions below
let adapter = win_tun.WintunCreateAdapter(pool_utf16.as_ptr(), name_utf16.as_ptr(), guid_ptr);
if adapter.is_null() {
return Err(io::Error::new(io::ErrorKind::Other, "Failed to crate adapter"));
}
Self::init(win_tun, adapter)
}
pub unsafe fn init(win_tun: wintun_raw::wintun, adapter: wintun_raw::WINTUN_ADAPTER_HANDLE) -> io::Result<Self> {
// 开启session
let session = win_tun.WintunStartSession(adapter, 128 * 1024);
if session.is_null() {
return Err(io::Error::new(io::ErrorKind::Other, "WintunStartSession failed"));
}
//SAFETY: We follow the contract required by CreateEventA. See MSDN
//(the pointers are allowed to be null, and 0 is okay for the others)
let shutdown_event = synchapi::CreateEventA(std::ptr::null_mut(),
0, 0, std::ptr::null_mut());
let read_event = win_tun.WintunGetReadWaitEvent(session) as winnt::HANDLE;
Ok(TunDevice {
session,
win_tun,
read_event,
shutdown_event,
adapter,
})
}
pub unsafe fn open<L>(library: L, name: &str) -> io::Result<Self>
where L: Into<libloading::Library>, {
let win_tun = match wintun_raw::wintun::from_library(library) {
Ok(win_tun) => win_tun,
Err(e) => {
return Err(io::Error::new(io::ErrorKind::Other, format!("library error {:?} ", e)));
}
};
log::set_default_logger_if_unset(&win_tun);
let name_utf16 = encode_utf16(name);
let adapter = win_tun.WintunOpenAdapter(name_utf16.as_ptr());
if adapter.is_null() {
return Err(io::Error::new(io::ErrorKind::Other, "Failed to open adapter"));
}
Self::init(win_tun, adapter)
}
pub fn delete(self) -> io::Result<()> {
drop(self);
Ok(())
}
pub fn version(&self) -> io::Result<Version> {
let version = unsafe { self.win_tun.WintunGetRunningDriverVersion() };
if version == 0 {
return Err(io::Error::new(io::ErrorKind::Other, "WintunGetRunningDriverVersion"));
} else {
Ok(Version {
major: ((version >> 16) & 0xFF) as u16,
minor: (version & 0xFF) as u16,
})
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct Version {
pub major: u16,
pub minor: u16,
}
impl TunDevice {
fn get_adapter_luid(&self) -> u64 {
let mut luid: wintun_raw::NET_LUID = unsafe { std::mem::zeroed() };
unsafe { self.win_tun.WintunGetAdapterLUID(self.adapter, &mut luid as *mut wintun_raw::NET_LUID) };
unsafe { std::mem::transmute(luid) }
}
}
impl IFace for TunDevice {
fn shutdown(&self) -> io::Result<()> {
let _ = unsafe { synchapi::SetEvent(self.shutdown_event) };
let _ = unsafe { handleapi::CloseHandle(self.shutdown_event) };
Ok(())
}
fn get_index(&self) -> io::Result<u32> {
let luid = self.get_adapter_luid();
ffi::luid_to_index(&unsafe { std::mem::transmute(luid) }).map(|index| index as u32)
}
fn get_name(&self) -> io::Result<String> {
let luid = self.get_adapter_luid();
ffi::luid_to_alias(&unsafe { std::mem::transmute(luid) }).map(|name| {
decode_utf16(&name)
})
}
fn set_name(&self, new_name: &str) -> io::Result<()> {
let name = self.get_name()?;
netsh::set_interface_name(&name, new_name)
}
fn set_ip<IP>(&self, address: IP, mask: IP) -> io::Result<()> where IP: Into<Ipv4Addr> {
netsh::set_interface_ip(self.get_index()?, &address.into(), &mask.into())
}
fn add_route<IP>(&self, dest: IP, netmask: IP, gateway: IP) -> io::Result<()> where IP: Into<Ipv4Addr> {
route::add_route(self.get_index()?, dest.into(), netmask.into(), gateway.into())
}
fn delete_route<IP>(&self, dest: IP, netmask: IP, gateway: IP) -> io::Result<()> where IP: Into<Ipv4Addr> {
route::delete_route(self.get_index()?, dest.into(), netmask.into(), gateway.into())
}
fn set_mtu(&self, mtu: u16) -> io::Result<()> {
netsh::set_interface_mtu(self.get_index()?, mtu)
}
}
impl TunDevice {
pub fn try_receive(&self) -> io::Result<Option<packet::TunPacket>> {
let mut size = 0u32;
let bytes_ptr = unsafe {
self.win_tun
.WintunReceivePacket(self.session, &mut size as *mut u32)
};
debug_assert!(size <= u16::MAX as u32);
if bytes_ptr.is_null() {
//Wintun returns ERROR_NO_MORE_ITEMS instead of blocking if packets are not available
let last_error = unsafe { winapi::um::errhandlingapi::GetLastError() };
if last_error == winapi::shared::winerror::ERROR_NO_MORE_ITEMS {
Ok(None)
} else {
Err(io::Error::new(io::ErrorKind::Other, "try_receive failed"))
}
} else {
Ok(Some(packet::TunPacket {
kind: packet::Kind::ReceivePacket,
size: size as usize,
//SAFETY: ptr is non null, aligned for u8, and readable for up to size bytes (which
//must be less than isize::MAX because bytes is a u16
bytes_ptr,
tun_device: Some(&self),
}))
}
}
pub fn receive_blocking(&self) -> io::Result<packet::TunPacket> {
loop {
//Try 5 times to receive without blocking so we don't have to issue a syscall to wait
//for the event if packets are being received at a rapid rate
for _ in 0..5 {
match self.try_receive()? {
None => {
continue;
}
Some(packet) => {
return Ok(packet);
}
}
}
//Wait on both the read handle and the shutdown handle so that we stop when requested
let handles = [self.read_event, self.shutdown_event];
let result = unsafe {
//SAFETY: We abide by the requirements of WaitForMultipleObjects, handles is a
//pointer to valid, aligned, stack memory
synchapi::WaitForMultipleObjects(
2,
&handles as *const winnt::HANDLE,
0,
winbase::INFINITE,
)
};
match result {
winbase::WAIT_FAILED => return Err(io::Error::new(io::ErrorKind::Other, "WAIT_FAILED")),
_ => {
if result == winbase::WAIT_OBJECT_0 {
//We have data!
continue;
} else if result == winbase::WAIT_OBJECT_0 + 1 {
//Shutdown event triggered
return Err(io::Error::new(io::ErrorKind::Other, "Shutdown event triggered"));
}
}
}
}
}
}
impl TunDevice {
pub fn allocate_send_packet(&self, size: u16) -> io::Result<packet::TunPacket> {
let bytes_ptr = unsafe {
self.win_tun.WintunAllocateSendPacket(self.session, size as u32)
};
if bytes_ptr.is_null() {
Err(io::Error::new(io::ErrorKind::Other, "allocate_send_packet failed"))
} else {
Ok(packet::TunPacket {
kind: packet::Kind::SendPacketPending,
size: size as usize,
//SAFETY: ptr is non null, aligned for u8, and readable for up to size bytes (which
//must be less than isize::MAX because bytes is a u16
bytes_ptr,
tun_device: None,
})
}
}
pub fn send_packet(&self, mut packet: packet::TunPacket) {
assert!(matches!(packet.kind, packet::Kind::SendPacketPending));
unsafe {
self.win_tun
.WintunSendPacket(self.session, packet.bytes_ptr)
};
//Mark the packet at sent
packet.kind = packet::Kind::SendPacketSent;
}
}
impl Drop for TunDevice {
fn drop(&mut self) {
//Close adapter on drop
//This is why we need an Arc of wintun
unsafe {
self.win_tun.WintunCloseAdapter(self.adapter);
self.win_tun.WintunDeleteDriver()
};
}
}
+64
View File
@@ -0,0 +1,64 @@
use crate::TunDevice;
pub(crate) enum Kind {
SendPacketPending,
//Send packet type, but not sent yet
SendPacketSent,
//Send packet type - sent
ReceivePacket,
}
/// Represents a wintun packet
pub struct TunPacket<'a> {
pub(crate) kind: Kind,
pub(crate) size:usize,
pub(crate) bytes_ptr: *const u8,
//Share ownership of session to prevent the session from being dropped before packets that
//belong to it
pub(crate) tun_device: Option<&'a TunDevice>,
}
impl <'a>TunPacket<'a> {
/// Returns the bytes this packet holds as &mut.
/// The lifetime of the bytes is tied to the lifetime of this packet.
pub fn bytes_mut(&mut self) -> &mut [u8] {
unsafe { std::slice::from_raw_parts_mut(self.bytes_ptr as *mut u8, self.size) }
}
/// Returns an immutable reference to the bytes this packet holds.
/// The lifetime of the bytes is tied to the lifetime of this packet.
pub fn bytes(&self) -> &[u8] {
unsafe { std::slice::from_raw_parts(self.bytes_ptr,self.size) }
}
}
impl <'a>Drop for TunPacket<'a> {
fn drop(&mut self) {
match self.kind {
Kind::ReceivePacket => {
unsafe {
//SAFETY:
//
// 1. We share ownership of the session therefore it hasn't been dropped yet
// 2. Bytes is valid because each packet holds exclusive access to a region of the
// ring buffer that the wintun session owns. We return that region of
// memory back to wintun here
let tun_device = self.tun_device.unwrap();
tun_device.win_tun
.WintunReleaseReceivePacket(tun_device.session, self.bytes_ptr)
};
}
Kind::SendPacketPending => {
//If someone allocates a packet with session.allocate_send_packet() and then it is
//dropped without being sent, this will hold up the send queue because wintun expects
//that every allocated packet is sent
panic!("Packet was never sent!");
}
Kind::SendPacketSent => {
//Nop
}
}
}
}
@@ -1,5 +1,4 @@
/* automatically generated by rust-bindgen 0.59.1 */ /* automatically generated by rust-bindgen 0.59.1 */
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] #[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct __BindgenBitfieldUnit<Storage> { pub struct __BindgenBitfieldUnit<Storage> {
-14
View File
@@ -1,14 +0,0 @@
out.pcap
# Generated by Cargo
# will have compiled files and executables
debug/
target/
# These are backup files generated by rustfmt
**/*.rs.bk
# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb
/.idea
-60
View File
@@ -1,60 +0,0 @@
# ChangeLog
This format is based on [Keep a Changelog](https://keepachangelog.com/)
and this project adheres to [Semantic Versioning](https://semver.org).
## [0.2.1] - 2021-12-03
### Fixed
Type in readme
## [0.2.0] - 2021-12-03
Added support for wintun 0.14.
### Breaking Changes
- Wintun driver versions before `0.14` are no longer support due to beraking
changes in the C API
- `Adapter::create` returns a `Result<Adapter, ...>` instead of a `Result<CreateData, ...>`.
This was done because the underlying Wintun function was changed to only return an adapter handle
- `Adapter::create` the pool parameter was removed because it was also removed from the C function
- `Adapter::delete` takes no parameters and returns a `Result<(), ()>`.
The `force_close_sessions` parameter was removed because it was removed from the
C function. Same for the bool inside the Ok(..) variant
- `Adapter::create` and `Adapter::open` return `Arc<Adapter>` instead of `Adapter`
- `get_running_driver_version` now returns a proper Result<Version, ()>.
### Added
- `reset_logger` function to disable logging after a logger has been set.
## [0.1.5] - 2021-08-27
### Fixed
- Readme on crates.io
## [0.1.4] - 2021-08-27
### Added
- `panic_on_unsent_packets` feature flag to help in debugging ring buffer blockage issues
## [0.1.3] - 2021-06-28
### Fixed
- Cargo.toml metadata to include `package.metadata.docs.rs.default-target`.
Fixes build issue on docs.rs (we can only build docs on windows, 0.1.1 doesn't work)
## [0.1.2] - 2021-06-28
docs.rs testing
## [0.1.1] - 2021-06-28
- Cargo.toml metadata to build on linux
## [0.1.0] - 2021-06-28
First release with initial api
-427
View File
@@ -1,427 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "aho-corasick"
version = "0.7.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
dependencies = [
"memchr",
]
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "byteorder"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "derive-into-owned"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "576fce04d31d592013a5887ba8d9c3830adff329e5096d7e1eb5e8e61262ca62"
dependencies = [
"quote 0.3.15",
"syn 0.11.11",
]
[[package]]
name = "either"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
[[package]]
name = "env_logger"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3"
dependencies = [
"atty",
"humantime",
"log",
"regex",
"termcolor",
]
[[package]]
name = "getrandom"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]]
name = "humantime"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "hwaddr"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e414433a9e4338f4e87fa29d0670c883a5e73e7955c45f4a49130c0aa992c85b"
dependencies = [
"phf",
]
[[package]]
name = "itertools"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf"
dependencies = [
"either",
]
[[package]]
name = "libc"
version = "0.2.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119"
[[package]]
name = "libloading"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afe203d669ec979b7128619bae5a63b7b42e9203c1b29146079ee05e2f604b52"
dependencies = [
"cfg-if",
"winapi",
]
[[package]]
name = "log"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [
"cfg-if",
]
[[package]]
name = "memchr"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
[[package]]
name = "once_cell"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
[[package]]
name = "packet"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c136c7ad0619ed4f88894aecf66ad86c80683e7b5d707996e6a3a7e0e3916944"
dependencies = [
"bitflags",
"byteorder",
"hwaddr",
"thiserror",
]
[[package]]
name = "pcap-file"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ad13fed1a83120159aea81b265074f21d753d157dd16b10cc3790ecba40a341"
dependencies = [
"byteorder",
"derive-into-owned",
"thiserror",
]
[[package]]
name = "phf"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
dependencies = [
"phf_shared",
]
[[package]]
name = "phf_shared"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7"
dependencies = [
"siphasher",
]
[[package]]
name = "ppv-lite86"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba"
[[package]]
name = "proc-macro2"
version = "1.0.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43"
dependencies = [
"unicode-xid 0.2.2",
]
[[package]]
name = "quote"
version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
[[package]]
name = "quote"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
"rand_hc",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
dependencies = [
"getrandom",
]
[[package]]
name = "rand_hc"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
dependencies = [
"rand_core",
]
[[package]]
name = "regex"
version = "1.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.6.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
[[package]]
name = "siphasher"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "533494a8f9b724d33625ab53c6c4800f7cc445895924a8ef649222dcb76e938b"
[[package]]
name = "subprocess"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "055cf3ebc2981ad8f0a5a17ef6652f652d87831f79fddcba2ac57bcb9a0aa407"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "syn"
version = "0.11.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad"
dependencies = [
"quote 0.3.15",
"synom",
"unicode-xid 0.0.4",
]
[[package]]
name = "syn"
version = "1.0.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59"
dependencies = [
"proc-macro2",
"quote 1.0.10",
"unicode-xid 0.2.2",
]
[[package]]
name = "synom"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6"
dependencies = [
"unicode-xid 0.0.4",
]
[[package]]
name = "termcolor"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
dependencies = [
"winapi-util",
]
[[package]]
name = "thiserror"
version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
dependencies = [
"proc-macro2",
"quote 1.0.10",
"syn 1.0.82",
]
[[package]]
name = "unicode-xid"
version = "0.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc"
[[package]]
name = "unicode-xid"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "wasi"
version = "0.10.2+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
[[package]]
name = "widestring"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c168940144dd21fd8046987c16a46a33d5fc84eec29ef9dcddc2ac9e31526b7c"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "wintun"
version = "0.2.1"
dependencies = [
"env_logger",
"itertools",
"libloading",
"log",
"once_cell",
"packet",
"pcap-file",
"rand",
"subprocess",
"widestring",
"winapi",
]
-35
View File
@@ -1,35 +0,0 @@
[package]
name = "wintun"
version = "0.2.1"
edition = "2021"
authors = ["null.black Inc. <[email protected]>", "Troy Neubauer <[email protected]>"]
repository = "https://github.com/nulldotblack/wintun"
readme = "README.md"
documentation = "https://docs.rs/wintun/"
description = "Safe idiomatic bindings to the WinTun C library"
license = "MIT"
keywords = ["wintun", "tap", "tun", "vpn", "wireguard"]
categories = ["api-bindings"]
[package.metadata.docs.rs]
default-target = "x86_64-pc-windows-msvc"
targets = ["aarch64-pc-windows-msvc", "i686-pc-windows-msvc", "x86_64-pc-windows-msvc"]
[features]
panic_on_unsent_packets = []
[dependencies]
winapi = { version = "0.3", features = ["synchapi", "winbase", "winerror", "ipexport", "iphlpapi", "handleapi"] }
widestring = "0.4"
libloading = "0.7"
once_cell = "1.8"
log = "0.4"
rand = "0.8.3"
itertools = "0.10.1"
[dev-dependencies]
env_logger = "0.8"
winapi = { version = "0.3", features = ["netioapi", "iptypes", "iphlpapi", "nldef"] }
packet = "0.1.4"
pcap-file = "1.1.1"
subprocess = "0.2.7"
-7
View File
@@ -1,7 +0,0 @@
Copyright 2021 null.black Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-80
View File
@@ -1,80 +0,0 @@
# wintun
Safe rust idiomatic bindings for the Wintun C library: <https://wintun.net>
All features of the Wintun library are wrapped using pure rust types and functions to make
usage feel ergonomic.
## Usage
Inside your code load the wintun.dll signed driver file, downloaded from <https://wintun.net>,
using [`load`], [`load_from_path`] or [`load_from_library`].
Then either call [`Adapter::create`] or [`Adapter::open`] to obtain a wintun
adapter. Start a session with [`Adapter::start_session`].
## Example
```rust
use std::sync::Arc;
//Must be run as Administrator because we create network adapters
//Load the wintun dll file so that we can call the underlying C functions
//Unsafe because we are loading an arbitrary dll file
let wintun = unsafe { wintun::load_from_path("path/to/wintun.dll") }
.expect("Failed to load wintun dll");
//Try to open an adapter with the name "Demo"
let adapter = match wintun::Adapter::open(&wintun, "Demo") {
Ok(a) => a,
Err(_) => {
//If loading failed (most likely it didn't exist), create a new one
wintun::Adapter::create(&wintun, "Example", "Demo", None)
.expect("Failed to create wintun adapter!")
}
};
//Specify the size of the ring buffer the wintun driver should use.
let session = Arc::new(adapter.start_session(wintun::MAX_RING_CAPACITY).unwrap());
//Get a 20 byte packet from the ring buffer
let mut packet = session.allocate_send_packet(20).unwrap();
let bytes: &mut [u8] = packet.bytes_mut();
//Write IPV4 version and header length
bytes[0] = 0x40;
//Finish writing IP header
bytes[9] = 0x69;
bytes[10] = 0x04;
bytes[11] = 0x20;
//...
//Send the packet to wintun virtual adapter for processing by the system
session.send_packet(packet);
//Stop any readers blocking for data on other threads
//Only needed when a blocking reader is preventing shutdown Ie. it holds an Arc to the
//session, blocking it from being dropped
session.shutdown();
//the session is stopped on drop
//drop(session);
//drop(adapter)
//And the adapter closes its resources when dropped
```
See `examples/wireshark.rs` for a more complete example that writes received packets to a pcap
file.
## Features
- `panic_on_unsent_packets`: Panics if a send packet is dropped without being sent. Useful for
debugging packet issues because unsent packets that are dropped without being sent hold up
wintun's internal ring buffer.
## TODO:
- Add async support
Requires hooking into a windows specific reactor and registering read interest on wintun's read
handle. Asyncify other slow operations via tokio::spawn_blocking. As always, PR's are welcome!
License: MIT
-7
View File
@@ -1,7 +0,0 @@
#!/bin/bash
bindgen \
--allowlist-function "Wintun.*" \
--allowlist-type "WINTUN_.*" \
--dynamic-loading wintun \
--dynamic-link-require-all \
wintun/wintun_functions.h > src/wintun_raw.rs
-345
View File
@@ -1,345 +0,0 @@
/// Representation of a winton adapter with safe idiomatic bindings to the functionality provided by
/// the WintunAdapter* C functions.
///
/// The [`Adapter::create`] and [`Adapter::open`] functions serve as the entry point to using
/// wintun functionality
use crate::error;
use crate::session;
use crate::util;
use crate::util::UnsafeHandle;
use crate::wintun_raw;
use crate::Wintun;
use std::ptr;
use std::sync::Arc;
use itertools::Itertools;
use log::*;
use once_cell::sync::OnceCell;
use rand::Rng;
use widestring::U16CStr;
use widestring::U16CString;
use winapi::{
shared::winerror,
um::{ipexport, iphlpapi, synchapi},
};
/// Wrapper around a <https://git.zx2c4.com/wintun/about/#wintun_adapter_handle>
pub struct Adapter {
adapter: UnsafeHandle<wintun_raw::WINTUN_ADAPTER_HANDLE>,
wintun: Wintun,
guid: u128,
}
fn encode_utf16(string: &str, max_characters: usize) -> Result<U16CString, error::WintunError> {
let utf16 = U16CString::from_str(string)?;
if utf16.len() >= max_characters {
//max_characters is the maximum number of characters including the null terminator. And .len() measures the
//number of characters (excluding the null terminator). Therefore we can hold a string with
//max_characters - 1 because the null terminator sits in the last element. However a string
//of length max_characters needs max_characters + 1 to store the null terminator the >=
//check holds
Err(format!(
//TODO: Better error handling
"Length too large. Size: {}, Max: {}",
utf16.len(),
max_characters
)
.into())
} else {
Ok(utf16)
}
}
fn encode_pool_name(name: &str) -> Result<U16CString, error::WintunError> {
encode_utf16(name, crate::MAX_POOL)
}
fn encode_adapter_name(name: &str) -> Result<U16CString, error::WintunError> {
encode_utf16(name, crate::MAX_POOL)
}
fn get_adapter_luid(wintun: &Wintun, adapter: wintun_raw::WINTUN_ADAPTER_HANDLE) -> u64 {
let mut luid: wintun_raw::NET_LUID = unsafe { std::mem::zeroed() };
unsafe { wintun.WintunGetAdapterLUID(adapter, &mut luid as *mut wintun_raw::NET_LUID) };
unsafe { std::mem::transmute(luid) }
}
impl Adapter {
//TODO: Call get last error for error information on failure and improve error types
/// Creates a new wintun adapter inside the pool `pool` with name `name`
///
/// Optionally a GUID can be specified that will become the GUID of this adapter once created.
/// Adapters obtained via this function will be able to return their adapter index via
/// [`Adapter::get_adapter_index`]
pub fn create(
wintun: &Wintun,
pool: &str,
name: &str,
guid: Option<u128>,
) -> Result<Arc<Adapter>, error::WintunError> {
let pool_utf16 = encode_pool_name(pool)?;
let name_utf16 = encode_adapter_name(name)?;
let guid = match guid {
Some(guid) => guid,
None => {
// Use random bytes so that we can identify this adapter in get_adapter_index
let mut guid_bytes: [u8; 16] = [0u8; 16];
rand::thread_rng().fill(&mut guid_bytes);
u128::from_ne_bytes(guid_bytes)
}
};
//SAFETY: guid is a unique integer so transmuting either all zeroes or the user's preferred
//guid to the winapi guid type is safe and will allow the windows kernel to see our GUID
let guid_struct: wintun_raw::GUID = unsafe { std::mem::transmute(guid) };
//TODO: The guid of the adapter once created might differ from the one provided because of
//the byte order of the segments of the GUID struct that are larger than a byte. Verify
//that this works as expected
let guid_ptr = &guid_struct as *const wintun_raw::GUID;
crate::log::set_default_logger_if_unset(wintun);
//SAFETY: the function is loaded from the wintun dll properly, we are providing valid
//pointers, and all the strings are correct null terminated UTF-16. This safety rationale
//applies for all Wintun* functions below
let result = unsafe {
wintun.WintunCreateAdapter(pool_utf16.as_ptr(), name_utf16.as_ptr(), guid_ptr)
};
if result.is_null() {
Err("Failed to crate adapter".into())
} else {
Ok(Arc::new(Adapter {
adapter: UnsafeHandle(result),
wintun: wintun.clone(),
guid,
}))
}
}
/// Attempts to open an existing wintun interface name `name`.
///
/// Adapters opened via this call will have an unknown GUID meaning [`Adapter::get_adapter_index`]
/// will always fail because knowing the adapter's GUID is required to determine its index.
/// Currently a workaround is to delete and re-create a new adapter every time one is needed so
/// that it gets created with a known GUID, allowing [`Adapter::get_adapter_index`] to works as
/// expected. There is likely a way to get the GUID of our adapter using the Windows Registry
/// or via the Win32 API, so PR's that solve this issue are always welcome!
pub fn open(wintun: &Wintun, name: &str) -> Result<Arc<Adapter>, error::WintunError> {
let name_utf16 = encode_adapter_name(name)?;
crate::log::set_default_logger_if_unset(wintun);
let result = unsafe { wintun.WintunOpenAdapter(name_utf16.as_ptr()) };
if result.is_null() {
Err("WintunOpenAdapter failed".into())
} else {
Ok(Arc::new(Adapter {
adapter: UnsafeHandle(result),
wintun: wintun.clone(),
// TODO: get GUID somehow
guid: 0,
}))
}
}
/// Delete an adapter, consuming it in the process
pub fn delete(self) -> Result<(), ()> {
//Dropping an adapter closes it
drop(self);
// Return a result here so that if later the API changes to be fallible, we can support it
// without making a breaking change
Ok(())
}
/// Initiates a new wintun session on the given adapter.
///
/// Capacity is the size in bytes of the ring buffer used internally by the driver. Must be
/// a power of two between [`crate::MIN_RING_CAPACITY`] and [`crate::MIN_RING_CAPACITY`].
pub fn start_session(
self: &Arc<Self>,
capacity: u32,
) -> Result<session::Session, error::WintunError> {
let range = crate::MIN_RING_CAPACITY..=crate::MAX_RING_CAPACITY;
if !range.contains(&capacity) {
return Err(Box::new(error::ApiError::CapacityOutOfRange(
error::OutOfRangeData {
range,
value: capacity,
},
)));
}
if !capacity.is_power_of_two() {
return Err(Box::new(error::ApiError::CapacityNotPowerOfTwo(capacity)));
}
let result = unsafe { self.wintun.WintunStartSession(self.adapter.0, capacity) };
if result.is_null() {
Err("WintunStartSession failed".into())
} else {
Ok(session::Session {
session: UnsafeHandle(result),
wintun: self.wintun.clone(),
read_event: OnceCell::new(),
shutdown_event: unsafe {
//SAFETY: We follow the contract required by CreateEventA. See MSDN
//(the pointers are allowed to be null, and 0 is okay for the others)
UnsafeHandle(synchapi::CreateEventA(
std::ptr::null_mut(),
0,
0,
std::ptr::null_mut(),
))
},
adapter: Arc::clone(self),
})
}
}
/// Returns the Win32 LUID for this adapter
pub fn get_luid(&self) -> u64 {
get_adapter_luid(&self.wintun, self.adapter.0)
}
/// Returns the Win32 interface index of this adapter. Useful for specifying the interface
/// when executing `netsh interface ip` commands
pub fn get_adapter_index(&self) -> Result<u32, error::WintunError> {
let mut buf_len: u32 = 0;
//First figure out the size of the buffer needed to store the adapter info
//SAFETY: We are upholding the contract of GetInterfaceInfo. buf_len is a valid pointer to
//stack memory
let result =
unsafe { iphlpapi::GetInterfaceInfo(std::ptr::null_mut(), &mut buf_len as *mut u32) };
if result != winerror::NO_ERROR && result != winerror::ERROR_INSUFFICIENT_BUFFER {
let err_msg = util::get_error_message(result);
error!("Failed to get interface info: {}", err_msg);
//TODO: Better error types
return Err(format!("GetInterfaceInfo failed: {}", err_msg).into());
}
//Allocate a buffer of the requested size
//IP_INTERFACE_INFO must be aligned by at least 4 byte boundaries so use u32 as the
//underlying data storage type
let buf_elements = buf_len as usize / std::mem::size_of::<u32>() + 1;
//Round up incase integer division truncated a byte that filled a partial element
let mut buf: Vec<u32> = vec![0; buf_elements];
let buf_bytes = buf.len() * std::mem::size_of::<u32>();
assert!(buf_bytes >= buf_len as usize);
//SAFETY:
//
// 1. We are upholding the contract of GetInterfaceInfo.
// 2. `final_buf_len` is an aligned, valid pointer to stack memory
// 3. buf is a valid, non-null pointer to at least `buf_len` bytes of heap memory,
// aligned to at least 4 byte boundaries
//
//Get the info
let mut final_buf_len: u32 = buf_len;
let result = unsafe {
iphlpapi::GetInterfaceInfo(
buf.as_mut_ptr() as *mut ipexport::IP_INTERFACE_INFO,
&mut final_buf_len as *mut u32,
)
};
if result != winerror::NO_ERROR {
let err_msg = util::get_error_message(result);
//TODO: maybe over allocate the buffer in case the needed size changes between the two
//calls to GetInterfaceInfo if another adapter is added
error!(
"Failed to get interface info a second time: {}. Original len: {}, final len: {}",
err_msg, buf_len, final_buf_len
);
return Err(format!("GetInterfaceInfo failed a second time: {}", err_msg).into());
}
let info = buf.as_mut_ptr() as *const ipexport::IP_INTERFACE_INFO;
//SAFETY:
// info is a valid, non-null, at least 4 byte aligned pointer obtained from
// Vec::with_capacity that is readable for up to `buf_len` bytes which is guaranteed to be
// larger than on IP_INTERFACE_INFO struct as the kernel would never ask for less memory then
// what it will write. The largest type inside IP_INTERFACE_INFO is a u32 therefore
// a painter to IP_INTERFACE_INFO requires an alignment of at leant 4 bytes, which
// Vec<u32>::as_mut_ptr() provides
let adapter_base = unsafe { &*info };
let adapter_count = adapter_base.NumAdapters;
let first_adapter = &adapter_base.Adapter as *const ipexport::IP_ADAPTER_INDEX_MAP;
// SAFETY:
// 1. first_adapter is a valid, non null pointer, aligned to at least 4 byte boundaries
// obtained from moving a multiple of 4 offset into the buf given by Vec::with_capacity.
// 2. We gave GetInterfaceInfo a buffer of at least least `buf_len` bytes to work with and it
// succeeded in writing the adapter information within the bounds of that buffer, otherwise
// it would've failed. Because the operation succeeded, we know that reading n=NumAdapters
// IP_ADAPTER_INDEX_MAP structs stays within the bounds of buf's buffer
let interfaces =
unsafe { std::slice::from_raw_parts(first_adapter, adapter_count as usize) };
let mut tmp = Vec::new();
for interface in interfaces {
let name =
unsafe { U16CStr::from_ptr_str(&interface.Name as *const u16).to_string_lossy() };
//Nam is something like: \DEVICE\TCPIP_{29C47F55-C7BD-433A-8BF7-408DFD3B3390}
//where the GUID is the {29C4...90}, separated by dashes
let open = name.chars().position(|c| c == '{').ok_or(format!(
"Failed to find {{ character inside adapter name: {}",
name
))?;
let close = name.chars().position(|c| c == '}').ok_or(format!(
"Failed to find }} character inside adapter name: {}",
name
))?;
let digits: Vec<u8> = name[open..close]
.chars()
.filter(|c| c.is_digit(16))
.chunks(2)
.into_iter()
.filter_map(|mut chunk| {
//Filter out chunks that have < 2 digits
if let Some(a) = chunk.next() {
if let Some(b) = chunk.next() {
return Some((a, b));
}
}
None
})
.map(|digits| {
let chars: [u8; 2] = [digits.0 as u8, digits.1 as u8];
let s = std::str::from_utf8(&chars).unwrap();
u8::from_str_radix(s, 16).unwrap()
})
.collect();
//Our index is the adapter which has a guid in its name that matches ours
//For now we just check for a guid with the same hex bytes in any order
//TODO: byte swap GUID from name so that we can compare self.guid with the parsed GUID
//directly
let mut match_count = 0;
for byte in self.guid.to_ne_bytes() {
if digits.contains(&byte) {
match_count += 1;
}
}
tmp.push(format!("interfaces name={:?},digits={:?},index={:?}", name,digits, interface.Index));
if match_count == digits.len() {
return Ok(interface.Index);
}
}
log::info!("interfaces:{:?},guid={}",tmp,self.guid);
Err("Unable to find matching GUID".into())
}
}
impl Drop for Adapter {
fn drop(&mut self) {
//Close adapter on drop
//This is why we need an Arc of wintun
unsafe { self.wintun.WintunCloseAdapter(self.adapter.0) };
self.adapter = UnsafeHandle(ptr::null_mut());
}
}
-36
View File
@@ -1,36 +0,0 @@
use std::fmt::Display;
pub type WintunError = Box<dyn std::error::Error>;
/// Error type used to convey that a value is outside of a range that it must fall inside
#[derive(Debug)]
pub struct OutOfRangeData<T> {
pub range: std::ops::RangeInclusive<T>,
pub value: T,
}
/// Error type returned when preconditions of this API are broken
#[derive(Debug)]
pub enum ApiError {
CapacityNotPowerOfTwo(u32),
CapacityOutOfRange(OutOfRangeData<u32>),
}
impl Display for ApiError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self {
ApiError::CapacityOutOfRange(data) => write!(
f,
"Capacity {} out of range. Must be within {}..={}",
data.value,
data.range.start(),
data.range.end()
),
ApiError::CapacityNotPowerOfTwo(cap) => {
write!(f, "Capacity {} is not a power of two", cap)
}
}
}
}
impl std::error::Error for ApiError {}
-174
View File
@@ -1,174 +0,0 @@
//! Safe rust idiomatic bindings for the Wintun C library: <https://wintun.net>
//!
//! All features of the Wintun library are wrapped using pure rust types and functions to make
//! usage feel ergonomic.
//!
//! # Usage
//!
//! Inside your code load the wintun.dll signed driver file, downloaded from <https://wintun.net>,
//! using [`load`], [`load_from_path`] or [`load_from_library`].
//!
//! Then either call [`Adapter::create`] or [`Adapter::open`] to obtain a wintun
//! adapter. Start a session with [`Adapter::start_session`].
//!
//! # Example
//! ```no_run
//! use std::sync::Arc;
//!
//! //Must be run as Administrator because we create network adapters
//! //Load the wintun dll file so that we can call the underlying C functions
//! //Unsafe because we are loading an arbitrary dll file
//! let wintun = unsafe { wintun::load_from_path("path/to/wintun.dll") }
//! .expect("Failed to load wintun dll");
//!
//! //Try to open an adapter with the name "Demo"
//! let adapter = match wintun::Adapter::open(&wintun, "Demo") {
//! Ok(a) => a,
//! Err(_) => {
//! //If loading failed (most likely it didn't exist), create a new one
//! wintun::Adapter::create(&wintun, "Example", "Demo", None)
//! .expect("Failed to create wintun adapter!")
//! }
//! };
//! //Specify the size of the ring buffer the wintun driver should use.
//! let session = Arc::new(adapter.start_session(wintun::MAX_RING_CAPACITY).unwrap());
//!
//! //Get a 20 byte packet from the ring buffer
//! let mut packet = session.allocate_send_packet(20).unwrap();
//! let bytes: &mut [u8] = packet.bytes_mut();
//! //Write IPV4 version and header length
//! bytes[0] = 0x40;
//!
//! //Finish writing IP header
//! bytes[9] = 0x69;
//! bytes[10] = 0x04;
//! bytes[11] = 0x20;
//! //...
//!
//! //Send the packet to wintun virtual adapter for processing by the system
//! session.send_packet(packet);
//!
//! //Stop any readers blocking for data on other threads
//! //Only needed when a blocking reader is preventing shutdown Ie. it holds an Arc to the
//! //session, blocking it from being dropped
//! session.shutdown();
//!
//! //the session is stopped on drop
//! //drop(session);
//!
//! //drop(adapter)
//! //And the adapter closes its resources when dropped
//! ```
//!
//! See `examples/wireshark.rs` for a more complete example that writes received packets to a pcap
//! file.
//!
//! # Features
//!
//! - `panic_on_unsent_packets`: Panics if a send packet is dropped without being sent. Useful for
//! debugging packet issues because unsent packets that are dropped without being sent hold up
//! wintun's internal ring buffer.
//!
//! # TODO:
//! - Add async support
//! Requires hooking into a windows specific reactor and registering read interest on wintun's read
//! handle. Asyncify other slow operations via tokio::spawn_blocking. As always, PR's are welcome!
//!
mod adapter;
mod error;
mod log;
mod packet;
mod session;
mod util;
//Generated by bingen
#[allow(
non_snake_case,
dead_code,
unused_variables,
non_camel_case_types,
deref_nullptr,
clippy::all
)]
mod wintun_raw;
pub use crate::adapter::Adapter;
pub use crate::error::{ApiError, OutOfRangeData, WintunError};
pub use crate::log::{default_logger, reset_logger, set_logger};
pub use crate::packet::Packet;
pub use crate::session::Session;
pub use crate::util::get_running_driver_version;
// TODO: Get bindgen to scrape these from the `wintun.h`
// We need to make sure these stay up to date
/// The maximum size of wintun's internal ring buffer (in bytes)
pub const MAX_RING_CAPACITY: u32 = 0x400_0000;
/// The minimum size of wintun's internal ring buffer (in bytes)
pub const MIN_RING_CAPACITY: u32 = 0x2_0000;
/// Maximum pool name length including zero terminator
pub const MAX_POOL: usize = 256;
pub type Wintun = Arc<wintun_raw::wintun>;
use std::sync::Arc;
/// Attempts to load the Wintun library from the current directory using the default name "wintun.dll".
///
/// Use [`load_from_path`] with an absolute path when more control is needed as to where wintun.dll is
///
///
/// # Safety
/// This function loads a dll file with the name wintun.dll using the default system search paths.
/// This is inherently unsafe as a user could simply rename undefined_behavior.dll to wintun.dll
/// and do nefarious things inside of its DllMain function. In most cases, a regular wintun.dll
/// file which exports all of the required functions for these bindings to work is loaded. Because
/// WinTun is a well-written and well-tested library, loading a _normal_ wintun.dll file should be safe.
/// Hoverer one can never be too cautious when loading a dll file.
///
/// For more information see [`libloading`]'s dynamic library safety guarantees: [`libloading`][`libloading::Library::new`]
pub unsafe fn load() -> Result<Wintun, libloading::Error> {
load_from_path("wintun")
}
/// Attempts to load the Wintun library as a dynamic library from the given path.
///
///
/// # Safety
/// This function loads a dll file with the path provided.
/// This is inherently unsafe as a user could simply rename undefined_behavior.dll to wintun.dll
/// and do nefarious things inside of its DllMain function. In most cases, a regular wintun.dll
/// file which exports all of the required functions for these bindings to work is loaded. Because
/// WinTun is a well-written and well-tested library, loading a _normal_ wintun.dll file should be safe.
/// Hoverer one can never be too cautious when loading a dll file.
///
/// For more information see [`libloading`]'s dynamic library safety guarantees: [`libloading`][`libloading::Library::new`]
pub unsafe fn load_from_path<P>(path: P) -> Result<Wintun, libloading::Error>
where
P: AsRef<::std::ffi::OsStr>,
{
check_version(wintun_raw::wintun::new(path)?)
}
/// Attempts to load the Wintun library from an existing [`libloading::Library`].
///
///
/// # Safety
/// This function loads the required WinTun functions using the provided library. Reading a symbol table
/// of a dynamic library and transmuting the function pointers inside to have the parameters and return
/// values expected by the functions documented at: <https://git.zx2c4.com/wintun/about/#reference>
/// is inherently unsafe.
///
/// For more information see [`libloading`]'s dynamic library safety guarantees: [`libloading::Library::new`]
pub unsafe fn load_from_library<L>(library: L) -> Result<Wintun, libloading::Error>
where
L: Into<libloading::Library>,
{
check_version(wintun_raw::wintun::from_library(library)?)
}
fn check_version(lib: wintun_raw::wintun) -> Result<Wintun, libloading::Error> {
Ok(Arc::new(lib))
}
-88
View File
@@ -1,88 +0,0 @@
use crate::session;
use std::sync::Arc;
pub(crate) enum Kind {
SendPacketPending, //Send packet type, but not sent yet
SendPacketSent, //Send packet type - sent
ReceivePacket,
}
/// Represents a wintun packet
pub struct Packet {
pub(crate) kind: Kind,
//This lifetime is not actually 'static, however before you get your pitchforks let me explain...
//The bytes in this slice live for as long at the session that allocated them, or until
//WintunReleaseReceivePacket, or WintunSendPacket is called on them (whichever happens first).
//The wrapper functions that call into WintunReleaseReceivePacket, and WintunSendPacket
//consume the packet, meaning the end of this packet's lifetime coincides with the end of byte's
//lifetime. Because we never copy out of bytes, this pointer becomes inaccessible when the
//packet is dropped.
//
//This just leaves packets potentially outliving the session that allocated them posing a
//problem.
//Fortunately we have an Arc to the session that allocated this packet, meaning that the lifetime
//of the session that created this packet is at least as long as the packet.
//Because this is private (to external users) and we only write to this field when allocating
//new packets, it is impossible for the memory that is pointed to by bytes to outlive the
//underlying memory allocated by wintun.
//
//So what I told you was true, from a certain point of view.
//From the point of view of this packet, bytes' lifetime is 'static because we are always
//dropped before the underlying memory is freed
//
//Its also important to know that WintunAllocateSendPacket and WintunReceivePacket always
//return sections of memory that never overlap, so we have exclusive access to the memory,
//therefore mut is okay here.
pub(crate) bytes: &'static mut [u8],
//Share ownership of session to prevent the session from being dropped before packets that
//belong to it
pub(crate) session: Arc<session::Session>,
}
impl Packet {
/// Returns the bytes this packet holds as &mut.
/// The lifetime of the bytes is tied to the lifetime of this packet.
pub fn bytes_mut(&mut self) -> &mut [u8] {
self.bytes
}
/// Returns an immutable reference to the bytes this packet holds.
/// The lifetime of the bytes is tied to the lifetime of this packet.
pub fn bytes(&self) -> &[u8] {
self.bytes
}
}
impl Drop for Packet {
fn drop(&mut self) {
match self.kind {
Kind::ReceivePacket => {
unsafe {
//SAFETY:
//
// 1. We share ownership of the session therefore it hasn't been dropped yet
// 2. Bytes is valid because each packet holds exclusive access to a region of the
// ring buffer that the wintun session owns. We return that region of
// memory back to wintun here
self.session
.wintun
.WintunReleaseReceivePacket(self.session.session.0, self.bytes.as_ptr())
};
}
Kind::SendPacketPending => {
//If someone allocates a packet with session.allocate_send_packet() and then it is
//dropped without being sent, this will hold up the send queue because wintun expects
//that every allocated packet is sent
#[cfg(feature = "panic_on_unsent_packets")]
panic!("Packet was never sent!");
}
Kind::SendPacketSent => {
//Nop
}
}
}
}
-182
View File
@@ -1,182 +0,0 @@
extern crate winapi;
use crate::packet;
use crate::util::UnsafeHandle;
use crate::wintun_raw;
use crate::Adapter;
use crate::Wintun;
use once_cell::sync::OnceCell;
use winapi::shared::winerror;
use winapi::um::errhandlingapi::GetLastError;
use winapi::um::handleapi;
use winapi::um::synchapi;
use winapi::um::winbase;
use winapi::um::winnt;
use std::sync::Arc;
use std::{ptr, slice};
/// Wrapper around a <https://git.zx2c4.com/wintun/about/#wintun_session_handle>
pub struct Session {
/// The session handle given to us by WintunStartSession
pub(crate) session: UnsafeHandle<wintun_raw::WINTUN_SESSION_HANDLE>,
/// Shared dll for required wintun driver functions
pub(crate) wintun: Wintun,
/// Windows event handle that is signaled by the wintun driver when data becomes available to
/// read
pub(crate) read_event: OnceCell<UnsafeHandle<winnt::HANDLE>>,
/// Windows event handle that is signaled when [`Session::shutdown`] is called force blocking
/// readers to exit
pub(crate) shutdown_event: UnsafeHandle<winnt::HANDLE>,
/// The adapter that owns this session
pub(crate) adapter: Arc<Adapter>,
}
impl Session {
/// Allocates a send packet of the specified size. Wraps WintunAllocateSendPacket
///
/// All packets returned from this function must be sent using [`Session::send_packet`] because
/// wintun establishes the send packet order based on the invocation order of this function.
/// Therefore if a packet is allocated using this function, and then never sent, it will hold
/// up the send queue for all other packets allocated in the future. It is okay for the session
/// to shutdown with allocated packets that have not yet been sent
pub fn allocate_send_packet(self: &Arc<Self>, size: u16) -> Result<packet::Packet, ()> {
let ptr = unsafe {
self.wintun
.WintunAllocateSendPacket(self.session.0, size as u32)
};
if ptr.is_null() {
Err(())
} else {
Ok(packet::Packet {
//SAFETY: ptr is non null, aligned for u8, and readable for up to size bytes (which
//must be less than isize::MAX because bytes is a u16
bytes: unsafe { slice::from_raw_parts_mut(ptr, size as usize) },
session: self.clone(),
kind: packet::Kind::SendPacketPending,
})
}
}
/// Sends a packet previously allocated with [`Session::allocate_send_packet`]
pub fn send_packet(&self, mut packet: packet::Packet) {
assert!(matches!(packet.kind, packet::Kind::SendPacketPending));
unsafe {
self.wintun
.WintunSendPacket(self.session.0, packet.bytes.as_ptr())
};
//Mark the packet at sent
packet.kind = packet::Kind::SendPacketSent;
}
/// Attempts to receive a packet from the virtual interface without blocking.
/// If there are no packets currently in the receive queue, this function returns Ok(None)
/// without blocking. If blocking until a packet is desirable, use [`Session::receive_blocking`]
pub fn try_receive(self: &Arc<Self>) -> Result<Option<packet::Packet>, ()> {
let mut size = 0u32;
let ptr = unsafe {
self.wintun
.WintunReceivePacket(self.session.0, &mut size as *mut u32)
};
debug_assert!(size <= u16::MAX as u32);
if ptr.is_null() {
//Wintun returns ERROR_NO_MORE_ITEMS instead of blocking if packets are not available
let last_error = unsafe { GetLastError() };
if last_error == winerror::ERROR_NO_MORE_ITEMS {
Ok(None)
} else {
Err(())
}
} else {
Ok(Some(packet::Packet {
kind: packet::Kind::ReceivePacket,
//SAFETY: ptr is non null, aligned for u8, and readable for up to size bytes (which
//must be less than isize::MAX because bytes is a u16
bytes: unsafe { slice::from_raw_parts_mut(ptr, size as usize) },
session: self.clone(),
}))
}
}
/// Returns the low level read event handle that is signaled when more data becomes available
/// to read
pub(crate) fn get_read_wait_event(&self) -> Result<winnt::HANDLE, ()> {
Ok(self
.read_event
.get_or_init(|| unsafe {
UnsafeHandle(self.wintun.WintunGetReadWaitEvent(self.session.0) as winnt::HANDLE)
})
.0)
}
/// Blocks until a packet is available, returning the next packet in the receive queue once this happens.
/// If the session is closed via [`Session::shutdown`] all threads currently blocking inside this function
/// will return Err(())
pub fn receive_blocking(self: &Arc<Self>) -> Result<packet::Packet, ()> {
loop {
//Try 5 times to receive without blocking so we don't have to issue a syscall to wait
//for the event if packets are being received at a rapid rate
for _ in 0..5 {
match self.try_receive() {
Err(err) => return Err(err),
Ok(Some(packet)) => return Ok(packet),
Ok(None) => {
//Try again
continue;
}
}
}
//Wait on both the read handle and the shutdown handle so that we stop when requested
let handles = [self.get_read_wait_event()?, self.shutdown_event.0];
let result = unsafe {
//SAFETY: We abide by the requirements of WaitForMultipleObjects, handles is a
//pointer to valid, aligned, stack memory
synchapi::WaitForMultipleObjects(
2,
&handles as *const winnt::HANDLE,
0,
winbase::INFINITE,
)
};
match result {
winbase::WAIT_FAILED => return Err(()),
_ => {
if result == winbase::WAIT_OBJECT_0 {
//We have data!
continue;
} else if result == winbase::WAIT_OBJECT_0 + 1 {
//Shutdown event triggered
return Err(());
}
}
}
}
}
/// Cancels any active calls to [`Session::receive_blocking`] making them instantly return Err(_) so that session can be shutdown cleanly
pub fn shutdown(&self) {
let _ = unsafe { synchapi::SetEvent(self.shutdown_event.0) };
let _ = unsafe { handleapi::CloseHandle(self.shutdown_event.0) };
}
}
impl Drop for Session {
fn drop(&mut self) {
let _ = Arc::clone(&self.adapter);
unsafe { self.wintun.WintunEndSession(self.session.0) };
self.session.0 = ptr::null_mut();
//Adapter must be dropped after we call `WintunEndSession`,
//if `self.adapter is the last reference
//drop(self.adapter)
}
}
-66
View File
@@ -1,66 +0,0 @@
use winapi::{
shared::ntdef::{LANG_NEUTRAL, SUBLANG_DEFAULT},
um::{winbase, winnt::MAKELANGID},
};
use std::mem::MaybeUninit;
use std::ptr;
use widestring::U16Str;
/// A wrapper struct that allows a type to be Send and Sync
pub(crate) struct UnsafeHandle<T>(pub T);
/// We never read from the pointer. It only serves as a handle we pass to the kernel or C code that
/// doesn't have the same mutable aliasing restrictions we have in Rust
unsafe impl<T> Send for UnsafeHandle<T> {}
unsafe impl<T> Sync for UnsafeHandle<T> {}
/// Returns a a human readable error message from a windows error code
pub fn get_error_message(err_code: u32) -> String {
const LEN: usize = 256;
let mut buf = MaybeUninit::<[u16; LEN]>::uninit();
//SAFETY: name is a allocated on the stack above therefore it must be valid, non-null and
//aligned for u16
let first = unsafe { *buf.as_mut_ptr() }.as_mut_ptr();
//Write default null terminator in case WintunGetAdapterName leaves name unchanged
unsafe { first.write(0u16) };
let chars_written = unsafe {
winbase::FormatMessageW(
winbase::FORMAT_MESSAGE_FROM_SYSTEM | winbase::FORMAT_MESSAGE_IGNORE_INSERTS,
ptr::null(),
err_code,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT) as u32,
first,
LEN as u32,
ptr::null_mut(),
)
};
//SAFETY: first is a valid, non-null, aligned, pointer
format!(
"{} ({})",
unsafe { U16Str::from_ptr(first, chars_written as usize) }.to_string_lossy(),
err_code
)
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct Version {
pub major: u16,
pub minor: u16,
}
/// Returns the major and minor version of the wintun driver
pub fn get_running_driver_version(wintun: &crate::Wintun) -> Result<Version, ()> {
let version = unsafe { wintun.WintunGetRunningDriverVersion() };
if version == 0 {
Err(())
} else {
Ok(Version {
major: ((version >> 16) & 0xFF) as u16,
minor: (version & 0xFF) as u16,
})
}
}
-84
View File
@@ -1,84 +0,0 @@
Prebuilt Binaries License
-------------------------
1. DEFINITIONS. "Software" means the precise contents of the "wintun.dll"
files that are included in the .zip file that contains this document as
downloaded from wintun.net/builds.
2. LICENSE GRANT. WireGuard LLC grants to you a non-exclusive and
non-transferable right to use Software for lawful purposes under certain
obligations and limited rights as set forth in this agreement.
3. RESTRICTIONS. Software is owned and copyrighted by WireGuard LLC. It is
licensed, not sold. Title to Software and all associated intellectual
property rights are retained by WireGuard. You must not:
a. reverse engineer, decompile, disassemble, extract from, or otherwise
modify the Software;
b. modify or create derivative work based upon Software in whole or in
parts, except insofar as only the API interfaces of the "wintun.h" file
distributed alongside the Software (the "Permitted API") are used;
c. remove any proprietary notices, labels, or copyrights from the Software;
d. resell, redistribute, lease, rent, transfer, sublicense, or otherwise
transfer rights of the Software without the prior written consent of
WireGuard LLC, except insofar as the Software is distributed alongside
other software that uses the Software only via the Permitted API;
e. use the name of WireGuard LLC, the WireGuard project, the Wintun
project, or the names of its contributors to endorse or promote products
derived from the Software without specific prior written consent.
4. LIMITED WARRANTY. THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTY OF
ANY KIND. WIREGUARD LLC HEREBY EXCLUDES AND DISCLAIMS ALL IMPLIED OR
STATUTORY WARRANTIES, INCLUDING ANY WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE, QUALITY, NON-INFRINGEMENT, TITLE, RESULTS,
EFFORTS, OR QUIET ENJOYMENT. THERE IS NO WARRANTY THAT THE PRODUCT WILL BE
ERROR-FREE OR WILL FUNCTION WITHOUT INTERRUPTION. YOU ASSUME THE ENTIRE
RISK FOR THE RESULTS OBTAINED USING THE PRODUCT. TO THE EXTENT THAT
WIREGUARD LLC MAY NOT DISCLAIM ANY WARRANTY AS A MATTER OF APPLICABLE LAW,
THE SCOPE AND DURATION OF SUCH WARRANTY WILL BE THE MINIMUM PERMITTED UNDER
SUCH LAW. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR
A PARTICULAR PURPOSE OR NON-INFRINGEMENT ARE DISCLAIMED, EXCEPT TO THE
EXTENT THAT THESE DISCLAIMERS ARE HELD TO BE LEGALLY INVALID.
5. LIMITATION OF LIABILITY. To the extent not prohibited by law, in no event
WireGuard LLC or any third-party-developer will be liable for any lost
revenue, profit or data or for special, indirect, consequential, incidental
or punitive damages, however caused regardless of the theory of liability,
arising out of or related to the use of or inability to use Software, even
if WireGuard LLC has been advised of the possibility of such damages.
Solely you are responsible for determining the appropriateness of using
Software and accept full responsibility for all risks associated with its
exercise of rights under this agreement, including but not limited to the
risks and costs of program errors, compliance with applicable laws, damage
to or loss of data, programs or equipment, and unavailability or
interruption of operations. The foregoing limitations will apply even if
the above stated warranty fails of its essential purpose. You acknowledge,
that it is in the nature of software that software is complex and not
completely free of errors. In no event shall WireGuard LLC or any
third-party-developer be liable to you under any theory for any damages
suffered by you or any user of Software or for any special, incidental,
indirect, consequential or similar damages (including without limitation
damages for loss of business profits, business interruption, loss of
business information or any other pecuniary loss) arising out of the use or
inability to use Software, even if WireGuard LLC has been advised of the
possibility of such damages and regardless of the legal or quitable theory
(contract, tort, or otherwise) upon which the claim is based.
6. TERMINATION. This agreement is affected until terminated. You may
terminate this agreement at any time. This agreement will terminate
immediately without notice from WireGuard LLC if you fail to comply with
the terms and conditions of this agreement. Upon termination, you must
delete Software and all copies of Software and cease all forms of
distribution of Software.
7. SEVERABILITY. If any provision of this agreement is held to be
unenforceable, this agreement will remain in effect with the provision
omitted, unless omission would frustrate the intent of the parties, in
which case this agreement will immediately terminate.
8. RESERVATION OF RIGHTS. All rights not expressly granted in this agreement
are reserved by WireGuard LLC. For example, WireGuard LLC reserves the
right at any time to cease development of Software, to alter distribution
details, features, specifications, capabilities, functions, licensing
terms, release dates, APIs, ABIs, general availability, or other
characteristics of the Software.
-270
View File
@@ -1,270 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 OR MIT
*
* Copyright (C) 2018-2021 WireGuard LLC. All Rights Reserved.
*/
#pragma once
#include <winsock2.h>
#include <windows.h>
#include <ipexport.h>
#include <ifdef.h>
#include <ws2ipdef.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifndef ALIGNED
# if defined(_MSC_VER)
# define ALIGNED(n) __declspec(align(n))
# elif defined(__GNUC__)
# define ALIGNED(n) __attribute__((aligned(n)))
# else
# error "Unable to define ALIGNED"
# endif
#endif
/* MinGW is missing this one, unfortunately. */
#ifndef _Post_maybenull_
# define _Post_maybenull_
#endif
#pragma warning(push)
#pragma warning(disable : 4324) /* structure was padded due to alignment specifier */
/**
* A handle representing Wintun adapter
*/
typedef struct _WINTUN_ADAPTER *WINTUN_ADAPTER_HANDLE;
/**
* Creates a new Wintun adapter.
*
* @param Name The requested name of the adapter. Zero-terminated string of up to MAX_ADAPTER_NAME-1
* characters.
*
* @param TunnelType Name of the adapter tunnel type. Zero-terminated string of up to MAX_ADAPTER_NAME-1
* characters.
*
* @param RequestedGUID The GUID of the created network adapter, which then influences NLA generation deterministically.
* If it is set to NULL, the GUID is chosen by the system at random, and hence a new NLA entry is
* created for each new adapter. It is called "requested" GUID because the API it uses is
* completely undocumented, and so there could be minor interesting complications with its usage.
*
* @return If the function succeeds, the return value is the adapter handle. Must be released with
* WintunCloseAdapter. If the function fails, the return value is NULL. To get extended error information, call
* GetLastError.
*/
typedef _Must_inspect_result_
_Return_type_success_(return != NULL)
_Post_maybenull_
WINTUN_ADAPTER_HANDLE(WINAPI WINTUN_CREATE_ADAPTER_FUNC)
(_In_z_ LPCWSTR Name, _In_z_ LPCWSTR TunnelType, _In_opt_ const GUID *RequestedGUID);
/**
* Opens an existing Wintun adapter.
*
* @param Name The requested name of the adapter. Zero-terminated string of up to MAX_ADAPTER_NAME-1
* characters.
*
* @return If the function succeeds, the return value is the adapter handle. Must be released with
* WintunCloseAdapter. If the function fails, the return value is NULL. To get extended error information, call
* GetLastError.
*/
typedef _Must_inspect_result_
_Return_type_success_(return != NULL)
_Post_maybenull_
WINTUN_ADAPTER_HANDLE(WINAPI WINTUN_OPEN_ADAPTER_FUNC)(_In_z_ LPCWSTR Name);
/**
* Releases Wintun adapter resources and, if adapter was created with WintunCreateAdapter, removes adapter.
*
* @param Adapter Adapter handle obtained with WintunCreateAdapter or WintunOpenAdapter.
*/
typedef VOID(WINAPI WINTUN_CLOSE_ADAPTER_FUNC)(_In_opt_ WINTUN_ADAPTER_HANDLE Adapter);
/**
* Deletes the Wintun driver if there are no more adapters in use.
*
* @return If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To
* get extended error information, call GetLastError.
*/
typedef _Return_type_success_(return != FALSE)
BOOL(WINAPI WINTUN_DELETE_DRIVER_FUNC)(VOID);
/**
* Returns the LUID of the adapter.
*
* @param Adapter Adapter handle obtained with WintunCreateAdapter or WintunOpenAdapter
*
* @param Luid Pointer to LUID to receive adapter LUID.
*/
typedef VOID(WINAPI WINTUN_GET_ADAPTER_LUID_FUNC)(_In_ WINTUN_ADAPTER_HANDLE Adapter, _Out_ NET_LUID *Luid);
/**
* Determines the version of the Wintun driver currently loaded.
*
* @return If the function succeeds, the return value is the version number. If the function fails, the return value is
* zero. To get extended error information, call GetLastError. Possible errors include the following:
* ERROR_FILE_NOT_FOUND Wintun not loaded
*/
typedef _Return_type_success_(return != 0)
DWORD(WINAPI WINTUN_GET_RUNNING_DRIVER_VERSION_FUNC)(VOID);
/**
* Determines the level of logging, passed to WINTUN_LOGGER_CALLBACK.
*/
typedef enum
{
WINTUN_LOG_INFO, /**< Informational */
WINTUN_LOG_WARN, /**< Warning */
WINTUN_LOG_ERR /**< Error */
} WINTUN_LOGGER_LEVEL;
/**
* Called by internal logger to report diagnostic messages
*
* @param Level Message level.
*
* @param Timestamp Message timestamp in in 100ns intervals since 1601-01-01 UTC.
*
* @param Message Message text.
*/
typedef VOID(CALLBACK *WINTUN_LOGGER_CALLBACK)(
_In_ WINTUN_LOGGER_LEVEL Level,
_In_ DWORD64 Timestamp,
_In_z_ LPCWSTR Message);
/**
* Sets logger callback function.
*
* @param NewLogger Pointer to callback function to use as a new global logger. NewLogger may be called from various
* threads concurrently. Should the logging require serialization, you must handle serialization in
* NewLogger. Set to NULL to disable.
*/
typedef VOID(WINAPI WINTUN_SET_LOGGER_FUNC)(_In_ WINTUN_LOGGER_CALLBACK NewLogger);
/**
* Minimum ring capacity.
*/
#define WINTUN_MIN_RING_CAPACITY 0x20000 /* 128kiB */
/**
* Maximum ring capacity.
*/
#define WINTUN_MAX_RING_CAPACITY 0x4000000 /* 64MiB */
/**
* A handle representing Wintun session
*/
typedef struct _TUN_SESSION *WINTUN_SESSION_HANDLE;
/**
* Starts Wintun session.
*
* @param Adapter Adapter handle obtained with WintunOpenAdapter or WintunCreateAdapter
*
* @param Capacity Rings capacity. Must be between WINTUN_MIN_RING_CAPACITY and WINTUN_MAX_RING_CAPACITY (incl.)
* Must be a power of two.
*
* @return Wintun session handle. Must be released with WintunEndSession. If the function fails, the return value is
* NULL. To get extended error information, call GetLastError.
*/
typedef _Must_inspect_result_
_Return_type_success_(return != NULL)
_Post_maybenull_
WINTUN_SESSION_HANDLE(WINAPI WINTUN_START_SESSION_FUNC)(_In_ WINTUN_ADAPTER_HANDLE Adapter, _In_ DWORD Capacity);
/**
* Ends Wintun session.
*
* @param Session Wintun session handle obtained with WintunStartSession
*/
typedef VOID(WINAPI WINTUN_END_SESSION_FUNC)(_In_ WINTUN_SESSION_HANDLE Session);
/**
* Gets Wintun session's read-wait event handle.
*
* @param Session Wintun session handle obtained with WintunStartSession
*
* @return Pointer to receive event handle to wait for available data when reading. Should
* WintunReceivePackets return ERROR_NO_MORE_ITEMS (after spinning on it for a while under heavy
* load), wait for this event to become signaled before retrying WintunReceivePackets. Do not call
* CloseHandle on this event - it is managed by the session.
*/
typedef HANDLE(WINAPI WINTUN_GET_READ_WAIT_EVENT_FUNC)(_In_ WINTUN_SESSION_HANDLE Session);
/**
* Maximum IP packet size
*/
#define WINTUN_MAX_IP_PACKET_SIZE 0xFFFF
/**
* Retrieves one or packet. After the packet content is consumed, call WintunReleaseReceivePacket with Packet returned
* from this function to release internal buffer. This function is thread-safe.
*
* @param Session Wintun session handle obtained with WintunStartSession
*
* @param PacketSize Pointer to receive packet size.
*
* @return Pointer to layer 3 IPv4 or IPv6 packet. Client may modify its content at will. If the function fails, the
* return value is NULL. To get extended error information, call GetLastError. Possible errors include the
* following:
* ERROR_HANDLE_EOF Wintun adapter is terminating;
* ERROR_NO_MORE_ITEMS Wintun buffer is exhausted;
* ERROR_INVALID_DATA Wintun buffer is corrupt
*/
typedef _Must_inspect_result_
_Return_type_success_(return != NULL)
_Post_maybenull_
_Post_writable_byte_size_(*PacketSize)
BYTE *(WINAPI WINTUN_RECEIVE_PACKET_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _Out_ DWORD *PacketSize);
/**
* Releases internal buffer after the received packet has been processed by the client. This function is thread-safe.
*
* @param Session Wintun session handle obtained with WintunStartSession
*
* @param Packet Packet obtained with WintunReceivePacket
*/
typedef VOID(
WINAPI WINTUN_RELEASE_RECEIVE_PACKET_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _In_ const BYTE *Packet);
/**
* Allocates memory for a packet to send. After the memory is filled with packet data, call WintunSendPacket to send
* and release internal buffer. WintunAllocateSendPacket is thread-safe and the WintunAllocateSendPacket order of
* calls define the packet sending order.
*
* @param Session Wintun session handle obtained with WintunStartSession
*
* @param PacketSize Exact packet size. Must be less or equal to WINTUN_MAX_IP_PACKET_SIZE.
*
* @return Returns pointer to memory where to prepare layer 3 IPv4 or IPv6 packet for sending. If the function fails,
* the return value is NULL. To get extended error information, call GetLastError. Possible errors include the
* following:
* ERROR_HANDLE_EOF Wintun adapter is terminating;
* ERROR_BUFFER_OVERFLOW Wintun buffer is full;
*/
typedef _Must_inspect_result_
_Return_type_success_(return != NULL)
_Post_maybenull_
_Post_writable_byte_size_(PacketSize)
BYTE *(WINAPI WINTUN_ALLOCATE_SEND_PACKET_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _In_ DWORD PacketSize);
/**
* Sends the packet and releases internal buffer. WintunSendPacket is thread-safe, but the WintunAllocateSendPacket
* order of calls define the packet sending order. This means the packet is not guaranteed to be sent in the
* WintunSendPacket yet.
*
* @param Session Wintun session handle obtained with WintunStartSession
*
* @param Packet Packet obtained with WintunAllocateSendPacket
*/
typedef VOID(WINAPI WINTUN_SEND_PACKET_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _In_ const BYTE *Packet);
#pragma warning(pop)
#ifdef __cplusplus
}
#endif
-19
View File
@@ -1,19 +0,0 @@
// Information about functions taken from:
// https://git.zx2c4.com/wintun/tree/example/example.c
#include "wintun.h"
WINTUN_CREATE_ADAPTER_FUNC WintunCreateAdapter;
WINTUN_CLOSE_ADAPTER_FUNC WintunCloseAdapter;
WINTUN_OPEN_ADAPTER_FUNC WintunOpenAdapter;
WINTUN_GET_ADAPTER_LUID_FUNC WintunGetAdapterLUID;
WINTUN_GET_RUNNING_DRIVER_VERSION_FUNC WintunGetRunningDriverVersion;
WINTUN_DELETE_DRIVER_FUNC WintunDeleteDriver;
WINTUN_SET_LOGGER_FUNC WintunSetLogger;
WINTUN_START_SESSION_FUNC WintunStartSession;
WINTUN_END_SESSION_FUNC WintunEndSession;
WINTUN_GET_READ_WAIT_EVENT_FUNC WintunGetReadWaitEvent;
WINTUN_RECEIVE_PACKET_FUNC WintunReceivePacket;
WINTUN_RELEASE_RECEIVE_PACKET_FUNC WintunReleaseReceivePacket;
WINTUN_ALLOCATE_SEND_PACKET_FUNC WintunAllocateSendPacket;
WINTUN_SEND_PACKET_FUNC WintunSendPacket;