From a8bfe8a54c6a36217ae5716d5aa804ceb884082c Mon Sep 17 00:00:00 2001 From: lubeilin <1791778603@qq.com> Date: Tue, 3 Jan 2023 22:36:18 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9B=E5=BB=BA=E9=A1=B9=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.toml | 58 ++ build.rs | 14 + packet/Cargo.toml | 10 + packet/src/error.rs | 25 + packet/src/icmp/icmp.rs | 174 ++++++ packet/src/icmp/mod.rs | 414 +++++++++++++ packet/src/ip/ipv4/mod.rs | 2 + packet/src/ip/ipv4/packet.rs | 250 ++++++++ packet/src/ip/ipv4/protocol.rs | 742 +++++++++++++++++++++++ packet/src/ip/mod.rs | 18 + packet/src/lib.rs | 146 +++++ packet/src/tcp/mod.rs | 42 ++ packet/src/tcp/tcp.rs | 175 ++++++ packet/src/udp/mod.rs | 1 + packet/src/udp/udp.rs | 177 ++++++ proto/message.proto | 40 ++ src/admin_check.rs | 76 +++ src/error/mod.rs | 24 + src/handle/heartbeat_handler.rs | 45 ++ src/handle/mod.rs | 132 ++++ src/handle/punch_handler.rs | 354 +++++++++++ src/handle/registration_handler.rs | 113 ++++ src/handle/tun_handler.rs | 127 ++++ src/handle/udp_recv_handler.rs | 299 +++++++++ src/main.rs | 185 ++++++ src/nat/check.rs | 156 +++++ src/nat/mod.rs | 1 + src/proto/message.rs | 941 +++++++++++++++++++++++++++++ src/proto/mod.rs | 3 + src/protocol/control_packet.rs | 205 +++++++ src/protocol/error_packet.rs | 69 +++ src/protocol/mod.rs | 145 +++++ src/protocol/service_packet.rs | 32 + src/protocol/turn_packet.rs | 81 +++ src/tun_device/mod.rs | 14 + src/tun_device/unix.rs | 155 +++++ src/tun_device/windows.rs | 131 ++++ 37 files changed, 5576 insertions(+) create mode 100644 Cargo.toml create mode 100644 build.rs create mode 100644 packet/Cargo.toml create mode 100644 packet/src/error.rs create mode 100644 packet/src/icmp/icmp.rs create mode 100644 packet/src/icmp/mod.rs create mode 100644 packet/src/ip/ipv4/mod.rs create mode 100644 packet/src/ip/ipv4/packet.rs create mode 100644 packet/src/ip/ipv4/protocol.rs create mode 100644 packet/src/ip/mod.rs create mode 100644 packet/src/lib.rs create mode 100644 packet/src/tcp/mod.rs create mode 100644 packet/src/tcp/tcp.rs create mode 100644 packet/src/udp/mod.rs create mode 100644 packet/src/udp/udp.rs create mode 100644 proto/message.proto create mode 100644 src/admin_check.rs create mode 100644 src/error/mod.rs create mode 100644 src/handle/heartbeat_handler.rs create mode 100644 src/handle/mod.rs create mode 100644 src/handle/punch_handler.rs create mode 100644 src/handle/registration_handler.rs create mode 100644 src/handle/tun_handler.rs create mode 100644 src/handle/udp_recv_handler.rs create mode 100644 src/main.rs create mode 100644 src/nat/check.rs create mode 100644 src/nat/mod.rs create mode 100644 src/proto/message.rs create mode 100644 src/proto/mod.rs create mode 100644 src/protocol/control_packet.rs create mode 100644 src/protocol/error_packet.rs create mode 100644 src/protocol/mod.rs create mode 100644 src/protocol/service_packet.rs create mode 100644 src/protocol/turn_packet.rs create mode 100644 src/tun_device/mod.rs create mode 100644 src/tun_device/unix.rs create mode 100644 src/tun_device/windows.rs diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..78a54ce --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,58 @@ +[package] +name = "virtual_network" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +packet = { path = "./packet" } +bytes = "1.3.0" + +libc = "0.2.137" + +dashmap = "5.4.0" +crossbeam = "0.8.2" +parking_lot = "0.12.1" + +rsa = "0.7.2" +rand = "0.8.5" +sha2 = { version = "0.10.6", features = ["oid"] } +colored = "2.0.0" + +thiserror = "1.0.37" +chrono = "0.4.23" +lazy_static = "1.4.0" +moka = "0.9.6" +protobuf = "3.2.0" + +console = "0.15.2" +mac_address = "1.1.4" +clap = { version = "4.0.32", features = ["derive"] } +#clap_derive = "4.0.21" +[target.'cfg(any(unix))'.dependencies] +tun = { version = "0.5" } +sudo = "0.6.0" + +[target.'cfg(target_os = "windows")'.dependencies] +winapi = { version = "0.3.9", features = ["handleapi", "processthreadsapi", "winnt", "securitybaseapi", "impl-default"] } +wintun = "0.2.1" +libloading = "0.7.4" +runas = "0.2.1" + +#[dependencies.windows] +# +#libloading = "0.7.4" +# +#version = "0.37.0" +#features = [ +# "alloc", +# "Win32_Foundation", +# "Win32_NetworkManagement_IpHelper", +# "Win32_Networking_WinSock", +# "Win32_UI_WindowsAndMessaging", +# "Win32_System_IO" +#] +[build-dependencies] +protobuf-codegen = "3.2.0" +protoc-bin-vendored = "3.0.0" \ No newline at end of file diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..8e9f1ab --- /dev/null +++ b/build.rs @@ -0,0 +1,14 @@ +fn main() { + std::fs::create_dir_all("src/proto").unwrap(); + protobuf_codegen::Codegen::new() + .pure() + .out_dir("src/proto") + .inputs(&["proto/message.proto"]) + .include("proto") + // .customize( + // protobuf_codegen::Customize::default() + // .tokio_bytes(true) + // ) + .run() + .expect("Codegen failed."); +} diff --git a/packet/Cargo.toml b/packet/Cargo.toml new file mode 100644 index 0000000..a3c9c7d --- /dev/null +++ b/packet/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "packet" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +byteorder = "1.4.3" +thiserror = "1.0.37" \ No newline at end of file diff --git a/packet/src/error.rs b/packet/src/error.rs new file mode 100644 index 0000000..da01a22 --- /dev/null +++ b/packet/src/error.rs @@ -0,0 +1,25 @@ +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum Error { + #[error("the buffer is too small")] + SmallBuffer, + + #[error("the packet is invalid")] + InvalidPacket, + #[error("Unimplemented")] + Unimplemented, + // #[error("the vaue is invalid for the field")] + // InvalidValue, + // + // #[error("the value has already been defined")] + // AlreadyDefined, + // + // #[error(transparent)] + // Io(#[from] io::Error), + // + // #[error(transparent)] + // Nul(#[from] ffi::NulError), +} + +pub type Result = ::std::result::Result; diff --git a/packet/src/icmp/icmp.rs b/packet/src/icmp/icmp.rs new file mode 100644 index 0000000..b175403 --- /dev/null +++ b/packet/src/icmp/icmp.rs @@ -0,0 +1,174 @@ +use std::fmt; + +use byteorder::{BigEndian, ReadBytesExt}; + +use crate::cal_checksum; +use crate::error::*; +/// icmp 协议 +/* https://www.rfc-editor.org/rfc/rfc792 + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Code | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 不同Type和Code有不同含义 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 数据体 不同Type和Code有不同含义 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +*/ +use crate::icmp::{Code, Kind}; +use crate::ip::ipv4::packet::IpV4Packet; + +pub struct IcmpPacket { + buffer: B, +} + +impl> IcmpPacket { + pub fn unchecked(buffer: B) -> Self { + Self { buffer } + } + pub fn new(buffer: B) -> Result { + if buffer.as_ref().len() < 8 { + Err(Error::SmallBuffer)? + } + let packet = Self::unchecked(buffer); + Ok(packet) + } +} + +impl + AsMut<[u8]>> IcmpPacket { + pub fn set_kind(&mut self, kind: Kind) { + self.buffer.as_mut()[0] = kind.into(); + } + pub fn update_checksum(&mut self) { + self.buffer.as_mut()[2..4].copy_from_slice(&[0, 0]); + let checksum = cal_checksum(self.buffer.as_ref()); + self.buffer.as_mut()[2..4].copy_from_slice(&checksum.to_be_bytes()); + } +} + +impl> IcmpPacket { + pub fn kind(&self) -> Kind { + Kind::from(self.buffer.as_ref()[0]) + } + pub fn code(&self) -> Code { + Code::from(self.kind(), self.buffer.as_ref()[1]) + } + pub fn checksum(&self) -> u16 { + (&self.buffer.as_ref()[2..]) + .read_u16::() + .unwrap() + } + pub fn is_valid(&self) -> bool { + self.checksum() == 0 || cal_checksum(self.buffer.as_ref()) == 0 + } + pub fn header_other(&self) -> HeaderOther { + match self.kind() { + Kind::EchoReply + | Kind::EchoRequest + | Kind::TimestampRequest + | Kind::TimestampReply + | Kind::InformationRequest + | Kind::InformationReply => { + let ide = (&self.buffer.as_ref()[4..]) + .read_u16::() + .unwrap(); + let seq = (&self.buffer.as_ref()[6..]) + .read_u16::() + .unwrap(); + HeaderOther::Identifier(ide, seq) + } + Kind::DestinationUnreachable | Kind::TimeExceeded | Kind::SourceQuench => { + let bytes = self.buffer.as_ref(); + HeaderOther::Unused(bytes[4], bytes[5], bytes[6], bytes[7]) + } + Kind::Redirect => { + let bytes = self.buffer.as_ref(); + HeaderOther::Address(bytes[4], bytes[5], bytes[6], bytes[7]) + } + Kind::ParameterProblem => HeaderOther::Pointer(self.buffer.as_ref()[4]), + _ => { + let bytes = self.buffer.as_ref(); + HeaderOther::UnKnown(bytes[4], bytes[5], bytes[6], bytes[7]) + } + } + } + pub fn payload(&self) -> &[u8] { + &self.buffer.as_ref()[8..] + } + pub fn description(&self) -> Description<&[u8]> { + use std::io::Cursor; + match self.kind() { + Kind::DestinationUnreachable + | Kind::TimeExceeded + | Kind::ParameterProblem + | Kind::SourceQuench + | Kind::Redirect => match IpV4Packet::new(self.payload()) { + Ok(d) => Description::Ip(d), + Err(_) => Description::Other(self.payload()), + }, + Kind::TimestampRequest | Kind::TimestampReply => { + let mut buffer = Cursor::new(self.payload()); + Description::Timestamp( + buffer.read_u32::().unwrap(), + buffer.read_u32::().unwrap(), + buffer.read_u32::().unwrap(), + ) + } + _ => Description::Other(self.payload()), + } + } +} + +impl> fmt::Debug for IcmpPacket { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct(if self.is_valid() { + "icmp::Packet" + } else { + "icmp::Packet!" + }) + .field("kind", &self.kind()) + .field("code", &self.code()) + .field("checksum", &self.checksum()) + .field("payload", &self.payload()) + .finish() + } +} + +#[derive(Debug)] +pub enum HeaderOther { + /// 全零 + Unused(u8, u8, u8, u8), + /// If code = 0, identifies the octet where an error was detected. + Pointer(u8), + /// Address of the gateway to which traffic for the network specified + /// in the internet destination network field of the original + /// datagram's data should be sent. + Address(u8, u8, u8, u8), + /// Identifier | Sequence Number + Identifier(u16, u16), + UnKnown(u8, u8, u8, u8), +} + +pub enum Description { + Ip(IpV4Packet), + ///时间戳 Originate Timestamp,Receive Timestamp,Transmit Timestamp + Timestamp(u32, u32, u32), + Other(B), +} + +impl + std::fmt::Debug> fmt::Debug for Description { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Description::Ip(packet) => f.debug_struct(&format!("{:?}", packet)).finish(), + Description::Timestamp(originate, receive, transmit) => f + .debug_struct("") + .field("originate", originate) + .field("receive", receive) + .field("transmit", transmit) + .finish(), + Description::Other(bytes) => f.debug_struct(&format!("{:?}", bytes)).finish(), + } + } +} diff --git a/packet/src/icmp/mod.rs b/packet/src/icmp/mod.rs new file mode 100644 index 0000000..dc9c214 --- /dev/null +++ b/packet/src/icmp/mod.rs @@ -0,0 +1,414 @@ +pub mod icmp; + +#[derive(Eq, PartialEq, Copy, Clone, Debug)] +pub enum Kind { + /// ping应答,type=0 + /* + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Code | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Identifier | Sequence Number | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Data ... + +-+-+-+-+- + */ + EchoReply, + /// 目的地不可达,差错报文的一种,路由器收到一个不能转发的数据报,会向源地址返回这个报文,type=3 + /* + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Code | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | unused | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Internet Header + 64 bits of Original Data Datagram | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + DestinationUnreachable, + /// 源抑制报文,用于防止接收端缓存溢出,接收设备发送这个来请求源设备降低发送速度,type=4 + /* + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Code | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | unused | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Internet Header + 64 bits of Original Data Datagram | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + SourceQuench, + /// 重定向报文,当路由器接收包的接口正好是去往目的地的出口时,会向源地址发送重定向报文,告知源直接将数据发往自己的下一跳,type=5 + /* + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Code | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Gateway Internet Address | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Internet Header + 64 bits of Original Data Datagram | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + Redirect, + /// ping请求,type=8 + EchoRequest, + /// 路由器通告,type=9, + RouterAdvertisement, + /// 路由器请求,type=10 + RouterSolicitation, + /// 报文ttl为0后,路由器会向源发送此报文,type=11 + /// Tracert工作原理: + /// 首先向目的地发送ttl=1的包,下一跳路由器收到后ttl-1,此时ttl=0,将向源发送 ICMP time exceeded + /// 再发送ttl=2的包,以此类推,直到目标主机接收到改包,此时不会回复ICMP time exceeded,代表已经探测到目的地 + /* + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Code | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | unused | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Internet Header + 64 bits of Original Data Datagram | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + TimeExceeded, + /// 参数错误,数据有误、校验和不对等,type=12 + /* + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Code | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Pointer | unused | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Internet Header + 64 bits of Original Data Datagram | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + 注:Pointer指示错误的位置 + */ + ParameterProblem, + /// 时间戳请求,type=13 + /* + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Code | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Identifier | Sequence Number | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Originate Timestamp | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Receive Timestamp | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Transmit Timestamp | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + TimestampRequest, + /// 时间戳响应,type=14 + TimestampReply, + /// 信息请求,type=15 + /* + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Code | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Identifier | Sequence Number | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + InformationRequest, + /// 信息响应,type=16 + InformationReply, + /// 地址掩码请求,type=17 + AddressMaskRequest, + /// 地址掩码应答,type=18 + AddressMaskReply, + /// + TraceRoute, + /// + Unknown(u8), +} + +impl From for Kind { + fn from(value: u8) -> Kind { + use self::Kind::*; + + match value { + 0 => EchoReply, + 3 => DestinationUnreachable, + 4 => SourceQuench, + 5 => Redirect, + 8 => EchoRequest, + 9 => RouterAdvertisement, + 10 => RouterSolicitation, + 11 => TimeExceeded, + 12 => ParameterProblem, + 13 => TimestampRequest, + 14 => TimestampReply, + 15 => InformationRequest, + 16 => InformationReply, + 17 => AddressMaskRequest, + 18 => AddressMaskReply, + 30 => TraceRoute, + v => Unknown(v), + } + } +} + +impl Into for Kind { + fn into(self) -> u8 { + use self::Kind::*; + match self { + EchoReply => 0, + DestinationUnreachable => 3, + SourceQuench => 4, + Redirect => 5, + EchoRequest => 8, + RouterAdvertisement => 9, + RouterSolicitation => 10, + TimeExceeded => 11, + ParameterProblem => 12, + TimestampRequest => 13, + TimestampReply => 14, + InformationRequest => 15, + InformationReply => 16, + AddressMaskRequest => 17, + AddressMaskReply => 18, + TraceRoute => 30, + Unknown(v) => v, + } + } +} + +#[derive(Eq, PartialEq, Copy, Clone, Debug)] +pub enum Code { + DestinationUnreachable(DestinationUnreachable), + Redirect(Redirect), + ParameterProblem(ParameterProblem), + Other(u8), +} + +impl Code { + pub fn from(kind: Kind, code: u8) -> Code { + match kind { + Kind::DestinationUnreachable => { + Code::DestinationUnreachable(DestinationUnreachable::from(code)) + } + Kind::Redirect => Code::Redirect(Redirect::from(code)), + Kind::ParameterProblem => Code::ParameterProblem(ParameterProblem::from(code)), + _ => Code::Other(code), + } + } +} + +#[derive(Eq, PartialEq, Copy, Clone, Debug)] +pub enum DestinationUnreachable { + /// 网络不可达 + DestinationNetworkUnreachable, + /// 主机不可达 + DestinationHostUnreachable, + /// 协议不可达 + DestinationProtocolUnreachable, + /// 端口不可达 + DestinationPortUnreachable, + /// 需要进行分片但设置不分片比特 + FragmentationRequired, + /// 源站选路失败 + SourceRouteFailed, + /// 目的网络未知 + DestinationNetworkUnknown, + /// 目的主机未知 + DestinationHostUnknown, + /// 源主机被隔离(作废不用) + SourceHostIsolated, + /// 目的网络被强制禁止 + NetworkAdministrativelyProhibited, + /// 目的主机被强制禁止 + HostAdministrativelyProhibited, + /// 由于服务类型TOS,网络不可达 + NetworkUnreachableForTos, + /// 由于服务类型TOS,主机不可达 + HostUnreachableForTos, + /// 由于过滤,通信被强制禁止 + CommunicationAdministrativelyProhibited, + /// 主机越权 + HostPrecedenceViolation, + /// 优先中止生效 + PrecedentCutoffInEffect, + /// + Unknown(u8), +} + +/// Codes for Redirect Message packets. +#[derive(Eq, PartialEq, Copy, Clone, Debug)] +pub enum Redirect { + /// 对网络重定向 + RedirectDatagramForNetwork, + /// 对主机重定向 + RedirectDatagramForHost, + /// 对服务类型和网络重定向 + RedirectDatagramForTosAndNetwork, + /// 对服务类型和主机重定向 + RedirectDatagramForTosAndHost, + /// + Unknown(u8), +} + +/// Codes for TimeExceeded Message packets. +#[derive(Eq, PartialEq, Copy, Clone, Debug)] +pub enum TimeExceeded { + /// TTL超时报文 + Transit, + /// 分片重组超时报文 + Reassembly, + /// + Unknown(u8), +} +/// Codes for Parameter Problem packets. +#[derive(Eq, PartialEq, Copy, Clone, Debug)] +pub enum ParameterProblem { + /// 坏的IP首部(包括各种差错) + PointerIndicatesError, + /// 缺少必需的选项 + MissingRequiredData, + /// 长度错误 + BadLength, + /// + Unknown(u8), +} + +impl From for DestinationUnreachable { + fn from(value: u8) -> Self { + use self::DestinationUnreachable::*; + + match value { + 0 => DestinationNetworkUnreachable, + 1 => DestinationHostUnreachable, + 2 => DestinationProtocolUnreachable, + 3 => DestinationPortUnreachable, + 4 => FragmentationRequired, + 5 => SourceRouteFailed, + 6 => DestinationNetworkUnknown, + 7 => DestinationHostUnknown, + 8 => SourceHostIsolated, + 9 => NetworkAdministrativelyProhibited, + 10 => HostAdministrativelyProhibited, + 11 => NetworkUnreachableForTos, + 12 => HostUnreachableForTos, + 13 => CommunicationAdministrativelyProhibited, + 14 => HostPrecedenceViolation, + 15 => PrecedentCutoffInEffect, + v => Unknown(v), + } + } +} + +impl Into for DestinationUnreachable { + fn into(self) -> u8 { + use self::DestinationUnreachable::*; + + match self { + DestinationNetworkUnreachable => 0, + DestinationHostUnreachable => 1, + DestinationProtocolUnreachable => 2, + DestinationPortUnreachable => 3, + FragmentationRequired => 4, + SourceRouteFailed => 5, + DestinationNetworkUnknown => 6, + DestinationHostUnknown => 7, + SourceHostIsolated => 8, + NetworkAdministrativelyProhibited => 9, + HostAdministrativelyProhibited => 10, + NetworkUnreachableForTos => 11, + HostUnreachableForTos => 12, + CommunicationAdministrativelyProhibited => 13, + HostPrecedenceViolation => 14, + PrecedentCutoffInEffect => 15, + Unknown(v) => v, + } + } +} + +impl From for Redirect { + fn from(value: u8) -> Self { + use self::Redirect::*; + + match value { + 0 => RedirectDatagramForNetwork, + 1 => RedirectDatagramForHost, + 2 => RedirectDatagramForTosAndNetwork, + 3 => RedirectDatagramForTosAndHost, + v => Unknown(v), + } + } +} + +impl Into for Redirect { + fn into(self) -> u8 { + use self::Redirect::*; + + match self { + RedirectDatagramForNetwork => 0, + RedirectDatagramForHost => 1, + RedirectDatagramForTosAndNetwork => 2, + RedirectDatagramForTosAndHost => 3, + Unknown(v) => v, + } + } +} + +impl From for TimeExceeded { + fn from(value: u8) -> Self { + use self::TimeExceeded::*; + + match value { + 0 => Transit, + 1 => Reassembly, + v => Unknown(v), + } + } +} + +impl Into for TimeExceeded { + fn into(self) -> u8 { + use self::TimeExceeded::*; + + match self { + Transit => 0, + Reassembly => 1, + Unknown(v) => v, + } + } +} + +impl From for ParameterProblem { + fn from(value: u8) -> Self { + use self::ParameterProblem::*; + + match value { + 0 => PointerIndicatesError, + 1 => MissingRequiredData, + 2 => BadLength, + v => Unknown(v), + } + } +} + +impl Into for ParameterProblem { + fn into(self) -> u8 { + use self::ParameterProblem::*; + + match self { + PointerIndicatesError => 0, + MissingRequiredData => 1, + BadLength => 2, + Unknown(v) => v, + } + } +} diff --git a/packet/src/ip/ipv4/mod.rs b/packet/src/ip/ipv4/mod.rs new file mode 100644 index 0000000..c9cf115 --- /dev/null +++ b/packet/src/ip/ipv4/mod.rs @@ -0,0 +1,2 @@ +pub mod packet; +pub mod protocol; diff --git a/packet/src/ip/ipv4/packet.rs b/packet/src/ip/ipv4/packet.rs new file mode 100644 index 0000000..9dd2bb8 --- /dev/null +++ b/packet/src/ip/ipv4/packet.rs @@ -0,0 +1,250 @@ +use std::fmt; +use std::net::Ipv4Addr; + +use byteorder::{BigEndian, ReadBytesExt}; + +use crate::cal_checksum; +use crate::error::*; +use crate::ip::ipv4::protocol::Protocol; + +/// ip协议 +/* +RFC: 791 https://www.ietf.org/rfc/rfc791.txt + + 0 15 31 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 版本(4) | 头部长度(4) | 服务类型(8) | 总字节数(16) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 标识(16) | 标志(3) | 片偏移(13) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 生存时间(8) | 协议(8) | 头部校验和(16) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 源ip地址(32) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 目的ip地址(32) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 选项 + 填充 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + 数据体 + 注:头部长度单位是4字节,所以ip头最长60字节,选项最长40字节,选项填充按4字节对齐 +*/ + +pub struct IpV4Packet { + pub buffer: B, +} + +impl> IpV4Packet { + pub fn unchecked(buffer: B) -> Self { + Self { buffer } + } + pub fn new(buffer: B) -> Result { + if buffer.as_ref()[0] >> 4 != 4 { + Err(Error::Unimplemented)? + } + if buffer.as_ref().len() < 20 { + Err(Error::SmallBuffer)? + } + let packet = Self::unchecked(buffer); + if packet.buffer.as_ref().len() < packet.header_len() as usize * 4 { + Err(Error::SmallBuffer)? + } + Ok(packet) + } +} + +impl> IpV4Packet { + pub fn header(&self) -> &[u8] { + &self.buffer.as_ref()[..(self.header_len() as usize * 4)] + } + pub fn payload(&self) -> &[u8] { + &self.buffer.as_ref()[(self.header_len() as usize * 4)..] + // match self.protocol() { + // Protocol::Udp => { + // let udp = UdpPacket::new(IpAddr::V4(self.source_ip()), + // IpAddr::V4(self.destination_ip()), + // &self.buffer.as_ref()[(self.header_len() as usize * 4)..])?; + // Ok(crate::IpUpperLayer::UDP(udp)) + // } + // _ => { + // Ok(crate::IpUpperLayer::Unknown(self.buffer.as_ref())); + // } + // } + } +} + +impl + AsMut<[u8]>> IpV4Packet { + pub fn header_mut(&mut self) -> &mut [u8] { + let len = self.header_len() as usize * 4; + &mut self.buffer.as_mut()[..len] + } + pub fn payload_mut(&mut self) -> &mut [u8] { + let len = self.header_len() as usize * 4; + &mut self.buffer.as_mut()[len..] + } + + pub fn set_source_ip(&mut self, value: Ipv4Addr) { + self.header_mut()[12..16].copy_from_slice(&value.octets()); + } + pub fn set_destination_ip(&mut self, value: Ipv4Addr) { + self.header_mut()[16..20].copy_from_slice(&value.octets()); + } + fn set_checksum(&mut self, value: u16) { + self.header_mut()[10..12].copy_from_slice(&value.to_be_bytes()) + } + /// 更新校验和 + pub fn update_checksum(&mut self) { + //先将校验和置0 + self.set_checksum(0); + self.set_checksum(cal_checksum(self.header())) + } +} + +impl> IpV4Packet { + /// 版本号,ipv4的为4 + pub fn version(&self) -> u8 { + self.buffer.as_ref()[0] >> 4 + } + + /// 头部长度,以4字节为单位 + pub fn header_len(&self) -> u8 { + self.buffer.as_ref()[0] & 0b1111 + } + + /// 差异化服务编码点 + /// + /// 类别(3)+丢失概率(2)+用途(1) + /// + /// + /// 类别子字段值 | 名称 + /// ---|:--- + /// 000 | 常规(Routine) + /// 001 | 优先(Priority) + /// 010 | 立即(Immediate) + /// 011 | 瞬间(Flash) + /// 100 | 瞬间覆盖(Flash Override) + /// 101 | 严重(CRITIC/ECP) + /// 110 | 网间控制(Internetwork Control) + /// 111 | 网络控制(Network Control) + /// + /// + /// 参考:https://www.modb.pro/db/477116 + pub fn dscp(&self) -> u8 { + self.buffer.as_ref()[1] >> 2 + } + + /// 显示拥塞 00:发送主机不支持ECN 01或者10:发送主机支持ECN 11:路由器正在经历拥塞 + pub fn ecn(&self) -> u8 { + self.buffer.as_ref()[1] & 0b11 + } + + /// ip报总字节数 + pub fn length(&self) -> u16 { + (&self.buffer.as_ref()[2..]) + .read_u16::() + .unwrap() + } + + /// 标识. ip报文在数据链路层可能会被拆分,同一报文的不同分组标识字段相同 + pub fn id(&self) -> u16 { + (&self.buffer.as_ref()[4..]) + .read_u16::() + .unwrap() + } + + /// 标志 3位. + /// 第1位没有使用 + /// 第2位表示不分段位(DF) + /// 0:允许数据报分段 + /// 1:数据报不能分段 + /// 置1之后路由器不能对其分段处理,如果超过MTU值则路由器不能对其转发将其丢弃,并向源点发送错误消息 + /// 第3位表示更多段位 + /// 0:数据包后面没有包,该包为最后的包 + /// 1:数据包后面有更多的包 + pub fn flags(&self) -> u8 { + self.buffer.as_ref()[6] >> 5 + } + + /// 片偏移 13位. + /// 以字节为单位,用于指明分段起始点相对于包头起始点的偏移量 + /// 由于分段到达时可能错序,所以分段的偏移字段可以使接收者按照正确的顺序重组数据包 + pub fn offset(&self) -> u16 { + (&self.buffer.as_ref()[6..]) + .read_u16::() + .unwrap() + & 0x1fff + } + + /// 生存时间. + /// 每一跳 减1 到0了则会被丢弃 + pub fn ttl(&self) -> u8 { + self.buffer.as_ref()[8] + } + + /// 协议. + pub fn protocol(&self) -> Protocol { + self.buffer.as_ref()[9].into() + } + + /// 首部校验和 + pub fn checksum(&self) -> u16 { + (&self.buffer.as_ref()[10..]) + .read_u16::() + .unwrap() + } + /// 验证校验和 + /// + /// TCP/IP协议栈不会自己计算校验和,而是简单地将一个空的校验和字段(零或随机填充)交给网卡硬件。 + /// 所以抓到发出去的包校验和可能是错误的 + pub fn is_valid(&self) -> bool { + self.checksum() == 0 || cal_checksum(self.header()) == 0 + } + /// 源ip. + pub fn source_ip(&self) -> Ipv4Addr { + Ipv4Addr::new( + self.buffer.as_ref()[12], + self.buffer.as_ref()[13], + self.buffer.as_ref()[14], + self.buffer.as_ref()[15], + ) + } + + /// 目标ip. + pub fn destination_ip(&self) -> Ipv4Addr { + Ipv4Addr::new( + self.buffer.as_ref()[16], + self.buffer.as_ref()[17], + self.buffer.as_ref()[18], + self.buffer.as_ref()[19], + ) + } + + /// 选项. + pub fn options(&self) -> &[u8] { + &self.buffer.as_ref()[20..(self.header_len() as usize * 4)] + } +} + +impl> fmt::Debug for IpV4Packet { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ip::v4::Packet") + .field("version", &self.version()) + .field("header_len", &self.header_len()) + .field("dscp", &self.dscp()) + .field("ecn", &self.ecn()) + .field("length", &self.length()) + .field("id", &self.id()) + .field("flags", &self.flags()) + .field("offset", &self.offset()) + .field("ttl", &self.ttl()) + .field("protocol", &self.protocol()) + .field("checksum", &self.checksum()) + .field("is_valid", &self.is_valid()) + .field("source", &self.source_ip()) + .field("destination", &self.destination_ip()) + .field("options", &self.options()) + .field("payload", &self.payload()) + .finish() + } +} diff --git a/packet/src/ip/ipv4/protocol.rs b/packet/src/ip/ipv4/protocol.rs new file mode 100644 index 0000000..0dc8e4f --- /dev/null +++ b/packet/src/ip/ipv4/protocol.rs @@ -0,0 +1,742 @@ +#[derive(Eq, PartialEq, Copy, Clone, Debug)] +pub enum Protocol { + /// + Hopopt, + + /// + Icmp, + + /// + Igmp, + + /// + Ggp, + + /// + Ipv4, + + /// + St, + + /// + Tcp, + + /// + Cbt, + + /// + Egp, + + /// + Igp, + + /// + BbnRccMon, + + /// + NvpII, + + /// + Pup, + + /// + Argus, + + /// + Emcon, + + /// + Xnet, + + /// + Chaos, + + /// + Udp, + + /// + Mux, + + /// + DcnMeas, + + /// + Hmp, + + /// + Prm, + + /// + XnsIdp, + + /// + Trunk1, + + /// + Trunk2, + + /// + Leaf1, + + /// + Leaf2, + + /// + Rdp, + + /// + Irtp, + + /// + IsoTp4, + + /// + Netblt, + + /// + MfeNsp, + + /// + MeritInp, + + /// + Dccp, + + /// + ThreePc, + + /// + Idpr, + + /// + Xtp, + + /// + Ddp, + + /// + IdprCmtp, + + /// + TpPlusPlus, + + /// + Il, + + /// + Ipv6, + + /// + Sdrp, + + /// + Ipv6Route, + + /// + Ipv6Frag, + + /// + Idrp, + + /// + Rsvp, + + /// + Gre, + + /// + Dsr, + + /// + Bna, + + /// + Esp, + + /// + Ah, + + /// + INlsp, + + /// + Swipe, + + /// + Narp, + + /// + Mobile, + + /// + Tlsp, + + /// + Skip, + + /// + Ipv6Icmp, + + /// + Ipv6NoNxt, + + /// + Ipv6Opts, + + /// + HostInternal, + + /// + Cftp, + + /// + LocalNetwork, + + /// + SatExpak, + + /// + Kryptolan, + + /// + Rvd, + + /// + Ippc, + + /// + DistributedFs, + + /// + SatMon, + + /// + Visa, + + /// + Ipcv, + + /// + Cpnx, + + /// + Cphb, + + /// + Wsn, + + /// + Pvp, + + /// + BrSatMon, + + /// + SunNd, + + /// + WbMon, + + /// + WbExpak, + + /// + IsoIp, + + /// + Vmtp, + + /// + SecureVmtp, + + /// + Vines, + + /// + TtpOrIptm, + + /// + NsfnetIgp, + + /// + Dgp, + + /// + Tcf, + + /// + Eigrp, + + /// + OspfigP, + + /// + SpriteRpc, + + /// + Larp, + + /// + Mtp, + + /// + Ax25, + + /// + IpIp, + + /// + Micp, + + /// + SccSp, + + /// + Etherip, + + /// + Encap, + + /// + PrivEncryption, + + /// + Gmtp, + + /// + Ifmp, + + /// + Pnni, + + /// + Pim, + + /// + Aris, + + /// + Scps, + + /// + Qnx, + + /// + AN, + + /// + IpComp, + + /// + Snp, + + /// + CompaqPeer, + + /// + IpxInIp, + + /// + Vrrp, + + /// + Pgm, + + /// + ZeroHop, + + /// + L2tp, + + /// + Ddx, + + /// + Iatp, + + /// + Stp, + + /// + Srp, + + /// + Uti, + + /// + Smp, + + /// + Sm, + + /// + Ptp, + + /// + IsisOverIpv4, + + /// + Fire, + + /// + Crtp, + + /// + Crudp, + + /// + Sscopmce, + + /// + Iplt, + + /// + Sps, + + /// + Pipe, + + /// + Sctp, + + /// + Fc, + + /// + RsvpE2eIgnore, + + /// + MobilityHeader, + + /// + UdpLite, + + /// + MplsInIp, + + /// + Manet, + + /// + Hip, + + /// + Shim6, + + /// + Wesp, + Rohc, + Test1, + Test2, + Unknown(u8), +} + +impl From for Protocol { + fn from(value: u8) -> Protocol { + use self::Protocol::*; + + match value { + 0 => Hopopt, + 1 => Icmp, + 2 => Igmp, + 3 => Ggp, + 4 => Ipv4, + 5 => St, + 6 => Tcp, + 7 => Cbt, + 8 => Egp, + 9 => Igp, + 10 => BbnRccMon, + 11 => NvpII, + 12 => Pup, + 13 => Argus, + 14 => Emcon, + 15 => Xnet, + 16 => Chaos, + 17 => Udp, + 18 => Mux, + 19 => DcnMeas, + 20 => Hmp, + 21 => Prm, + 22 => XnsIdp, + 23 => Trunk1, + 24 => Trunk2, + 25 => Leaf1, + 26 => Leaf2, + 27 => Rdp, + 28 => Irtp, + 29 => IsoTp4, + 30 => Netblt, + 31 => MfeNsp, + 32 => MeritInp, + 33 => Dccp, + 34 => ThreePc, + 35 => Idpr, + 36 => Xtp, + 37 => Ddp, + 38 => IdprCmtp, + 39 => TpPlusPlus, + 40 => Il, + 41 => Ipv6, + 42 => Sdrp, + 43 => Ipv6Route, + 44 => Ipv6Frag, + 45 => Idrp, + 46 => Rsvp, + 47 => Gre, + 48 => Dsr, + 49 => Bna, + 50 => Esp, + 51 => Ah, + 52 => INlsp, + 53 => Swipe, + 54 => Narp, + 55 => Mobile, + 56 => Tlsp, + 57 => Skip, + 58 => Ipv6Icmp, + 59 => Ipv6NoNxt, + 60 => Ipv6Opts, + 61 => HostInternal, + 62 => Cftp, + 63 => LocalNetwork, + 64 => SatExpak, + 65 => Kryptolan, + 66 => Rvd, + 67 => Ippc, + 68 => DistributedFs, + 69 => SatMon, + 70 => Visa, + 71 => Ipcv, + 72 => Cpnx, + 73 => Cphb, + 74 => Wsn, + 75 => Pvp, + 76 => BrSatMon, + 77 => SunNd, + 78 => WbMon, + 79 => WbExpak, + 80 => IsoIp, + 81 => Vmtp, + 82 => SecureVmtp, + 83 => Vines, + 84 => TtpOrIptm, + 85 => NsfnetIgp, + 86 => Dgp, + 87 => Tcf, + 88 => Eigrp, + 89 => OspfigP, + 90 => SpriteRpc, + 91 => Larp, + 92 => Mtp, + 93 => Ax25, + 94 => IpIp, + 95 => Micp, + 96 => SccSp, + 97 => Etherip, + 98 => Encap, + 99 => PrivEncryption, + 100 => Gmtp, + 101 => Ifmp, + 102 => Pnni, + 103 => Pim, + 104 => Aris, + 105 => Scps, + 106 => Qnx, + 107 => AN, + 108 => IpComp, + 109 => Snp, + 110 => CompaqPeer, + 111 => IpxInIp, + 112 => Vrrp, + 113 => Pgm, + 114 => ZeroHop, + 115 => L2tp, + 116 => Ddx, + 117 => Iatp, + 118 => Stp, + 119 => Srp, + 120 => Uti, + 121 => Smp, + 122 => Sm, + 123 => Ptp, + 124 => IsisOverIpv4, + 125 => Fire, + 126 => Crtp, + 127 => Crudp, + 128 => Sscopmce, + 129 => Iplt, + 130 => Sps, + 131 => Pipe, + 132 => Sctp, + 133 => Fc, + 134 => RsvpE2eIgnore, + 135 => MobilityHeader, + 136 => UdpLite, + 137 => MplsInIp, + 138 => Manet, + 139 => Hip, + 140 => Shim6, + 141 => Wesp, + 142 => Rohc, + 253 => Test1, + 254 => Test2, + p => Unknown(p), + } + } +} + +impl Into for Protocol { + fn into(self) -> u8 { + use self::Protocol::*; + + match self { + Hopopt => 0, + Icmp => 1, + Igmp => 2, + Ggp => 3, + Ipv4 => 4, + St => 5, + Tcp => 6, + Cbt => 7, + Egp => 8, + Igp => 9, + BbnRccMon => 10, + NvpII => 11, + Pup => 12, + Argus => 13, + Emcon => 14, + Xnet => 15, + Chaos => 16, + Udp => 17, + Mux => 18, + DcnMeas => 19, + Hmp => 20, + Prm => 21, + XnsIdp => 22, + Trunk1 => 23, + Trunk2 => 24, + Leaf1 => 25, + Leaf2 => 26, + Rdp => 27, + Irtp => 28, + IsoTp4 => 29, + Netblt => 30, + MfeNsp => 31, + MeritInp => 32, + Dccp => 33, + ThreePc => 34, + Idpr => 35, + Xtp => 36, + Ddp => 37, + IdprCmtp => 38, + TpPlusPlus => 39, + Il => 40, + Ipv6 => 41, + Sdrp => 42, + Ipv6Route => 43, + Ipv6Frag => 44, + Idrp => 45, + Rsvp => 46, + Gre => 47, + Dsr => 48, + Bna => 49, + Esp => 50, + Ah => 51, + INlsp => 52, + Swipe => 53, + Narp => 54, + Mobile => 55, + Tlsp => 56, + Skip => 57, + Ipv6Icmp => 58, + Ipv6NoNxt => 59, + Ipv6Opts => 60, + HostInternal => 61, + Cftp => 62, + LocalNetwork => 63, + SatExpak => 64, + Kryptolan => 65, + Rvd => 66, + Ippc => 67, + DistributedFs => 68, + SatMon => 69, + Visa => 70, + Ipcv => 71, + Cpnx => 72, + Cphb => 73, + Wsn => 74, + Pvp => 75, + BrSatMon => 76, + SunNd => 77, + WbMon => 78, + WbExpak => 79, + IsoIp => 80, + Vmtp => 81, + SecureVmtp => 82, + Vines => 83, + TtpOrIptm => 84, + NsfnetIgp => 85, + Dgp => 86, + Tcf => 87, + Eigrp => 88, + OspfigP => 89, + SpriteRpc => 90, + Larp => 91, + Mtp => 92, + Ax25 => 93, + IpIp => 94, + Micp => 95, + SccSp => 96, + Etherip => 97, + Encap => 98, + PrivEncryption => 99, + Gmtp => 100, + Ifmp => 101, + Pnni => 102, + Pim => 103, + Aris => 104, + Scps => 105, + Qnx => 106, + AN => 107, + IpComp => 108, + Snp => 109, + CompaqPeer => 110, + IpxInIp => 111, + Vrrp => 112, + Pgm => 113, + ZeroHop => 114, + L2tp => 115, + Ddx => 116, + Iatp => 117, + Stp => 118, + Srp => 119, + Uti => 120, + Smp => 121, + Sm => 122, + Ptp => 123, + IsisOverIpv4 => 124, + Fire => 125, + Crtp => 126, + Crudp => 127, + Sscopmce => 128, + Iplt => 129, + Sps => 130, + Pipe => 131, + Sctp => 132, + Fc => 133, + RsvpE2eIgnore => 134, + MobilityHeader => 135, + UdpLite => 136, + MplsInIp => 137, + Manet => 138, + Hip => 139, + Shim6 => 140, + Wesp => 141, + Rohc => 142, + Test1 => 253, + Test2 => 254, + Unknown(p) => p, + } + } +} diff --git a/packet/src/ip/mod.rs b/packet/src/ip/mod.rs new file mode 100644 index 0000000..1ee3452 --- /dev/null +++ b/packet/src/ip/mod.rs @@ -0,0 +1,18 @@ +use ipv4::packet::IpV4Packet; + +use crate::error::*; + +pub mod ipv4; + +pub enum IpPacket { + V4(IpV4Packet), +} + +impl> IpPacket { + pub fn new(buffer: B) -> Result { + match buffer.as_ref()[0] >> 4 { + 4 => Ok(IpPacket::V4(IpV4Packet::new(buffer)?)), + _ => Err(Error::InvalidPacket), + } + } +} diff --git a/packet/src/lib.rs b/packet/src/lib.rs new file mode 100644 index 0000000..e6291da --- /dev/null +++ b/packet/src/lib.rs @@ -0,0 +1,146 @@ +use std::net::Ipv4Addr; + +use byteorder::BigEndian; +use byteorder::ReadBytesExt; + +pub mod error; +pub mod icmp; +pub mod ip; +pub mod tcp; +pub mod udp; + +// pub enum IpUpperLayer { +// UDP(UdpPacket), +// Unknown(B), +// } +// +// impl> fmt::Debug for IpUpperLayer { +// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +// match self { +// IpUpperLayer::UDP(p) => { +// f.debug_struct("udp::Packet") +// .field("data", p).finish() +// } +// IpUpperLayer::Unknown(p) => { +// f.debug_struct("Unknown") +// .field("data", &p.as_ref()).finish() +// } +// } +// } +// } + +/// https://datatracker.ietf.org/doc/html/rfc1071 4.1节 +/// +/// 计算校验和,各协议都是通用的 +/// 计算: +/// 首先将校验和置0,然后对首部每个16位数进行二进制反码求和, +/// 得到校验和之后,持续取高16位加到低16位,直到高16位全为0 +/// 最后取反 +/// +/// 校验: +/// 在已有校验和的情况下,再计算校验和,正确的数据计算得到的值为0 +/* +unsigned short getChecksum(unsigned short * iphead, int count) +{ + unsigned long int sum = 0; + unsigned short checksum = 0; + + printf("\nStarting adress: %p\n", iphead); + + while(count > 1) { + sum += * (unsigned short *) (iphead); + count -=2; + printf("a: %p, content is: %d, new sum: %ld\n", iphead, (unsigned short) *(iphead), sum); + iphead++; + } + + if(count > 0) { + sum += * (unsigned short *) (iphead); + } + + while(sum >> 16) { + sum = (sum & 0xffff) + (sum >> 16); + } + + checksum = ~sum; + + return checksum; +} + */ +pub fn cal_checksum(buffer: &[u8]) -> u16 { + use std::io::Cursor; + let mut sum = 0; + let length = buffer.len(); + let mut buffer = Cursor::new(buffer); + while let Ok(value) = buffer.read_u16::() { + sum += u32::from(value); + } + if length & 1 == 1 { + //奇数,说明还有一位,不足的补0 + sum += u32c(buffer.read_u8().unwrap(), 0); + } + while sum >> 16 != 0 { + sum = (sum & 0xffff) + (sum >> 16); + } + !sum as u16 +} + +/// ipv4上层协议校验和计算方式 +/// ipv4 udp伪首部 用于参与计算首部校验和 +/* + 0 7 8 15 16 23 24 31 + +--------+--------+--------+--------+ + | source address | + +--------+--------+--------+--------+ + | destination address | + +--------+--------+--------+--------+ + | zero |protocol| length | + +--------+--------+--------+--------+ +*/ +pub fn ipv4_cal_checksum( + buffer: &[u8], + src_ip: &Ipv4Addr, + dest_ip: &Ipv4Addr, + protocol: u8, + length: u16, +) -> u16 { + use std::io::Cursor; + let mut sum = 0; + let src_ip = src_ip.octets(); + sum += u32c(src_ip[0], src_ip[1]); + sum += u32c(src_ip[2], src_ip[3]); + let dest_ip = dest_ip.octets(); + sum += u32c(dest_ip[0], dest_ip[1]); + sum += u32c(dest_ip[2], dest_ip[3]); + sum += u32c(0, protocol); + sum += length as u32; + let mut buffer = Cursor::new(buffer); + while let Ok(value) = buffer.read_u16::() { + sum += u32::from(value); + } + if length & 1 == 1 { + //奇数,说明还有一位 + sum += u32c(buffer.read_u8().unwrap(), 0); + } + while sum >> 16 != 0 { + sum = (sum & 0xffff) + (sum >> 16); + } + !sum as u16 +} + +#[inline] +fn u32c(x: u8, y: u8) -> u32 { + ((x as u32) << 8) | y as u32 +} + + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let sum = cal_checksum(&[255, 255]); + println!("{:?}", sum); + } +} diff --git a/packet/src/tcp/mod.rs b/packet/src/tcp/mod.rs new file mode 100644 index 0000000..ff641ba --- /dev/null +++ b/packet/src/tcp/mod.rs @@ -0,0 +1,42 @@ +use std::fmt; + +pub mod tcp; + +pub struct Flags(u8); + +pub const FIN: u8 = 0b0000_0001; +pub const SYN: u8 = 0b0000_0010; +pub const RST: u8 = 0b0000_0100; +pub const PSH: u8 = 0b0000_1000; +pub const ACK: u8 = 0b0001_0000; +pub const URG: u8 = 0b0010_0000; + +impl fmt::Debug for Flags { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut str = String::with_capacity(22); + if self.0 & URG != 0 { + str.push_str("URG|"); + } + if self.0 & ACK != 0 { + str.push_str("ACK|"); + } + if self.0 & PSH != 0 { + str.push_str("PSH|"); + } + if self.0 & RST != 0 { + str.push_str("RST|"); + } + if self.0 & SYN != 0 { + str.push_str("SYN|"); + } + if self.0 & FIN != 0 { + str.push_str("FIN|"); + } + if str.is_empty() { + f.debug_struct("NULL").finish() + } else { + let len = str.len() - 1; + f.debug_struct(&str[..len]).finish() + } + } +} diff --git a/packet/src/tcp/tcp.rs b/packet/src/tcp/tcp.rs new file mode 100644 index 0000000..11408db --- /dev/null +++ b/packet/src/tcp/tcp.rs @@ -0,0 +1,175 @@ +use std::fmt; +use std::net::IpAddr; + +use byteorder::{BigEndian, ReadBytesExt}; + +use crate::error::*; +use crate::tcp::Flags; + +/// tcp +/* + https://www.rfc-editor.org/rfc/rfc793 + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Source Port | Destination Port | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Sequence Number | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Acknowledgment Number | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Data | |U|A|P|R|S|F| | + | Offset| Reserved |R|C|S|S|Y|I| Window | + | | |G|K|H|T|N|N| | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Checksum | Urgent Pointer | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Options | Padding | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | data | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Source Port: 16位 源端口 + Destination Port:16位 目的端口 + Sequence Number:32位 序列号,如果存在syn标志,则为初始序列号 + Acknowledgment Number:32位 如果设置了ack标志,这个表示确认收到的序号 + Data Offset:4位 数据的开始偏移位,单位是4字节 + Reserved:6位 未使用,全零 + 控制位:6位 从左到右 + URG: 紧急指针 表示数据要优先处理 + ACK: 确认位 + PSH: 推送 要求把数据尽快的交给应用层,不做处理 + RST: 重置连接 + SYN: 同步序列号 + FIN: 结束发送 + Window: 16位 能接收的数据大小 + Checksum:16位 校验和,需要加入伪首部 + Urgent Pointer:16位 紧急指针 + Options+Padding:32位整数倍,最多40个字节 +*/ +pub struct TcpPacket { + source_ip: IpAddr, + destination_ip: IpAddr, + buffer: B, +} + +impl> TcpPacket { + pub fn unchecked(source_ip: IpAddr, destination_ip: IpAddr, buffer: B) -> TcpPacket { + TcpPacket { + source_ip, + destination_ip, + buffer, + } + } + pub fn new(source_ip: IpAddr, destination_ip: IpAddr, buffer: B) -> Result> { + let packet = TcpPacket::unchecked(source_ip, destination_ip, buffer); + + if packet.buffer.as_ref().len() < 20 { + Err(Error::SmallBuffer)? + } + + if packet.buffer.as_ref().len() < packet.data_offset() as usize * 4 { + Err(Error::SmallBuffer)? + } + + Ok(packet) + } +} + +impl> TcpPacket { + /// 源端口 + pub fn source_port(&self) -> u16 { + (&self.buffer.as_ref()[0..]) + .read_u16::() + .unwrap() + } + + /// 目标端口 + pub fn destination_port(&self) -> u16 { + (&self.buffer.as_ref()[2..]) + .read_u16::() + .unwrap() + } + /// 序列号 + pub fn sequence(&self) -> u32 { + (&self.buffer.as_ref()[4..]) + .read_u32::() + .unwrap() + } + /// 确认号 + pub fn acknowledgment(&self) -> u32 { + (&self.buffer.as_ref()[8..]) + .read_u32::() + .unwrap() + } + /// 数据偏移 4字节为单位 + pub fn data_offset(&self) -> u8 { + self.buffer.as_ref()[12] >> 4 + } + pub fn flags(&self) -> Flags { + Flags(self.buffer.as_ref()[13]) + } + pub fn window(&self) -> u16 { + (&self.buffer.as_ref()[14..]) + .read_u16::() + .unwrap() + } + pub fn checksum(&self) -> u16 { + (&self.buffer.as_ref()[16..]) + .read_u16::() + .unwrap() + } + /// 验证校验和,ipv4中为0表示不使用校验和,ipv6校验和不能为0 + /// TCP/IP协议栈不会自己计算校验和,而是简单地将一个空的校验和字段(零或随机填充)交给网卡硬件。 + /// 所以抓到发出去的包校验和可能是错误的 + pub fn is_valid(&self) -> bool { + self.checksum() == 0 || self.cal_checksum() == 0 + } + fn cal_checksum(&self) -> u16 { + match self.source_ip { + IpAddr::V4(src) => { + if let IpAddr::V4(dest) = self.destination_ip { + return crate::ipv4_cal_checksum( + self.buffer.as_ref(), + &src, + &dest, + 6, + self.buffer.as_ref().len() as u16, + ); + } + } + IpAddr::V6(_src) => {} + } + unimplemented!() + } + pub fn urgent_pointer(&self) -> u16 { + (&self.buffer.as_ref()[18..]) + .read_u16::() + .unwrap() + } + pub fn options(&self) -> &[u8] { + &self.buffer.as_ref()[20..(self.data_offset() as usize * 4)] + } + pub fn payload(&self) -> &[u8] { + &self.buffer.as_ref()[(self.data_offset() as usize * 4)..] + } +} + +impl> fmt::Debug for TcpPacket { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("tcp::Packet") + .field("source", &self.source_port()) + .field("destination", &self.destination_port()) + .field("sequence", &self.sequence()) + .field("acknowledgment", &self.acknowledgment()) + .field("offset", &self.data_offset()) + .field("flags", &self.flags()) + .field("window", &self.window()) + .field("checksum", &self.checksum()) + .field("is_valid", &self.is_valid()) + .field("pointer", &self.urgent_pointer()) + .field("options", &self.options()) + .field("payload", &self.payload()) + .finish() + } +} diff --git a/packet/src/udp/mod.rs b/packet/src/udp/mod.rs new file mode 100644 index 0000000..21e3805 --- /dev/null +++ b/packet/src/udp/mod.rs @@ -0,0 +1 @@ +pub mod udp; diff --git a/packet/src/udp/udp.rs b/packet/src/udp/udp.rs new file mode 100644 index 0000000..af6b1c5 --- /dev/null +++ b/packet/src/udp/udp.rs @@ -0,0 +1,177 @@ +use std::fmt; +use std::io::Cursor; +use std::net::IpAddr; + +use byteorder::WriteBytesExt; +use byteorder::{BigEndian, ReadBytesExt}; + +use crate::error::*; + +/// udp协议 +/// +/* +RFC 768 https://www.ietf.org/rfc/rfc768.txt + + 0 7 8 15 16 23 24 31 + +--------+--------+--------+--------+ + | 源端口(16) | 目的端口(16) | + +--------+--------+--------+--------+ + | 长度(16) | 校验和(16) | + +--------+--------+--------+--------+ + | + | 载荷 ... + +---------------- ... + + 注:1.长度包含标头和数据体,以字节为单位 + 2.伪首部和载荷参与校验和的计算,位数不够则补0 +*/ + +/// ipv6 udp伪首部 +/* https://datatracker.ietf.org/doc/html/rfc2460 + + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + + + + | | + + Source Address + + | | + + + + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + + + + | | + + Destination Address + + | | + + + + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Upper-Layer Packet Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | zero | Next Header | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +pub struct UdpPacket { + source_ip: IpAddr, + destination_ip: IpAddr, + buffer: B, +} + +impl> UdpPacket { + pub fn unchecked(source_ip: IpAddr, destination_ip: IpAddr, buffer: B) -> UdpPacket { + UdpPacket { + source_ip, + destination_ip, + buffer, + } + } + pub fn new(source_ip: IpAddr, destination_ip: IpAddr, buffer: B) -> Result> { + if buffer.as_ref().len() < 8 { + Err(Error::SmallBuffer)? + } + let packet = Self::unchecked(source_ip, destination_ip, buffer); + Ok(packet) + } +} + +impl> UdpPacket { + /// 源端口 + pub fn source_port(&self) -> u16 { + (&self.buffer.as_ref()[0..]) + .read_u16::() + .unwrap() + } + + /// 目标端口 + pub fn destination_port(&self) -> u16 { + (&self.buffer.as_ref()[2..]) + .read_u16::() + .unwrap() + } + + /// 总字节数 + pub fn length(&self) -> u16 { + (&self.buffer.as_ref()[4..]) + .read_u16::() + .unwrap() + } + + /// Checksum of the packet. + pub fn checksum(&self) -> u16 { + (&self.buffer.as_ref()[6..]) + .read_u16::() + .unwrap() + } + /// 验证校验和,ipv4中为0表示不使用校验和,ipv6校验和不能为0 + pub fn is_valid(&self) -> bool { + self.checksum() == 0 || self.cal_checksum() == 0 + } + pub fn payload(&self) -> &[u8] { + &self.buffer.as_ref()[8..] + } + fn cal_checksum(&self) -> u16 { + match self.source_ip { + IpAddr::V4(src) => { + if let IpAddr::V4(dest) = self.destination_ip { + return crate::ipv4_cal_checksum( + self.buffer.as_ref(), + &src, + &dest, + 17, + self.length(), + ); + } + } + IpAddr::V6(_src) => {} + } + unimplemented!() + } +} + +impl + AsMut<[u8]>> UdpPacket { + fn header_mut(&mut self) -> &mut [u8] { + &mut self.buffer.as_mut()[..8] + } +} + +impl + AsMut<[u8]>> UdpPacket { + /// 设置源端口 + pub fn set_source_port(&mut self, value: u16) -> &mut Self { + Cursor::new(&mut self.header_mut()[0..]) + .write_u16::(value) + .unwrap(); + self + } + + /// 设置目的端口 + pub fn set_destination_port(&mut self, value: u16) -> &mut Self { + Cursor::new(&mut self.header_mut()[2..]) + .write_u16::(value) + .unwrap(); + self + } + fn set_checknum(&mut self, value: u16) { + Cursor::new(&mut self.header_mut()[6..]) + .write_u16::(value) + .unwrap(); + } + pub fn update_checknum(&mut self) { + //先写0 + self.set_checknum(0); + self.set_checknum(self.cal_checksum()); + } +} + +impl> fmt::Debug for UdpPacket { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("udp::Packet") + .field("source", &self.source_port()) + .field("destination", &self.destination_port()) + .field("length", &self.length()) + .field("checksum", &self.checksum()) + .field("is_valid", &self.is_valid()) + .field("payload", &self.payload()) + .finish() + } +} diff --git a/proto/message.proto b/proto/message.proto new file mode 100644 index 0000000..68415ad --- /dev/null +++ b/proto/message.proto @@ -0,0 +1,40 @@ +syntax = "proto3"; +message RegistrationRequest{ + string token = 1; + string mac_address = 2; +} + +message RegistrationResponse{ + fixed32 virtual_ip = 1; + fixed32 virtual_gateway = 2; + fixed32 virtual_netmask = 3; + uint32 epoch = 4; + repeated fixed32 virtual_ip_list = 5; + fixed32 public_ip = 6; + uint32 public_port = 7; +} + +message DeviceList{ + uint32 epoch = 1; + repeated fixed32 virtual_ip_list = 2; +} + +message Punch{ + fixed32 virtual_ip = 1; + repeated fixed32 public_ip_list = 2; + uint32 public_port = 3; + uint32 public_port_range = 4; + NatType nat_type = 5; + bool reply = 6; + Step step = 7; +} +enum NatType{ + Symmetric = 0; + Cone = 1; +} +enum Step{ + Step1 = 0; + Step2 = 1; + Step3 = 2; + Step4 = 3; +} \ No newline at end of file diff --git a/src/admin_check.rs b/src/admin_check.rs new file mode 100644 index 0000000..94ca4d3 --- /dev/null +++ b/src/admin_check.rs @@ -0,0 +1,76 @@ +/// 使用 https://github.com/spa5k/is_sudo/blob/main/src/window.rs +use std::io::Error; +use std::ptr; + +use winapi::um::handleapi::CloseHandle; +use winapi::um::processthreadsapi::{GetCurrentProcess, OpenProcessToken}; +use winapi::um::securitybaseapi::GetTokenInformation; +use winapi::um::winnt::{HANDLE, TOKEN_ELEVATION, TOKEN_QUERY, TokenElevation}; + +// Use std::io::Error::last_os_error for errors. +// NOTE: For this example I'm simple passing on the OS error. +// However, customising the error could provide more context + +/// Returns true if the current process has admin rights, otherwise false. +pub fn is_app_elevated() -> bool { + _is_app_elevated().unwrap_or(false) +} + +/// On success returns a bool indicating if the current process has admin rights. +/// Otherwise returns an OS error. +/// +/// This is unlikely to fail but if it does it's even more unlikely that you have admin permissions anyway. +/// Therefore the public function above simply eats the error and returns a bool. +fn _is_app_elevated() -> Result { + let token = QueryAccessToken::from_current_process()?; + token.is_elevated() +} + +/// A safe wrapper around querying Windows access tokens. +pub struct QueryAccessToken(HANDLE); + +impl QueryAccessToken { + pub fn from_current_process() -> Result { + unsafe { + let mut handle: HANDLE = ptr::null_mut(); + let result = OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &mut handle); + + if result != 0 { + Ok(Self(handle)) + } else { + Err(Error::last_os_error()) + } + } + } + + /// On success returns a bool indicating if the access token has elevated privilidges. + /// Otherwise returns an OS error. + pub fn is_elevated(&self) -> Result { + unsafe { + let mut elevation = TOKEN_ELEVATION::default(); + let size = std::mem::size_of::() as u32; + let mut ret_size = size; + // The weird looking repetition of `as *mut _` is casting the reference to a c_void pointer. + if GetTokenInformation( + self.0, + TokenElevation, + &mut elevation as *mut _ as *mut _, + size, + &mut ret_size, + ) != 0 + { + Ok(elevation.TokenIsElevated != 0) + } else { + Err(Error::last_os_error()) + } + } + } +} + +impl Drop for QueryAccessToken { + fn drop(&mut self) { + if !self.0.is_null() { + unsafe { CloseHandle(self.0) }; + } + } +} diff --git a/src/error/mod.rs b/src/error/mod.rs new file mode 100644 index 0000000..7d46c90 --- /dev/null +++ b/src/error/mod.rs @@ -0,0 +1,24 @@ +use std::io; + +use crossbeam::channel::RecvError; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum Error { + #[error("packet error")] + PacketError(#[from] packet::error::Error), + #[error("Io error")] + Io(#[from] io::Error), + #[error("Channel error")] + Channel(#[from] RecvError), + #[error("Protobuf error")] + Protobuf(#[from] protobuf::Error), + #[error("Invalid packet")] + InvalidPacket, + #[error("Not support")] + NotSupport, + #[error("Stop")] + Stop(String), +} + +pub type Result = std::result::Result; diff --git a/src/handle/heartbeat_handler.rs b/src/handle/heartbeat_handler.rs new file mode 100644 index 0000000..a97e1bc --- /dev/null +++ b/src/handle/heartbeat_handler.rs @@ -0,0 +1,45 @@ +use std::net::{SocketAddr, UdpSocket}; +use std::thread; +use std::time::Duration; + +use chrono::Local; + +use crate::error::*; +use crate::handle::DIRECT_ROUTE_TABLE; +use crate::protocol::control_packet::PingPacket; +use crate::protocol::{control_packet, NetPacket, Protocol, Version}; +use crate::DEVICE_LIST; + +pub fn handle_loop(udp: UdpSocket, server_addr: SocketAddr) -> Result<()> { + const INTERVAL: u64 = 3000; + const MAX_INTERVAL: i64 = 3000 * 5; + let mut buf = [0u8; (4 + 8 + 4)]; + let mut net_packet = NetPacket::new(&mut buf)?; + net_packet.set_version(Version::V1); + net_packet.set_protocol(Protocol::Control); + net_packet.set_transport_protocol(control_packet::Protocol::Ping.into()); + net_packet.set_ttl(255); + loop { + let current_time = Local::now().timestamp(); + { + let mut ping = PingPacket::new(net_packet.payload_mut())?; + ping.set_time(current_time); + let epoch = { DEVICE_LIST.lock().0 }; + ping.set_epoch(epoch); + } + udp.send_to(net_packet.buffer(), server_addr)?; + for x in DIRECT_ROUTE_TABLE.iter() { + let virtual_ip = x.key().clone(); + let route = x.value().clone(); + drop(x); + if current_time - route.recv_time <= MAX_INTERVAL { + udp.send_to(net_packet.buffer(), route.address)?; + } else { + DIRECT_ROUTE_TABLE.remove_if(&virtual_ip, |_, route| { + current_time - route.recv_time > MAX_INTERVAL + }); + } + } + thread::sleep(Duration::from_millis(INTERVAL)); + } +} diff --git a/src/handle/mod.rs b/src/handle/mod.rs new file mode 100644 index 0000000..7554c6b --- /dev/null +++ b/src/handle/mod.rs @@ -0,0 +1,132 @@ +use std::net::{ Ipv4Addr, SocketAddr}; +use std::sync::atomic::AtomicI64; +use std::time::Duration; + +use chrono::Local; +use dashmap::DashMap; +use lazy_static::lazy_static; +use moka::sync::Cache; +use parking_lot::{const_mutex, Mutex}; + +use crate::proto::message::NatType; + +pub mod heartbeat_handler; +pub mod punch_handler; +pub mod registration_handler; +pub mod tun_handler; +pub mod udp_recv_handler; +lazy_static! { + /// 0. 机器纪元,每一次上线或者下线都会增1,由服务端维护,用于感知网络中机器变化 + /// 服务端和客户端的不一致,则服务端会推送新的设备列表 + /// 1. 网络中的虚拟ip列表 + pub static ref DEVICE_LIST:Mutex<(u32,Vec)> = const_mutex((0,Vec::new())); + /// 服务器延迟 + pub static ref SERVER_RT:AtomicI64 = AtomicI64::new(-1); + /// id + pub static ref ID:AtomicI64 = AtomicI64::new(0); + /// 直连路由表 + pub static ref DIRECT_ROUTE_TABLE:DashMap = DashMap::new(); + /// 地址映射 + pub static ref ADDR_TABLE:Cache = Cache::builder() + .time_to_idle(Duration::from_secs(60*5)).build(); + /// 当前设备的nat信息 + pub static ref NAT_INFO:Mutex> = const_mutex(None); +} + +#[derive(Clone, Debug)] +pub struct NatInfo { + public_ips: Vec, + public_port: u16, + public_port_range: u16, + nat_type: NatType, +} + +impl NatInfo { + pub fn new(public_ips: Vec, + public_port: u16, + public_port_range: u16, + nat_type: NatType, ) -> Self { + Self { + public_ips, + public_port, + public_port_range, + nat_type, + } + } +} + +/// 初始化nat信息 +pub fn init_nat_info(public_ip: u32, public_port: u16) { + match crate::nat::check::public_ip_list() { + Ok((nat_type, ips, port_range)) => { + let mut public_ips = Vec::new(); + public_ips.push(public_ip); + for ip in ips { + let ip = u32::from_be_bytes(ip.octets()); + if ip != public_ip { + public_ips.push(ip); + } + } + let nat_info = NatInfo::new(public_ips, + public_port, + port_range, nat_type); + // println!("nat信息:{:?}",nat_info); + let mut nat_info_lock = NAT_INFO.lock(); + nat_info_lock.replace(nat_info); + } + Err(e) => { + println!("获取nat数据失败,将无法进行udp打洞:{:?}", e); + } + } +} + +#[derive(Clone, Debug)] +pub struct CurrentDeviceInfo { + virtual_ip: Ipv4Addr, + virtual_gateway: Ipv4Addr, + virtual_netmask: Ipv4Addr, + //网络地址 + virtual_network: Ipv4Addr, + //直接广播地址 + broadcast_address: Ipv4Addr, + //链接的服务器地址 + connect_server: SocketAddr, +} + +impl CurrentDeviceInfo { + pub fn new(virtual_ip: Ipv4Addr, virtual_gateway: Ipv4Addr, virtual_netmask: Ipv4Addr, connect_server: SocketAddr) -> Self { + let broadcast_address = (!u32::from_be_bytes(virtual_netmask.octets())) + | u32::from_be_bytes(virtual_gateway.octets()); + let broadcast_address = Ipv4Addr::from(broadcast_address); + let virtual_network = u32::from_be_bytes(virtual_netmask.octets()) + & u32::from_be_bytes(virtual_gateway.octets()); + let virtual_network = Ipv4Addr::from(virtual_network); + Self { + virtual_ip, + virtual_netmask, + virtual_gateway, + virtual_network, + broadcast_address, + connect_server, + } + } +} + +#[derive(Clone,Debug)] +pub struct Route { + address: SocketAddr, + //用心跳探测延迟,收包时更新 + delay: i64, + //收包时更新,如果太久没有收到消息则剔除 + recv_time: i64, +} + +impl Route { + pub fn new(address: SocketAddr) -> Self { + Self { + address, + delay: -1, + recv_time: Local::now().timestamp(), + } + } +} diff --git a/src/handle/punch_handler.rs b/src/handle/punch_handler.rs new file mode 100644 index 0000000..93d5226 --- /dev/null +++ b/src/handle/punch_handler.rs @@ -0,0 +1,354 @@ +use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, UdpSocket}; +use std::thread; +use std::time::Duration; + +use crossbeam::channel::{Receiver, RecvTimeoutError, Sender, SendError, TrySendError}; +use dashmap::DashMap; +use lazy_static::lazy_static; +use protobuf::Message; + +use crate::{CurrentDeviceInfo, DEVICE_LIST, NAT_INFO, NatInfo}; +use crate::error::*; +use crate::handle::DIRECT_ROUTE_TABLE; +use crate::proto::message::{NatType, Punch, Step}; +use crate::protocol::{control_packet, NetPacket, Protocol, turn_packet, Version}; +use crate::protocol::control_packet::PunchRequestPacket; +use crate::protocol::turn_packet::TurnPacket; + +lazy_static! { + pub static ref STEP_MAP:DashMap = DashMap::new(); +} +/// 每一种类型一个通道,减少相互干扰 +pub fn bounded() -> (PunchSender, ConeReceiver, ReqSymmetricReceiver, ResSymmetricReceiver) { + let (cone_sender, cone_receiver) = crossbeam::channel::bounded(3); + let (req_symmetric_sender, req_symmetric_receiver) = crossbeam::channel::bounded(1); + let (res_symmetric_sender, res_symmetric_receiver) = crossbeam::channel::bounded(1); + (PunchSender::new(cone_sender, req_symmetric_sender, res_symmetric_sender), + ConeReceiver(cone_receiver), ReqSymmetricReceiver(req_symmetric_receiver), + ResSymmetricReceiver(res_symmetric_receiver)) +} + +pub struct ConeReceiver(Receiver); + +pub struct ReqSymmetricReceiver(Receiver); + +pub struct ResSymmetricReceiver(Receiver); + +#[derive(Clone)] +pub struct PunchSender { + cone_sender: Sender, + req_symmetric_sender: Sender, + res_symmetric_sender: Sender, +} + +impl PunchSender { + pub fn new(cone_sender: Sender, + req_symmetric_sender: Sender, + res_symmetric_sender: Sender, ) -> Self { + Self { + cone_sender, + req_symmetric_sender, + res_symmetric_sender, + } + } + pub fn send(&self, punch: Punch) -> std::result::Result<(), SendError> { + match punch.nat_type.enum_value_or_default() { + NatType::Symmetric => { + if punch.reply { + // 为true表示回应,也就是主动发起的打洞操作 + self.res_symmetric_sender.send(punch) + } else { + self.req_symmetric_sender.send(punch) + } + } + NatType::Cone => { + self.cone_sender.send(punch) + } + } + } + pub fn try_send(&self, punch: Punch) -> std::result::Result<(), TrySendError> { + match punch.nat_type.enum_value_or_default() { + NatType::Symmetric => { + if punch.reply { + // 为true表示回应,也就是主动发起的打洞操作 + self.res_symmetric_sender.try_send(punch) + } else { + self.req_symmetric_sender.try_send(punch) + } + } + NatType::Cone => { + self.cone_sender.try_send(punch) + } + } + } +} + +fn handle(udp: &UdpSocket, punch_list: Vec, buf: &[u8]) -> Result<()> { + let mut counter = 0u64; + for punch in punch_list { + let dest = Ipv4Addr::from(punch.virtual_ip); + if DIRECT_ROUTE_TABLE.contains_key(&dest) { + continue; + } + // println!("punch {:?}", punch); + match punch.nat_type.enum_value_or_default() { + NatType::Symmetric => { + match punch.step.enum_value_or_default() { + Step::Step1 | Step::Step2 | Step::Step3 => { + //预测范围发送 + for pub_ip in punch.public_ip_list { + let pub_ip = Ipv4Addr::from(pub_ip); + for range in 0..punch.public_port_range + 1 { + let right_port = ((punch.public_port + range) & 0xFFFF) as u16; + let left_port = ((0xFFFF + punch.public_port - range) & 0xFFFF) as u16; + if right_port != 0 { + // println!("{:?}", SocketAddr::V4(SocketAddrV4::new(pub_ip, right_port))); + udp.send_to( + buf, + SocketAddr::V4(SocketAddrV4::new(pub_ip, right_port)), + )?; + select_sleep(&mut counter); + } + if left_port != 0 && range != 0 { + // println!("{:?}", SocketAddr::V4(SocketAddrV4::new(pub_ip, right_port))); + if left_port == right_port { + break; + } + udp.send_to( + buf, + SocketAddr::V4(SocketAddrV4::new(pub_ip, left_port)), + )?; + select_sleep(&mut counter); + } + } + } + } + Step::Step4 => { + //全范围发送 + for pub_ip in punch.public_ip_list { + let pub_ip = Ipv4Addr::from(pub_ip); + for port in 1..0xFFFF { + udp.send_to( + buf, + SocketAddr::V4(SocketAddrV4::new(pub_ip, port)), + )?; + select_sleep(&mut counter); + } + } + } + } + } + NatType::Cone => { + for pub_ip in punch.public_ip_list { + udp.send_to( + buf, + SocketAddr::V4(SocketAddrV4::new( + Ipv4Addr::from(pub_ip), + punch.public_port as u16, + )), + )?; + select_sleep(&mut counter); + } + } + } + } + Ok(()) +} + +/// 给对称nat发送打洞数据包 +pub fn req_symmetric_handle_loop( + receiver: ReqSymmetricReceiver, + udp: UdpSocket, + cur_info: CurrentDeviceInfo, +) -> Result<()> { + let receiver = receiver.0; + handle_loop(receiver, udp, cur_info) +} + +/// 给对称nat发送打洞数据包,处理主动发起的打洞操作 +pub fn res_symmetric_handle_loop( + receiver: ResSymmetricReceiver, + udp: UdpSocket, + cur_info: CurrentDeviceInfo, +) -> Result<()> { + let receiver = receiver.0; + let mut buf = [0u8; 12]; + let mut packet = NetPacket::new(&mut buf)?; + packet.set_version(Version::V1); + packet.set_ttl(255); + packet.set_protocol(Protocol::Control); + packet.set_transport_protocol(control_packet::Protocol::PunchRequest.into()); + { + let mut punch_packet = PunchRequestPacket::new(packet.payload_mut())?; + punch_packet.set_source(cur_info.virtual_ip); + } + loop { + match receiver.recv_timeout(Duration::from_secs(30)) { + Ok(punch) => { + let mut list = Vec::new(); + list.push(punch); + loop { + match receiver.try_recv() { + Ok(punch) => { + list.push(punch); + } + Err(_) => { + break; + } + } + } + for punch in &list { + let dest = Ipv4Addr::from(punch.virtual_ip); + match punch.step.enum_value_or_default() { + Step::Step1 => { + STEP_MAP.insert(dest, Step::Step2); + } + Step::Step2 => { + STEP_MAP.insert(dest, Step::Step3); + } + Step::Step3 => { + STEP_MAP.insert(dest, Step::Step4); + } + Step::Step4 => { + STEP_MAP.insert(dest, Step::Step1); + } + } + } + if let Err(e) = handle(&udp, list, packet.buffer()) { + println!("{:?}", e); + } + } + Err(RecvTimeoutError::Timeout) => { + punch_request_handle(&udp, &cur_info)?; + } + Err(_) => { + return Err(Error::Stop("打洞线程通道关闭".to_string())); + } + } + } +} + +/// 给锥形nat发送打洞数据包 +pub fn cone_handle_loop( + receiver: ConeReceiver, + udp: UdpSocket, + cur_info: CurrentDeviceInfo, +) -> Result<()> { + let receiver = receiver.0; + handle_loop(receiver, udp, cur_info) +} + +pub fn handle_loop( + receiver: Receiver, + udp: UdpSocket, + cur_info: CurrentDeviceInfo, +) -> Result<()> { + let mut buf = [0u8; 12]; + let mut packet = NetPacket::new(&mut buf)?; + packet.set_version(Version::V1); + packet.set_ttl(255); + packet.set_protocol(Protocol::Control); + packet.set_transport_protocol(control_packet::Protocol::PunchRequest.into()); + { + let mut punch_packet = PunchRequestPacket::new(packet.payload_mut())?; + punch_packet.set_source(cur_info.virtual_ip); + } + loop { + match receiver.recv() { + Ok(punch) => { + let mut list = Vec::new(); + list.push(punch); + loop { + match receiver.try_recv() { + Ok(punch) => { + list.push(punch); + } + Err(_) => { + break; + } + } + } + if let Err(e) = handle(&udp, list, packet.buffer()) { + println!("{:?}", e); + } + } + Err(_) => { + return Err(Error::Stop("打洞线程通道关闭".to_string())); + } + } + } +} + +fn select_sleep(counter: &mut u64) { + *counter += 1; + thread::sleep(Duration::from_millis(1)); + // if *counter > 1 { + // if cone_nat { + // thread::sleep(Duration::from_millis(2)); + // } else { + // if (*counter) & 10 == 10 { + // thread::sleep(Duration::from_millis(1)); + // } + // } + // } +} + + +fn punch_request_handle(udp: &UdpSocket, cur_info: &CurrentDeviceInfo) -> Result<()> { + let nat_info_lock = NAT_INFO.lock(); + let nat_info = nat_info_lock.clone(); + drop(nat_info_lock); + if let Some(nat_info) = nat_info { + if let Err(e) = send_punch(&udp, + &cur_info, + nat_info) { + println!("发送打洞数据失败 :{:?}", e); + } + Ok(()) + } else { + Err(Error::Stop("未初始化nat信息".to_string())) + } +} + + +fn send_punch(udp: &UdpSocket, cur_info: &CurrentDeviceInfo, nat_info: NatInfo) -> Result<()> { + let lock = DEVICE_LIST.lock(); + let list = lock.1.clone(); + drop(lock); + for ip in list { + //只向ip比自己大的发起打洞,避免双方同时发起打洞浪费流量 + if ip > cur_info.virtual_ip && !DIRECT_ROUTE_TABLE.contains_key(&ip) { + let step = if let Some(step) = STEP_MAP.get(&ip) { + *step + } else { + Step::Step1 + }; + let bytes = punch_packet(cur_info.virtual_ip, + nat_info.clone(), ip, step)?; + udp.send_to(&bytes, cur_info.connect_server)?; + } + } + Ok(()) +} + +fn punch_packet(virtual_ip: Ipv4Addr, nat_info: NatInfo, dest: Ipv4Addr, step: Step) -> Result> { + let mut punch_reply = Punch::new(); + punch_reply.reply = false; + punch_reply.virtual_ip = u32::from_be_bytes(virtual_ip.octets()); + punch_reply.step = protobuf::EnumOrUnknown::new(step); + punch_reply.public_ip_list = nat_info.public_ips; + punch_reply.public_port = nat_info.public_port as u32; + punch_reply.public_port_range = nat_info.public_port_range as u32; + punch_reply.nat_type = protobuf::EnumOrUnknown::new(nat_info.nat_type); + let bytes = punch_reply.write_to_bytes()?; + let mut net_packet = NetPacket::new(vec![0u8; 4 + 8 + bytes.len()])?; + net_packet.set_version(Version::V1); + net_packet.set_protocol(Protocol::OtherTurn); + net_packet.set_transport_protocol(turn_packet::Protocol::Punch.into()); + net_packet.set_ttl(255); + let mut turn_packet = TurnPacket::new(net_packet.payload_mut())?; + turn_packet.set_source(virtual_ip); + turn_packet.set_destination(dest); + turn_packet.set_payload(&bytes); + Ok(net_packet.into_buffer()) +} diff --git a/src/handle/registration_handler.rs b/src/handle/registration_handler.rs new file mode 100644 index 0000000..b0d195e --- /dev/null +++ b/src/handle/registration_handler.rs @@ -0,0 +1,113 @@ +use std::io; +use std::net::{SocketAddr, UdpSocket}; +use std::sync::atomic::{AtomicI64, Ordering}; +use std::time::Duration; + +use chrono::Local; +use parking_lot::RwLock; +use protobuf::Message; + +use crate::error::*; +use crate::proto::message::{RegistrationRequest, RegistrationResponse}; +use crate::protocol::{error_packet, NetPacket, Protocol, service_packet, Version}; + +lazy_static::lazy_static! { + static ref REQUEST:RwLock> = parking_lot::const_rwlock(None); + static ref REGISTRATION_TIME:AtomicI64=AtomicI64::new(0); +} + +///向中继服务器注册,token标识一个虚拟网关,mac_address防止多次注册时得到的ip不一致 +pub fn registration( + udp: &UdpSocket, + server_address: SocketAddr, + token: String, + mac_address: String, +) -> Result { + // todo 和服务器通信加密 + let request_packet = registration_request_packet(token.clone(), mac_address.clone())?; + let buf = request_packet.buffer(); + let mut counter = 0; + let mut recv_buf = [0u8; 10240]; + udp.set_read_timeout(Some(Duration::from_millis(500)))?; + loop { + counter += 1; + if counter & 10 == 10 { + return Err(Error::Stop("注册请求超时".to_string())); + } + udp.send_to(buf, server_address)?; + let (len, addr) = match udp.recv_from(&mut recv_buf) { + Ok(ok) => ok, + Err(e) => { + if e.kind() == io::ErrorKind::WouldBlock || e.kind() == io::ErrorKind::TimedOut { + continue; + } + return Err(Error::Io(e)); + } + }; + if server_address != addr { + continue; + } + let net_packet = NetPacket::new(&recv_buf[..len])?; + match net_packet.protocol() { + Protocol::Service => { + match service_packet::Protocol::from(net_packet.transport_protocol()) { + service_packet::Protocol::RegistrationResponse => { + let response = + RegistrationResponse::parse_from_bytes(net_packet.payload())?; + let _ = REQUEST.write().replace((token, mac_address)); + udp.set_read_timeout(None)?; + return Ok(response); + } + _ => {} + } + } + Protocol::Error => { + match error_packet::Protocol::from(net_packet.transport_protocol()) { + error_packet::Protocol::TokenError => { + return Err(Error::Stop("token错误".to_string())); + } + _ => {} + } + } + _ => {} + } + } +} + +fn registration_request_packet(token: String, mac_address: String) -> Result>> { + let mut request = RegistrationRequest::new(); + request.token = token; + request.mac_address = mac_address; + let bytes = request.write_to_bytes()?; + let buf = vec![0u8; 4 + bytes.len()]; + let mut net_packet = NetPacket::new(buf)?; + net_packet.set_version(Version::V1); + net_packet.set_protocol(Protocol::Service); + net_packet.set_transport_protocol(service_packet::Protocol::RegistrationRequest.into()); + net_packet.set_ttl(255); + net_packet.set_payload(&bytes); + Ok(net_packet) +} + +pub fn fast_registration(udp: &UdpSocket, server_address: SocketAddr) -> Result<()> { + let last = REGISTRATION_TIME.load(Ordering::Relaxed); + let new = Local::now().timestamp(); + if new - last < 2000 + || REGISTRATION_TIME + .compare_exchange(last, new, Ordering::Relaxed, Ordering::Relaxed) + .is_err() + { + //短时间不重复注册 + return Ok(()); + } + let lock = REQUEST.read(); + let option = lock.clone(); + drop(lock); + if let Some((token, mac_address)) = option { + let request_packet = registration_request_packet(token, mac_address)?; + udp.send_to(request_packet.buffer(), server_address)?; + REGISTRATION_TIME.store(Local::now().timestamp(), Ordering::Relaxed); + return Ok(()); + } + return Err(Error::Stop("注册信息不存在".to_string())); +} diff --git a/src/handle/tun_handler.rs b/src/handle/tun_handler.rs new file mode 100644 index 0000000..140f8d1 --- /dev/null +++ b/src/handle/tun_handler.rs @@ -0,0 +1,127 @@ +/// 接收tun数据,并且转发到udp上 +use std::net::{IpAddr, Ipv4Addr, UdpSocket}; + +use chrono::Local; +use packet::icmp::icmp::IcmpPacket; +use packet::icmp::Kind; +use packet::ip::ipv4; +use packet::ip::ipv4::packet::IpV4Packet; + +use crate::error::*; +use crate::handle::{CurrentDeviceInfo, DIRECT_ROUTE_TABLE}; +use crate::protocol::{NetPacket, Protocol, Version}; +use crate::protocol::turn_packet::TurnPacket; +use crate::tun_device::TunReader; + +/// 是否在一个网段 +fn check_dest(dest: Ipv4Addr, cur_info: &CurrentDeviceInfo) -> bool { + u32::from_be_bytes(dest.octets()) & u32::from_be_bytes(cur_info.virtual_netmask.octets()) + == u32::from_be_bytes(cur_info.virtual_network.octets()) +} + +fn icmp(udp: &UdpSocket, mut ipv4_packet: IpV4Packet<&mut [u8]>) -> Result<()> { + if ipv4_packet.protocol() == ipv4::protocol::Protocol::Icmp { + let mut icmp = IcmpPacket::new(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(); + let mut addr = udp.local_addr()?; + addr.set_ip(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))); + udp.send_to(ipv4_packet.buffer, addr)?; + } + } + Ok(()) +} + +#[inline] +fn handle( + udp: &UdpSocket, + data: &mut [u8], + cur_info: &CurrentDeviceInfo, + net_packet: &mut NetPacket>, +) -> Result<()> { + let data_len = data.len(); + let ipv4_packet = match IpV4Packet::new(data) { + Ok(ipv4_packet) => ipv4_packet, + Err(packet::error::Error::Unimplemented) => { + return Ok(()); + } + Err(e) => Err(e)?, + }; + let src_ip = ipv4_packet.source_ip(); + let dest_ip = ipv4_packet.destination_ip(); + // if dest_ip == cur_info.broadcast_address { + // // 启动服务后会收到对137端口的广播 + // // 137端口是在局域网中提供计算机的名字或IP地址查询服务 + // return Ok(()); + // } + if src_ip != cur_info.virtual_ip || !check_dest(dest_ip, &cur_info) { + return Ok(()); + } + if src_ip == dest_ip { + return icmp(&udp, ipv4_packet); + } + let mut ipv4_turn_packet = TurnPacket::new(net_packet.payload_mut())?; + ipv4_turn_packet.set_source(src_ip); + ipv4_turn_packet.set_destination(dest_ip); + ipv4_turn_packet.set_payload(ipv4_packet.buffer); + //优先发到直连到地址 + if let Some(route) = DIRECT_ROUTE_TABLE.get(&dest_ip) { + let current_time = Local::now().timestamp(); + if current_time - route.recv_time < 3_000 { + udp.send_to(&net_packet.buffer()[..(4 + 8 + data_len)], route.address)?; + return Ok(()); + } + } + udp.send_to(&net_packet.buffer()[..(4 + 8 + data_len)], cur_info.connect_server)?; + return Ok(()); +} + +#[cfg(target_os = "windows")] +pub fn handle_loop( + udp: UdpSocket, + tun_reader: TunReader, + cur_info: CurrentDeviceInfo, +) -> Result<()> { + let mut net_packet = NetPacket::new(vec![0u8; 4 + 8 + 1500])?; + 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(255); + loop { + let mut data = tun_reader.next()?; + match handle(&udp, data.bytes_mut(), &cur_info, &mut net_packet) { + Ok(_) => {} + Err(e) => { + println!("{:?}", e) + } + } + } +} + +#[cfg(any(unix))] +pub fn handle_loop( + udp: UdpSocket, + mut tun_reader: TunReader, + cur_info: CurrentDeviceInfo, +) -> Result<()> { + let mut net_packet = NetPacket::new(vec![0u8; 4 + 8 + 1500])?; + net_packet.set_version(Version::V1); + net_packet.set_protocol(Protocol::Ipv4Turn); + net_packet.set_transport_protocol(0); + net_packet.set_ttl(255); + let mut buf = [0u8; 1500]; + loop { + let data = tun_reader.read(&mut buf)?; + match handle(&udp, data, &cur_info, &mut net_packet) { + Ok(_) => {} + Err(e) => { + println!("{:?}", e) + } + } + } +} \ No newline at end of file diff --git a/src/handle/udp_recv_handler.rs b/src/handle/udp_recv_handler.rs new file mode 100644 index 0000000..d7d2831 --- /dev/null +++ b/src/handle/udp_recv_handler.rs @@ -0,0 +1,299 @@ +use std::net::{Ipv4Addr, SocketAddr, UdpSocket}; +use std::sync::atomic::Ordering; + +use chrono::Local; +use crossbeam::channel::{Receiver, Sender, TrySendError}; +use packet::icmp::{icmp, Kind}; +use packet::ip::ipv4; +use packet::ip::ipv4::packet::IpV4Packet; +use protobuf::Message; + +use crate::CurrentDeviceInfo; +use crate::error::*; +use crate::handle::{ADDR_TABLE, DEVICE_LIST, DIRECT_ROUTE_TABLE, NAT_INFO, Route, SERVER_RT}; +use crate::handle::punch_handler::PunchSender; +use crate::handle::registration_handler::fast_registration; +use crate::proto::message::{DeviceList, Punch, RegistrationResponse}; +use crate::protocol::{control_packet, NetPacket, Protocol, service_packet, turn_packet, Version}; +use crate::protocol::control_packet::{ControlPacket, PunchResponsePacket}; +use crate::protocol::error_packet::InErrorPacket; +use crate::protocol::turn_packet::TurnPacket; +use crate::tun_device::TunWriter; + +pub fn recv_loop( + udp: UdpSocket, + server_addr: SocketAddr, + other_sender: Sender<(SocketAddr, Vec)>, + mut tun_writer: TunWriter, + current_device: CurrentDeviceInfo, +) -> Result<()> { + let mut buf = [0u8; 65536]; + let mut local_addr = udp.local_addr()?; + local_addr.set_ip(std::net::IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))); + loop { + match udp.recv_from(&mut buf) { + Ok((len, addr)) => { + if addr == local_addr { + //本地的包直接再发到网卡,这个主要用于处理当前虚拟ip的icmp ping + if let Ok(ip) = IpV4Packet::new(&buf[..len]) { + if ip.destination_ip() == current_device.virtual_ip { + let _ = tun_writer.write(&buf[..len]); + } + } + continue; + } + match recv_handle( + &udp, + addr, + &mut buf[..len], + &server_addr, + &other_sender, + &mut tun_writer, + ¤t_device, + ) { + Ok(_) => {} + Err(Error::Stop(str)) => { + return Err(Error::Stop(str)); + } + Err(_) => {} + } + } + Err(e) => { + println!("{:?}", e); + } + }; + } +} + +fn recv_handle( + udp: &UdpSocket, + recv_addr: SocketAddr, + buf: &mut [u8], + _server_addr: &SocketAddr, + other_sender: &Sender<(SocketAddr, Vec)>, + tun_writer: &mut TunWriter, + current_device: &CurrentDeviceInfo, +) -> Result<()> { + let mut net_packet = NetPacket::new(buf)?; + match net_packet.protocol() { + Protocol::Ipv4Turn => { + let mut ipv4_turn_packet = TurnPacket::new(net_packet.payload_mut())?; + let source = ipv4_turn_packet.source(); + let destination = ipv4_turn_packet.destination(); + let mut ipv4 = IpV4Packet::new(ipv4_turn_packet.payload_mut())?; + if ipv4.source_ip() == source + && ipv4.destination_ip() == destination + && current_device.virtual_ip == ipv4.destination_ip() + { + if ipv4.protocol() == ipv4::protocol::Protocol::Icmp { + let mut icmp_packet = icmp::IcmpPacket::new(ipv4.payload_mut())?; + if icmp_packet.kind() == Kind::EchoRequest { + //开启ping + icmp_packet.set_kind(Kind::EchoReply); + icmp_packet.update_checksum(); + ipv4.set_source_ip(destination); + ipv4.set_destination_ip(source); + ipv4.update_checksum(); + ipv4_turn_packet.set_source(destination); + ipv4_turn_packet.set_destination(source); + udp.send_to(net_packet.buffer(), recv_addr)?; + } else { + tun_writer.write(ipv4_turn_packet.payload())?; + } + } else { + tun_writer.write(ipv4_turn_packet.payload())?; + } + } + } + Protocol::UnKnow(_) => {} + _ => { + //发送到子线程处理 + let v = net_packet.buffer().to_vec(); + match other_sender.try_send((recv_addr, v)) { + Ok(_) => {} + Err(TrySendError::Disconnected(_)) => { + return Err(Error::Stop("处理线程停止".to_string())); + } + Err(e) => { + println!("子线程处理 :{:?}", e); + } + } + } + } + Ok(()) +} + +pub fn other_loop( + udp: UdpSocket, + receiver: Receiver<(SocketAddr, Vec)>, + current_device: CurrentDeviceInfo, + sender: PunchSender, +) -> Result<()> { + loop { + let (peer_addr, buf) = receiver.recv()?; + match other_handle(&udp, buf, peer_addr, ¤t_device, &sender) { + Ok(_) => {} + Err(Error::Stop(str)) => { + return Err(Error::Stop(str)); + } + Err(e) => { + println!("{:?}", e) + } + } + } +} + +fn other_handle( + udp: &UdpSocket, + buf: Vec, + peer_addr: SocketAddr, + current_device: &CurrentDeviceInfo, + sender: &PunchSender, +) -> Result<()> { + let server_addr = current_device.connect_server; + let mut net_packet = NetPacket::new(buf)?; + match net_packet.protocol() { + Protocol::Service => { + if peer_addr != current_device.connect_server { + return Ok(()); + } + match service_packet::Protocol::from(net_packet.transport_protocol()) { + service_packet::Protocol::RegistrationRequest => {} + service_packet::Protocol::RegistrationResponse => { + let response = RegistrationResponse::parse_from_bytes(net_packet.payload())?; + crate::handle::init_nat_info(response.public_ip, response.public_port as u16); + //todo 重连之后ip可能会发生改变(目前2分钟内未重连则会释放ip),需要更新本地ip(或者保证重连ip不变) + } + service_packet::Protocol::UpdateDeviceList => { + let device_list = DeviceList::parse_from_bytes(net_packet.payload())?; + let ip_list: Vec = device_list + .virtual_ip_list + .iter() + .map(|ip| Ipv4Addr::from(*ip)) + .collect(); + let mut dev = DEVICE_LIST.lock(); + if dev.0 < device_list.epoch || device_list.epoch - dev.0 > u32::MAX >> 2 { + dev.0 = device_list.epoch; + dev.1 = ip_list; + } + } + service_packet::Protocol::UnKnow(_) => {} + } + } + Protocol::Error => { + match InErrorPacket::new(net_packet.transport_protocol(), net_packet.payload())? { + InErrorPacket::TokenError => { + if server_addr == peer_addr { + //停止整个应用 + return Err(Error::Stop("token无效".to_string())); + } + } + InErrorPacket::Disconnect => { + if server_addr == peer_addr { + fast_registration(&udp, server_addr)?; + } + } + InErrorPacket::OtherError(e) => { + println!("{:?}", e.message()); + } + } + } + Protocol::Control => { + match ControlPacket::new(net_packet.transport_protocol(), net_packet.payload())? { + ControlPacket::PingPacket(_) => { + net_packet.set_transport_protocol(control_packet::Protocol::Pong.into()); + udp.send_to(&net_packet.buffer()[..12], peer_addr)?; + } + ControlPacket::PongPacket(pong_packet) => { + let current_time = Local::now().timestamp(); + let rt = current_time - pong_packet.time(); + if rt >= 0 { + if peer_addr == server_addr { + SERVER_RT.store(rt, Ordering::Relaxed) + } else { + //其他设备 + if let Some(virtual_ip) = ADDR_TABLE.get(&peer_addr) { + if let Some(mut info) = DIRECT_ROUTE_TABLE.get_mut(&virtual_ip) { + info.delay = rt; + info.recv_time = current_time; + } + } + } + } + } + ControlPacket::PunchRequest(punch_request) => { + // println!("打洞请求:{:?}", punch_request); + let src = punch_request.source(); + drop(punch_request); + //回应 + let mut punch_response = PunchResponsePacket::new(net_packet.payload_mut())?; + punch_response.set_source(current_device.virtual_ip); + net_packet.set_transport_protocol(control_packet::Protocol::PunchResponse.into()); + udp.send_to(net_packet.buffer(), peer_addr)?; + let route = Route::new(peer_addr); + DIRECT_ROUTE_TABLE.insert(src, route); + ADDR_TABLE.insert(peer_addr, src); + } + ControlPacket::PunchResponse(punch_response) => { + // println!("打洞响应:{:?}", punch_response); + let route = Route::new(peer_addr); + DIRECT_ROUTE_TABLE.insert(punch_response.source(), route); + ADDR_TABLE.insert(peer_addr, punch_response.source()); + } + } + } + Protocol::Ipv4Turn => {} + Protocol::OtherTurn => { + let turn_packet = TurnPacket::new(net_packet.payload())?; + // println!("{:?}",turn_packet); + let src = turn_packet.source(); + let dest = turn_packet.destination(); + if dest == current_device.virtual_ip { + match turn_packet::Protocol::from(net_packet.transport_protocol()) { + turn_packet::Protocol::Punch => { + let punch = Punch::parse_from_bytes(turn_packet.payload())?; + if punch.virtual_ip.to_be_bytes() == src.octets() { + if !punch.reply { + let mut punch_reply = Punch::new(); + punch_reply.reply = true; + punch_reply.virtual_ip = u32::from_be_bytes(current_device.virtual_ip.octets()); + punch_reply.step = punch.step; + if let Err(_) = sender.try_send(punch) { + return Ok(()); + } + let nat_info = NAT_INFO.lock(); + if let Some(info) = nat_info.as_ref() { + punch_reply.public_ip_list = info.public_ips.clone(); + punch_reply.public_port = info.public_port as u32; + punch_reply.public_port_range = info.public_port_range as u32; + punch_reply.nat_type = protobuf::EnumOrUnknown::new(info.nat_type); + drop(nat_info); + let bytes = punch_reply.write_to_bytes()?; + let mut net_packet = NetPacket::new(vec![0u8; 4 + 8 + bytes.len()])?; + net_packet.set_version(Version::V1); + net_packet.set_protocol(Protocol::OtherTurn); + net_packet.set_transport_protocol(turn_packet::Protocol::Punch.into()); + net_packet.set_ttl(255); + let mut turn_packet = TurnPacket::new(net_packet.payload_mut())?; + turn_packet.set_source(current_device.virtual_ip); + turn_packet.set_destination(src); + turn_packet.set_payload(&bytes); + udp.send_to(net_packet.buffer(), peer_addr)?; + } + } else { + let _ = sender.try_send(punch); + } + } + } + turn_packet::Protocol::UnKnow(_) => {} + } + } else { + panic!("ip") + } + } + Protocol::UnKnow(p) => { + println!("未知协议:{}", p) + } + } + Ok(()) +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..c28c912 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,185 @@ +use std::{io, thread}; +use std::net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4, UdpSocket}; + +use clap::Parser; +use console::style; +use crossbeam::sync::Parker; + +use crate::handle::{CurrentDeviceInfo, DEVICE_LIST, NAT_INFO, NatInfo}; +use crate::handle::registration_handler::registration; +use crate::tun_device::create_tun; + +pub mod tun_device; +pub mod nat; +pub mod error; +pub mod handle; +pub mod proto; +pub mod protocol; +#[cfg(windows)] +pub mod admin_check; + +#[derive(Parser, Debug)] +#[command(author = "Lu Beilin", version, about = "一个虚拟网络工具,启动后会获取一个ip,相同token下的设备之间可以用ip直接通信")] +struct Args { + /// 32位字符 + /// 相同token的设备之间才能通信。 + /// 建议使用uuid保证唯一性。 + /// 32-bit characters. + /// Only devices with the same token can communicate with each other. + /// It is recommended to use uuid to ensure uniqueness + #[arg(short, long)] + token: String, +} + +fn main() { + let args = Args::parse(); + #[cfg(windows)] + if !admin_check::is_app_elevated() { + let args: Vec<_> = std::env::args().collect(); + println!("{}", style("正在启动管理员权限执行...").red()); + if let Some(absolute_path) = std::env::current_exe() + .ok() + .and_then(|p| p.to_str().map(|p| p.to_string())) + { + let _ = runas::Command::new(&absolute_path).args(&args[1..]).status() + .expect("failed to execute"); + } else { + panic!("failed to execute") + } + return; + } + + #[cfg(any(unix))] + if sudo::RunningAs::Root != sudo::check() { + println!("{}", style("需要使用root权限执行...").red()); + sudo::escalate_if_needed().unwrap(); + } + + println!("{}", style("启动服务...").green()); + + let token = args.token; + // let d = Local::now().timestamp().to_string(); + let mac_address = mac_address::get_mac_address().unwrap().unwrap().to_string(); + let server_address = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(43, 139, 56, 10)), 29876); + // let server_address = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127,0,0,1)), 29876); + let mut port = 101 as u16; + let udp = loop { + match UdpSocket::bind(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::from(0), port))) { + Ok(udp) => { + break udp; + } + Err(e) => { + if e.kind() == io::ErrorKind::AddrInUse { + port += 1; + } else { + println!("创建udp失败:{:?}", e); + panic!() + } + } + } + }; + //注册 + let response = registration(&udp, server_address, token, mac_address).unwrap(); + { + let ip_list = response + .virtual_ip_list + .iter() + .map(|ip| Ipv4Addr::from(*ip)) + .collect(); + let mut dev = DEVICE_LIST.lock(); + dev.0 = response.epoch; + dev.1 = ip_list; + } + let virtual_ip = Ipv4Addr::from(response.virtual_ip); + let virtual_gateway = Ipv4Addr::from(response.virtual_gateway); + let virtual_netmask = Ipv4Addr::from(response.virtual_netmask); + println!("virtual_gateway:{:?}", virtual_gateway); + println!("virtual_netmask:{:?}", virtual_netmask); + println!("当前设备ip(virtual_ip):{}", style(virtual_ip).green()); + let parker = Parker::new(); + //心跳线程 + { + let un_parker = parker.unparker().clone(); + let udp = udp.try_clone().unwrap(); + let _ = thread::spawn(move || { + if let Err(e) = handle::heartbeat_handler::handle_loop(udp, server_address) { + println!("心跳线程停止:{:?}", e); + } + un_parker.unpark(); + }); + } + //初始化nat数据 + handle::init_nat_info(response.public_ip, response.public_port as u16); + // tun服务 + let (tun_writer, tun_reader) = + create_tun(virtual_ip, virtual_netmask, virtual_gateway).unwrap(); + // 打洞数据通道 + let (punch_sender, cone_receiver, req_symmetric_receiver, res_symmetric_receiver) = handle::punch_handler::bounded(); + //udp数据处理 + { + let un_parker = parker.unparker().clone(); + // 低优先级的udp数据通道 + let (sender, receiver) = crossbeam::channel::bounded(100); + let udp1 = udp.try_clone().unwrap(); + let _ = thread::spawn(move || { + let current_device = CurrentDeviceInfo::new(virtual_ip, virtual_gateway, virtual_netmask, server_address); + if let Err(e) = handle::udp_recv_handler::recv_loop( + udp1, + server_address, + sender, + tun_writer, + current_device, + ) { + println!("udp数据处理线程停止:{:?}", e); + } + un_parker.unpark(); + }); + let udp1 = udp.try_clone().unwrap(); + let un_parker = parker.unparker().clone(); + let _ = thread::spawn(move || { + let current_device = CurrentDeviceInfo::new(virtual_ip, virtual_gateway, virtual_netmask, server_address); + if let Err(e) = handle::udp_recv_handler::other_loop(udp1, receiver, current_device, punch_sender) { + println!("udp数据处理线程停止:{:?}", e); + } + un_parker.unpark(); + }); + } + //打洞处理 + { + let udp1 = udp.try_clone().unwrap(); + let _ = thread::spawn(move || { + let current_device = CurrentDeviceInfo::new(virtual_ip, virtual_gateway, virtual_netmask, server_address); + if let Err(e) = handle::punch_handler::cone_handle_loop(cone_receiver, udp1, current_device) { + println!("打洞响应线程停止:{:?}", e); + } + }); + let udp1 = udp.try_clone().unwrap(); + let _ = thread::spawn(move || { + let current_device = CurrentDeviceInfo::new(virtual_ip, virtual_gateway, virtual_netmask, server_address); + if let Err(e) = handle::punch_handler::req_symmetric_handle_loop(req_symmetric_receiver, udp1, current_device) { + println!("打洞触发线程停止:{:?}", e); + } + }); + let udp1 = udp.try_clone().unwrap(); + let _ = thread::spawn(move || { + let current_device = CurrentDeviceInfo::new(virtual_ip, virtual_gateway, virtual_netmask, server_address); + if let Err(e) = handle::punch_handler::res_symmetric_handle_loop(res_symmetric_receiver, udp1, current_device) { + println!("打洞触发线程停止:{:?}", e); + } + }); + } + //tun数据处理 + { + let udp = udp.try_clone().unwrap(); + let un_parker = parker.unparker().clone(); + let _ = thread::spawn(move || { + let current_device = CurrentDeviceInfo::new(virtual_ip, virtual_gateway, virtual_netmask, server_address); + if let Err(e) = handle::tun_handler::handle_loop(udp, tun_reader, current_device) { + println!("tun数据处理线程停止:{:?}", e); + } + un_parker.unpark(); + }); + } + parker.park(); + std::process::exit(1); +} diff --git a/src/nat/check.rs b/src/nat/check.rs new file mode 100644 index 0000000..e042778 --- /dev/null +++ b/src/nat/check.rs @@ -0,0 +1,156 @@ +use std::{io, thread}; +use std::collections::HashSet; +use std::net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket}; +use std::time::Duration; + +use crate::proto::message::NatType; + +// #[derive(Debug, Copy, Clone, PartialEq)] +// pub enum NatType { +// Symmetric, +// Cone, +// } +// +// impl Into for NatType { +// fn into(self) -> u8 { +// match self { +// NatType::Symmetric => 0, +// NatType::Cone => 1, +// } +// } +// } + +/// 返回所有公网ip和端口变化范围 +pub fn public_ip_list() -> io::Result<(NatType, Vec, u16)> { + let mut hash_set = HashSet::new(); + let mut max_port_range = 0; + let mut nat_type = NatType::Cone; + let mut port = 88; + for _ in 0..3 { + let udp = loop { + match UdpSocket::bind(SocketAddr::new(IpAddr::from(Ipv4Addr::from(0)), port)) { + Ok(udp) => { + break udp; + } + Err(e) => { + if e.kind() == io::ErrorKind::AddrInUse { + port += 1; + continue; + } + return Err(e); + } + } + }; + let (set, min_port, max_port) = public_ip_list_(&udp)?; + drop(udp); + let port_range = max_port - min_port; + //有多个ip或者端口有变化,说明是对称nat + if nat_type == NatType::Cone && (set.len() > 1 || port_range != 0) { + nat_type = NatType::Symmetric; + } + if max_port_range < port_range { + max_port_range = port_range; + } + for x in set { + hash_set.insert(x); + } + thread::sleep(Duration::from_micros(5)); + } + Ok((nat_type, hash_set.into_iter().collect(), max_port_range)) +} + +/// 测试样本较少,可能不对 +/// +/// - 移动宽带:锥形网络、一个ip、端口和局域网端口不相同 +/// - 电信宽带:锥形网络、一个ip,端口和局域网端口不相同 +/// - 联调宽带:对称网络、端口不变ip轮流用 +/// - 移动4g:对称网络、ip端口都变 使用小的端口变化量小 +/// - 联通4g:对称网络、只有一个ip 端口变化大 +/// - 电信4g:对称网络只有一个ip 公网端口比较连续 +/// - 综上:客户端使用小端口,针对对称网络 尝试所有ip 公网端口+-变化量的范围 +/// - 打通概率 移动宽带=电信宽带>联调宽带>电信4g>移动4g>>联调4g +pub fn public_ip_list_(udp: &UdpSocket) -> io::Result<(HashSet, u16, u16)> { + // println!("local port {:?}", udp.local_addr().unwrap().port()); + udp.set_read_timeout(Some(Duration::from_millis(300)))?; + let mut buf = [0u8; 128]; + let _ = udp.send_to(b"NatTest", "nat1.wherewego.top:35061")?; + let _ = udp.send_to(b"NatTest", "nat1.wherewego.top:35062")?; + let _ = udp.send_to(b"NatTest", "nat2.wherewego.top:35061")?; + let _ = udp.send_to(b"NatTest", "nat2.wherewego.top:35062")?; + let mut hash_set = HashSet::new(); + let mut count = 0; + let mut min_port = 65535; + let mut max_port = 0; + for _ in 0..4 { + if let Ok(len) = udp.recv(&mut buf) { + if len != 16 || &buf[..10] != &b"NatType213"[..] { + continue; + } + let port = u16::from_be_bytes([buf[14], buf[15]]); + if min_port > port { + min_port = port; + } + if max_port < port { + max_port = port; + } + let ip = Ipv4Addr::new(buf[10], buf[11], buf[12], buf[13]); + // println!("pub {:?}:{}", ip, port); + hash_set.insert(ip); + count += 1; + } + } + if count <= 1 { + return Err(io::Error::from(io::ErrorKind::TimedOut)); + } + Ok((hash_set, min_port, max_port)) +} + +/// 返回nat类型 +pub fn nat_test() -> io::Result { + for _ in 0..3 { + if NatType::Symmetric == nat_test_()? { + return Ok(NatType::Symmetric); + } + thread::sleep(Duration::from_micros(5)); + } + Ok(NatType::Cone) +} + +pub fn nat_test_() -> io::Result { + let udp = UdpSocket::bind("0.0.0.0:0")?; + udp.set_read_timeout(Some(Duration::from_millis(300)))?; + let mut buf = [0u8; 128]; + let _ = udp.send_to(b"NatTest", "nat1.wherewego.top:35061")?; + let _ = udp.send_to(b"NatTest", "nat1.wherewego.top:35062")?; + let _ = udp.send_to(b"NatTest", "nat2.wherewego.top:35061")?; + let _ = udp.send_to(b"NatTest", "nat2.wherewego.top:35062")?; + let mut tmp_ip_port: Option<[u8; 6]> = None; + let mut count = 0; + for _ in 0..4 { + if let Ok(len) = udp.recv(&mut buf) { + if len != 16 || &buf[..10] != &b"NatType213"[..] { + continue; + } + count += 1; + let mut ip_port = [0u8; 6]; + ip_port.copy_from_slice(&buf[10..16]); + if let Some(tmp_ip_port) = &tmp_ip_port { + if tmp_ip_port != &ip_port { + return Ok(NatType::Symmetric); + } + } else { + tmp_ip_port = Some(ip_port); + } + } + } + if count <= 1 { + return Err(io::Error::from(io::ErrorKind::TimedOut)); + } + Ok(NatType::Cone) +} +#[test] +fn nat_test_run(){ + let udp = UdpSocket::bind("0.0.0.0:101").unwrap(); + let print = public_ip_list_(&udp).unwrap(); + println!("{:?}",print); +} \ No newline at end of file diff --git a/src/nat/mod.rs b/src/nat/mod.rs new file mode 100644 index 0000000..1e41ae9 --- /dev/null +++ b/src/nat/mod.rs @@ -0,0 +1 @@ +pub mod check; \ No newline at end of file diff --git a/src/proto/message.rs b/src/proto/message.rs new file mode 100644 index 0000000..07d986b --- /dev/null +++ b/src/proto/message.rs @@ -0,0 +1,941 @@ +// This file is generated by rust-protobuf 3.2.0. Do not edit +// .proto file is parsed by pure +// @generated + +// https://github.com/rust-lang/rust-clippy/issues/702 +#![allow(unknown_lints)] +#![allow(clippy::all)] + +#![allow(unused_attributes)] +#![cfg_attr(rustfmt, rustfmt::skip)] + +#![allow(box_pointers)] +#![allow(dead_code)] +#![allow(missing_docs)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] +#![allow(non_upper_case_globals)] +#![allow(trivial_casts)] +#![allow(unused_results)] +#![allow(unused_mut)] + +//! Generated file from `message.proto` + +/// Generated files are compatible only with the same version +/// of protobuf runtime. +const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_2_0; + +#[derive(PartialEq,Clone,Default,Debug)] +// @@protoc_insertion_point(message:RegistrationRequest) +pub struct RegistrationRequest { + // message fields + // @@protoc_insertion_point(field:RegistrationRequest.token) + pub token: ::std::string::String, + // @@protoc_insertion_point(field:RegistrationRequest.mac_address) + pub mac_address: ::std::string::String, + // special fields + // @@protoc_insertion_point(special_field:RegistrationRequest.special_fields) + pub special_fields: ::protobuf::SpecialFields, +} + +impl<'a> ::std::default::Default for &'a RegistrationRequest { + fn default() -> &'a RegistrationRequest { + ::default_instance() + } +} + +impl RegistrationRequest { + pub fn new() -> RegistrationRequest { + ::std::default::Default::default() + } + + fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData { + let mut fields = ::std::vec::Vec::with_capacity(2); + let mut oneofs = ::std::vec::Vec::with_capacity(0); + fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>( + "token", + |m: &RegistrationRequest| { &m.token }, + |m: &mut RegistrationRequest| { &mut m.token }, + )); + fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>( + "mac_address", + |m: &RegistrationRequest| { &m.mac_address }, + |m: &mut RegistrationRequest| { &mut m.mac_address }, + )); + ::protobuf::reflect::GeneratedMessageDescriptorData::new_2::( + "RegistrationRequest", + fields, + oneofs, + ) + } +} + +impl ::protobuf::Message for RegistrationRequest { + const NAME: &'static str = "RegistrationRequest"; + + fn is_initialized(&self) -> bool { + true + } + + fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> { + while let Some(tag) = is.read_raw_tag_or_eof()? { + match tag { + 10 => { + self.token = is.read_string()?; + }, + 18 => { + self.mac_address = is.read_string()?; + }, + tag => { + ::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?; + }, + }; + } + ::std::result::Result::Ok(()) + } + + // Compute sizes of nested messages + #[allow(unused_variables)] + fn compute_size(&self) -> u64 { + let mut my_size = 0; + if !self.token.is_empty() { + my_size += ::protobuf::rt::string_size(1, &self.token); + } + if !self.mac_address.is_empty() { + my_size += ::protobuf::rt::string_size(2, &self.mac_address); + } + my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields()); + self.special_fields.cached_size().set(my_size as u32); + my_size + } + + fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> { + if !self.token.is_empty() { + os.write_string(1, &self.token)?; + } + if !self.mac_address.is_empty() { + os.write_string(2, &self.mac_address)?; + } + os.write_unknown_fields(self.special_fields.unknown_fields())?; + ::std::result::Result::Ok(()) + } + + fn special_fields(&self) -> &::protobuf::SpecialFields { + &self.special_fields + } + + fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields { + &mut self.special_fields + } + + fn new() -> RegistrationRequest { + RegistrationRequest::new() + } + + fn clear(&mut self) { + self.token.clear(); + self.mac_address.clear(); + self.special_fields.clear(); + } + + fn default_instance() -> &'static RegistrationRequest { + static instance: RegistrationRequest = RegistrationRequest { + token: ::std::string::String::new(), + mac_address: ::std::string::String::new(), + special_fields: ::protobuf::SpecialFields::new(), + }; + &instance + } +} + +impl ::protobuf::MessageFull for RegistrationRequest { + fn descriptor() -> ::protobuf::reflect::MessageDescriptor { + static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::Lazy::new(); + descriptor.get(|| file_descriptor().message_by_package_relative_name("RegistrationRequest").unwrap()).clone() + } +} + +impl ::std::fmt::Display for RegistrationRequest { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + ::protobuf::text_format::fmt(self, f) + } +} + +impl ::protobuf::reflect::ProtobufValue for RegistrationRequest { + type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage; +} + +#[derive(PartialEq,Clone,Default,Debug)] +// @@protoc_insertion_point(message:RegistrationResponse) +pub struct RegistrationResponse { + // message fields + // @@protoc_insertion_point(field:RegistrationResponse.virtual_ip) + pub virtual_ip: u32, + // @@protoc_insertion_point(field:RegistrationResponse.virtual_gateway) + pub virtual_gateway: u32, + // @@protoc_insertion_point(field:RegistrationResponse.virtual_netmask) + pub virtual_netmask: u32, + // @@protoc_insertion_point(field:RegistrationResponse.epoch) + pub epoch: u32, + // @@protoc_insertion_point(field:RegistrationResponse.virtual_ip_list) + pub virtual_ip_list: ::std::vec::Vec, + // @@protoc_insertion_point(field:RegistrationResponse.public_ip) + pub public_ip: u32, + // @@protoc_insertion_point(field:RegistrationResponse.public_port) + pub public_port: u32, + // special fields + // @@protoc_insertion_point(special_field:RegistrationResponse.special_fields) + pub special_fields: ::protobuf::SpecialFields, +} + +impl<'a> ::std::default::Default for &'a RegistrationResponse { + fn default() -> &'a RegistrationResponse { + ::default_instance() + } +} + +impl RegistrationResponse { + pub fn new() -> RegistrationResponse { + ::std::default::Default::default() + } + + fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData { + let mut fields = ::std::vec::Vec::with_capacity(7); + let mut oneofs = ::std::vec::Vec::with_capacity(0); + fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>( + "virtual_ip", + |m: &RegistrationResponse| { &m.virtual_ip }, + |m: &mut RegistrationResponse| { &mut m.virtual_ip }, + )); + fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>( + "virtual_gateway", + |m: &RegistrationResponse| { &m.virtual_gateway }, + |m: &mut RegistrationResponse| { &mut m.virtual_gateway }, + )); + fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>( + "virtual_netmask", + |m: &RegistrationResponse| { &m.virtual_netmask }, + |m: &mut RegistrationResponse| { &mut m.virtual_netmask }, + )); + fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>( + "epoch", + |m: &RegistrationResponse| { &m.epoch }, + |m: &mut RegistrationResponse| { &mut m.epoch }, + )); + fields.push(::protobuf::reflect::rt::v2::make_vec_simpler_accessor::<_, _>( + "virtual_ip_list", + |m: &RegistrationResponse| { &m.virtual_ip_list }, + |m: &mut RegistrationResponse| { &mut m.virtual_ip_list }, + )); + fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>( + "public_ip", + |m: &RegistrationResponse| { &m.public_ip }, + |m: &mut RegistrationResponse| { &mut m.public_ip }, + )); + fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>( + "public_port", + |m: &RegistrationResponse| { &m.public_port }, + |m: &mut RegistrationResponse| { &mut m.public_port }, + )); + ::protobuf::reflect::GeneratedMessageDescriptorData::new_2::( + "RegistrationResponse", + fields, + oneofs, + ) + } +} + +impl ::protobuf::Message for RegistrationResponse { + const NAME: &'static str = "RegistrationResponse"; + + fn is_initialized(&self) -> bool { + true + } + + fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> { + while let Some(tag) = is.read_raw_tag_or_eof()? { + match tag { + 13 => { + self.virtual_ip = is.read_fixed32()?; + }, + 21 => { + self.virtual_gateway = is.read_fixed32()?; + }, + 29 => { + self.virtual_netmask = is.read_fixed32()?; + }, + 32 => { + self.epoch = is.read_uint32()?; + }, + 42 => { + is.read_repeated_packed_fixed32_into(&mut self.virtual_ip_list)?; + }, + 45 => { + self.virtual_ip_list.push(is.read_fixed32()?); + }, + 53 => { + self.public_ip = is.read_fixed32()?; + }, + 56 => { + self.public_port = is.read_uint32()?; + }, + tag => { + ::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?; + }, + }; + } + ::std::result::Result::Ok(()) + } + + // Compute sizes of nested messages + #[allow(unused_variables)] + fn compute_size(&self) -> u64 { + let mut my_size = 0; + if self.virtual_ip != 0 { + my_size += 1 + 4; + } + if self.virtual_gateway != 0 { + my_size += 1 + 4; + } + if self.virtual_netmask != 0 { + my_size += 1 + 4; + } + if self.epoch != 0 { + my_size += ::protobuf::rt::uint32_size(4, self.epoch); + } + my_size += 5 * self.virtual_ip_list.len() as u64; + if self.public_ip != 0 { + my_size += 1 + 4; + } + if self.public_port != 0 { + my_size += ::protobuf::rt::uint32_size(7, self.public_port); + } + my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields()); + self.special_fields.cached_size().set(my_size as u32); + my_size + } + + fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> { + if self.virtual_ip != 0 { + os.write_fixed32(1, self.virtual_ip)?; + } + if self.virtual_gateway != 0 { + os.write_fixed32(2, self.virtual_gateway)?; + } + if self.virtual_netmask != 0 { + os.write_fixed32(3, self.virtual_netmask)?; + } + if self.epoch != 0 { + os.write_uint32(4, self.epoch)?; + } + for v in &self.virtual_ip_list { + os.write_fixed32(5, *v)?; + }; + if self.public_ip != 0 { + os.write_fixed32(6, self.public_ip)?; + } + if self.public_port != 0 { + os.write_uint32(7, self.public_port)?; + } + os.write_unknown_fields(self.special_fields.unknown_fields())?; + ::std::result::Result::Ok(()) + } + + fn special_fields(&self) -> &::protobuf::SpecialFields { + &self.special_fields + } + + fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields { + &mut self.special_fields + } + + fn new() -> RegistrationResponse { + RegistrationResponse::new() + } + + fn clear(&mut self) { + self.virtual_ip = 0; + self.virtual_gateway = 0; + self.virtual_netmask = 0; + self.epoch = 0; + self.virtual_ip_list.clear(); + self.public_ip = 0; + self.public_port = 0; + self.special_fields.clear(); + } + + fn default_instance() -> &'static RegistrationResponse { + static instance: RegistrationResponse = RegistrationResponse { + virtual_ip: 0, + virtual_gateway: 0, + virtual_netmask: 0, + epoch: 0, + virtual_ip_list: ::std::vec::Vec::new(), + public_ip: 0, + public_port: 0, + special_fields: ::protobuf::SpecialFields::new(), + }; + &instance + } +} + +impl ::protobuf::MessageFull for RegistrationResponse { + fn descriptor() -> ::protobuf::reflect::MessageDescriptor { + static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::Lazy::new(); + descriptor.get(|| file_descriptor().message_by_package_relative_name("RegistrationResponse").unwrap()).clone() + } +} + +impl ::std::fmt::Display for RegistrationResponse { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + ::protobuf::text_format::fmt(self, f) + } +} + +impl ::protobuf::reflect::ProtobufValue for RegistrationResponse { + type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage; +} + +#[derive(PartialEq,Clone,Default,Debug)] +// @@protoc_insertion_point(message:DeviceList) +pub struct DeviceList { + // message fields + // @@protoc_insertion_point(field:DeviceList.epoch) + pub epoch: u32, + // @@protoc_insertion_point(field:DeviceList.virtual_ip_list) + pub virtual_ip_list: ::std::vec::Vec, + // special fields + // @@protoc_insertion_point(special_field:DeviceList.special_fields) + pub special_fields: ::protobuf::SpecialFields, +} + +impl<'a> ::std::default::Default for &'a DeviceList { + fn default() -> &'a DeviceList { + ::default_instance() + } +} + +impl DeviceList { + pub fn new() -> DeviceList { + ::std::default::Default::default() + } + + fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData { + let mut fields = ::std::vec::Vec::with_capacity(2); + let mut oneofs = ::std::vec::Vec::with_capacity(0); + fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>( + "epoch", + |m: &DeviceList| { &m.epoch }, + |m: &mut DeviceList| { &mut m.epoch }, + )); + fields.push(::protobuf::reflect::rt::v2::make_vec_simpler_accessor::<_, _>( + "virtual_ip_list", + |m: &DeviceList| { &m.virtual_ip_list }, + |m: &mut DeviceList| { &mut m.virtual_ip_list }, + )); + ::protobuf::reflect::GeneratedMessageDescriptorData::new_2::( + "DeviceList", + fields, + oneofs, + ) + } +} + +impl ::protobuf::Message for DeviceList { + const NAME: &'static str = "DeviceList"; + + fn is_initialized(&self) -> bool { + true + } + + fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> { + while let Some(tag) = is.read_raw_tag_or_eof()? { + match tag { + 8 => { + self.epoch = is.read_uint32()?; + }, + 18 => { + is.read_repeated_packed_fixed32_into(&mut self.virtual_ip_list)?; + }, + 21 => { + self.virtual_ip_list.push(is.read_fixed32()?); + }, + tag => { + ::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?; + }, + }; + } + ::std::result::Result::Ok(()) + } + + // Compute sizes of nested messages + #[allow(unused_variables)] + fn compute_size(&self) -> u64 { + let mut my_size = 0; + if self.epoch != 0 { + my_size += ::protobuf::rt::uint32_size(1, self.epoch); + } + my_size += 5 * self.virtual_ip_list.len() as u64; + my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields()); + self.special_fields.cached_size().set(my_size as u32); + my_size + } + + fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> { + if self.epoch != 0 { + os.write_uint32(1, self.epoch)?; + } + for v in &self.virtual_ip_list { + os.write_fixed32(2, *v)?; + }; + os.write_unknown_fields(self.special_fields.unknown_fields())?; + ::std::result::Result::Ok(()) + } + + fn special_fields(&self) -> &::protobuf::SpecialFields { + &self.special_fields + } + + fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields { + &mut self.special_fields + } + + fn new() -> DeviceList { + DeviceList::new() + } + + fn clear(&mut self) { + self.epoch = 0; + self.virtual_ip_list.clear(); + self.special_fields.clear(); + } + + fn default_instance() -> &'static DeviceList { + static instance: DeviceList = DeviceList { + epoch: 0, + virtual_ip_list: ::std::vec::Vec::new(), + special_fields: ::protobuf::SpecialFields::new(), + }; + &instance + } +} + +impl ::protobuf::MessageFull for DeviceList { + fn descriptor() -> ::protobuf::reflect::MessageDescriptor { + static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::Lazy::new(); + descriptor.get(|| file_descriptor().message_by_package_relative_name("DeviceList").unwrap()).clone() + } +} + +impl ::std::fmt::Display for DeviceList { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + ::protobuf::text_format::fmt(self, f) + } +} + +impl ::protobuf::reflect::ProtobufValue for DeviceList { + type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage; +} + +#[derive(PartialEq,Clone,Default,Debug)] +// @@protoc_insertion_point(message:Punch) +pub struct Punch { + // message fields + // @@protoc_insertion_point(field:Punch.virtual_ip) + pub virtual_ip: u32, + // @@protoc_insertion_point(field:Punch.public_ip_list) + pub public_ip_list: ::std::vec::Vec, + // @@protoc_insertion_point(field:Punch.public_port) + pub public_port: u32, + // @@protoc_insertion_point(field:Punch.public_port_range) + pub public_port_range: u32, + // @@protoc_insertion_point(field:Punch.nat_type) + pub nat_type: ::protobuf::EnumOrUnknown, + // @@protoc_insertion_point(field:Punch.reply) + pub reply: bool, + // @@protoc_insertion_point(field:Punch.step) + pub step: ::protobuf::EnumOrUnknown, + // special fields + // @@protoc_insertion_point(special_field:Punch.special_fields) + pub special_fields: ::protobuf::SpecialFields, +} + +impl<'a> ::std::default::Default for &'a Punch { + fn default() -> &'a Punch { + ::default_instance() + } +} + +impl Punch { + pub fn new() -> Punch { + ::std::default::Default::default() + } + + fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData { + let mut fields = ::std::vec::Vec::with_capacity(7); + let mut oneofs = ::std::vec::Vec::with_capacity(0); + fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>( + "virtual_ip", + |m: &Punch| { &m.virtual_ip }, + |m: &mut Punch| { &mut m.virtual_ip }, + )); + fields.push(::protobuf::reflect::rt::v2::make_vec_simpler_accessor::<_, _>( + "public_ip_list", + |m: &Punch| { &m.public_ip_list }, + |m: &mut Punch| { &mut m.public_ip_list }, + )); + fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>( + "public_port", + |m: &Punch| { &m.public_port }, + |m: &mut Punch| { &mut m.public_port }, + )); + fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>( + "public_port_range", + |m: &Punch| { &m.public_port_range }, + |m: &mut Punch| { &mut m.public_port_range }, + )); + fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>( + "nat_type", + |m: &Punch| { &m.nat_type }, + |m: &mut Punch| { &mut m.nat_type }, + )); + fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>( + "reply", + |m: &Punch| { &m.reply }, + |m: &mut Punch| { &mut m.reply }, + )); + fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>( + "step", + |m: &Punch| { &m.step }, + |m: &mut Punch| { &mut m.step }, + )); + ::protobuf::reflect::GeneratedMessageDescriptorData::new_2::( + "Punch", + fields, + oneofs, + ) + } +} + +impl ::protobuf::Message for Punch { + const NAME: &'static str = "Punch"; + + fn is_initialized(&self) -> bool { + true + } + + fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> { + while let Some(tag) = is.read_raw_tag_or_eof()? { + match tag { + 13 => { + self.virtual_ip = is.read_fixed32()?; + }, + 18 => { + is.read_repeated_packed_fixed32_into(&mut self.public_ip_list)?; + }, + 21 => { + self.public_ip_list.push(is.read_fixed32()?); + }, + 24 => { + self.public_port = is.read_uint32()?; + }, + 32 => { + self.public_port_range = is.read_uint32()?; + }, + 40 => { + self.nat_type = is.read_enum_or_unknown()?; + }, + 48 => { + self.reply = is.read_bool()?; + }, + 56 => { + self.step = is.read_enum_or_unknown()?; + }, + tag => { + ::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?; + }, + }; + } + ::std::result::Result::Ok(()) + } + + // Compute sizes of nested messages + #[allow(unused_variables)] + fn compute_size(&self) -> u64 { + let mut my_size = 0; + if self.virtual_ip != 0 { + my_size += 1 + 4; + } + my_size += 5 * self.public_ip_list.len() as u64; + if self.public_port != 0 { + my_size += ::protobuf::rt::uint32_size(3, self.public_port); + } + if self.public_port_range != 0 { + my_size += ::protobuf::rt::uint32_size(4, self.public_port_range); + } + if self.nat_type != ::protobuf::EnumOrUnknown::new(NatType::Symmetric) { + my_size += ::protobuf::rt::int32_size(5, self.nat_type.value()); + } + if self.reply != false { + my_size += 1 + 1; + } + if self.step != ::protobuf::EnumOrUnknown::new(Step::Step1) { + my_size += ::protobuf::rt::int32_size(7, self.step.value()); + } + my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields()); + self.special_fields.cached_size().set(my_size as u32); + my_size + } + + fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> { + if self.virtual_ip != 0 { + os.write_fixed32(1, self.virtual_ip)?; + } + for v in &self.public_ip_list { + os.write_fixed32(2, *v)?; + }; + if self.public_port != 0 { + os.write_uint32(3, self.public_port)?; + } + if self.public_port_range != 0 { + os.write_uint32(4, self.public_port_range)?; + } + if self.nat_type != ::protobuf::EnumOrUnknown::new(NatType::Symmetric) { + os.write_enum(5, ::protobuf::EnumOrUnknown::value(&self.nat_type))?; + } + if self.reply != false { + os.write_bool(6, self.reply)?; + } + if self.step != ::protobuf::EnumOrUnknown::new(Step::Step1) { + os.write_enum(7, ::protobuf::EnumOrUnknown::value(&self.step))?; + } + os.write_unknown_fields(self.special_fields.unknown_fields())?; + ::std::result::Result::Ok(()) + } + + fn special_fields(&self) -> &::protobuf::SpecialFields { + &self.special_fields + } + + fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields { + &mut self.special_fields + } + + fn new() -> Punch { + Punch::new() + } + + fn clear(&mut self) { + self.virtual_ip = 0; + self.public_ip_list.clear(); + self.public_port = 0; + self.public_port_range = 0; + self.nat_type = ::protobuf::EnumOrUnknown::new(NatType::Symmetric); + self.reply = false; + self.step = ::protobuf::EnumOrUnknown::new(Step::Step1); + self.special_fields.clear(); + } + + fn default_instance() -> &'static Punch { + static instance: Punch = Punch { + virtual_ip: 0, + public_ip_list: ::std::vec::Vec::new(), + public_port: 0, + public_port_range: 0, + nat_type: ::protobuf::EnumOrUnknown::from_i32(0), + reply: false, + step: ::protobuf::EnumOrUnknown::from_i32(0), + special_fields: ::protobuf::SpecialFields::new(), + }; + &instance + } +} + +impl ::protobuf::MessageFull for Punch { + fn descriptor() -> ::protobuf::reflect::MessageDescriptor { + static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::Lazy::new(); + descriptor.get(|| file_descriptor().message_by_package_relative_name("Punch").unwrap()).clone() + } +} + +impl ::std::fmt::Display for Punch { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + ::protobuf::text_format::fmt(self, f) + } +} + +impl ::protobuf::reflect::ProtobufValue for Punch { + type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage; +} + +#[derive(Clone,Copy,PartialEq,Eq,Debug,Hash)] +// @@protoc_insertion_point(enum:NatType) +pub enum NatType { + // @@protoc_insertion_point(enum_value:NatType.Symmetric) + Symmetric = 0, + // @@protoc_insertion_point(enum_value:NatType.Cone) + Cone = 1, +} + +impl ::protobuf::Enum for NatType { + const NAME: &'static str = "NatType"; + + fn value(&self) -> i32 { + *self as i32 + } + + fn from_i32(value: i32) -> ::std::option::Option { + match value { + 0 => ::std::option::Option::Some(NatType::Symmetric), + 1 => ::std::option::Option::Some(NatType::Cone), + _ => ::std::option::Option::None + } + } + + const VALUES: &'static [NatType] = &[ + NatType::Symmetric, + NatType::Cone, + ]; +} + +impl ::protobuf::EnumFull for NatType { + fn enum_descriptor() -> ::protobuf::reflect::EnumDescriptor { + static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::EnumDescriptor> = ::protobuf::rt::Lazy::new(); + descriptor.get(|| file_descriptor().enum_by_package_relative_name("NatType").unwrap()).clone() + } + + fn descriptor(&self) -> ::protobuf::reflect::EnumValueDescriptor { + let index = *self as usize; + Self::enum_descriptor().value_by_index(index) + } +} + +impl ::std::default::Default for NatType { + fn default() -> Self { + NatType::Symmetric + } +} + +impl NatType { + fn generated_enum_descriptor_data() -> ::protobuf::reflect::GeneratedEnumDescriptorData { + ::protobuf::reflect::GeneratedEnumDescriptorData::new::("NatType") + } +} + +#[derive(Clone,Copy,PartialEq,Eq,Debug,Hash)] +// @@protoc_insertion_point(enum:Step) +pub enum Step { + // @@protoc_insertion_point(enum_value:Step.Step1) + Step1 = 0, + // @@protoc_insertion_point(enum_value:Step.Step2) + Step2 = 1, + // @@protoc_insertion_point(enum_value:Step.Step3) + Step3 = 2, + // @@protoc_insertion_point(enum_value:Step.Step4) + Step4 = 3, +} + +impl ::protobuf::Enum for Step { + const NAME: &'static str = "Step"; + + fn value(&self) -> i32 { + *self as i32 + } + + fn from_i32(value: i32) -> ::std::option::Option { + match value { + 0 => ::std::option::Option::Some(Step::Step1), + 1 => ::std::option::Option::Some(Step::Step2), + 2 => ::std::option::Option::Some(Step::Step3), + 3 => ::std::option::Option::Some(Step::Step4), + _ => ::std::option::Option::None + } + } + + const VALUES: &'static [Step] = &[ + Step::Step1, + Step::Step2, + Step::Step3, + Step::Step4, + ]; +} + +impl ::protobuf::EnumFull for Step { + fn enum_descriptor() -> ::protobuf::reflect::EnumDescriptor { + static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::EnumDescriptor> = ::protobuf::rt::Lazy::new(); + descriptor.get(|| file_descriptor().enum_by_package_relative_name("Step").unwrap()).clone() + } + + fn descriptor(&self) -> ::protobuf::reflect::EnumValueDescriptor { + let index = *self as usize; + Self::enum_descriptor().value_by_index(index) + } +} + +impl ::std::default::Default for Step { + fn default() -> Self { + Step::Step1 + } +} + +impl Step { + fn generated_enum_descriptor_data() -> ::protobuf::reflect::GeneratedEnumDescriptorData { + ::protobuf::reflect::GeneratedEnumDescriptorData::new::("Step") + } +} + +static file_descriptor_proto_data: &'static [u8] = b"\ + \n\rmessage.proto\"L\n\x13RegistrationRequest\x12\x14\n\x05token\x18\x01\ + \x20\x01(\tR\x05token\x12\x1f\n\x0bmac_address\x18\x02\x20\x01(\tR\nmacA\ + ddress\"\x83\x02\n\x14RegistrationResponse\x12\x1d\n\nvirtual_ip\x18\x01\ + \x20\x01(\x07R\tvirtualIp\x12'\n\x0fvirtual_gateway\x18\x02\x20\x01(\x07\ + R\x0evirtualGateway\x12'\n\x0fvirtual_netmask\x18\x03\x20\x01(\x07R\x0ev\ + irtualNetmask\x12\x14\n\x05epoch\x18\x04\x20\x01(\rR\x05epoch\x12&\n\x0f\ + virtual_ip_list\x18\x05\x20\x03(\x07R\rvirtualIpList\x12\x1b\n\tpublic_i\ + p\x18\x06\x20\x01(\x07R\x08publicIp\x12\x1f\n\x0bpublic_port\x18\x07\x20\ + \x01(\rR\npublicPort\"J\n\nDeviceList\x12\x14\n\x05epoch\x18\x01\x20\x01\ + (\rR\x05epoch\x12&\n\x0fvirtual_ip_list\x18\x02\x20\x03(\x07R\rvirtualIp\ + List\"\xef\x01\n\x05Punch\x12\x1d\n\nvirtual_ip\x18\x01\x20\x01(\x07R\tv\ + irtualIp\x12$\n\x0epublic_ip_list\x18\x02\x20\x03(\x07R\x0cpublicIpList\ + \x12\x1f\n\x0bpublic_port\x18\x03\x20\x01(\rR\npublicPort\x12*\n\x11publ\ + ic_port_range\x18\x04\x20\x01(\rR\x0fpublicPortRange\x12#\n\x08nat_type\ + \x18\x05\x20\x01(\x0e2\x08.NatTypeR\x07natType\x12\x14\n\x05reply\x18\ + \x06\x20\x01(\x08R\x05reply\x12\x19\n\x04step\x18\x07\x20\x01(\x0e2\x05.\ + StepR\x04step*\"\n\x07NatType\x12\r\n\tSymmetric\x10\0\x12\x08\n\x04Cone\ + \x10\x01*2\n\x04Step\x12\t\n\x05Step1\x10\0\x12\t\n\x05Step2\x10\x01\x12\ + \t\n\x05Step3\x10\x02\x12\t\n\x05Step4\x10\x03b\x06proto3\ +"; + +/// `FileDescriptorProto` object which was a source for this generated file +fn file_descriptor_proto() -> &'static ::protobuf::descriptor::FileDescriptorProto { + static file_descriptor_proto_lazy: ::protobuf::rt::Lazy<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::Lazy::new(); + file_descriptor_proto_lazy.get(|| { + ::protobuf::Message::parse_from_bytes(file_descriptor_proto_data).unwrap() + }) +} + +/// `FileDescriptor` object which allows dynamic access to files +pub fn file_descriptor() -> &'static ::protobuf::reflect::FileDescriptor { + static generated_file_descriptor_lazy: ::protobuf::rt::Lazy<::protobuf::reflect::GeneratedFileDescriptor> = ::protobuf::rt::Lazy::new(); + static file_descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::FileDescriptor> = ::protobuf::rt::Lazy::new(); + file_descriptor.get(|| { + let generated_file_descriptor = generated_file_descriptor_lazy.get(|| { + let mut deps = ::std::vec::Vec::with_capacity(0); + let mut messages = ::std::vec::Vec::with_capacity(4); + messages.push(RegistrationRequest::generated_message_descriptor_data()); + messages.push(RegistrationResponse::generated_message_descriptor_data()); + messages.push(DeviceList::generated_message_descriptor_data()); + messages.push(Punch::generated_message_descriptor_data()); + let mut enums = ::std::vec::Vec::with_capacity(2); + enums.push(NatType::generated_enum_descriptor_data()); + enums.push(Step::generated_enum_descriptor_data()); + ::protobuf::reflect::GeneratedFileDescriptor::new_generated( + file_descriptor_proto(), + deps, + messages, + enums, + ) + }); + ::protobuf::reflect::FileDescriptor::new_generated_2(generated_file_descriptor) + }) +} diff --git a/src/proto/mod.rs b/src/proto/mod.rs new file mode 100644 index 0000000..9a4077a --- /dev/null +++ b/src/proto/mod.rs @@ -0,0 +1,3 @@ +// @generated + +pub mod message; diff --git a/src/protocol/control_packet.rs b/src/protocol/control_packet.rs new file mode 100644 index 0000000..d1af643 --- /dev/null +++ b/src/protocol/control_packet.rs @@ -0,0 +1,205 @@ +use std::fmt; +use std::net::Ipv4Addr; + +use crate::error::*; + +#[derive(Eq, PartialEq, Copy, Clone, Debug)] +pub enum Protocol { + Ping, + Pong, + PunchRequest, + PunchResponse, + UnKnow(u8), +} + +impl From for Protocol { + fn from(value: u8) -> Self { + match value { + 1 => Protocol::Ping, + 2 => Protocol::Pong, + 3 => Protocol::PunchRequest, + 4 => Protocol::PunchResponse, + val => Protocol::UnKnow(val), + } + } +} + +impl Into for Protocol { + fn into(self) -> u8 { + match self { + Protocol::Ping => 1, + Protocol::Pong => 2, + Protocol::PunchRequest => 3, + Protocol::PunchResponse => 4, + Protocol::UnKnow(val) => val, + } + } +} + +pub enum ControlPacket { + PingPacket(PingPacket), + PongPacket(PongPacket), + PunchRequest(PunchRequestPacket), + PunchResponse(PunchResponsePacket), +} + +impl> ControlPacket { + pub fn new(protocol: u8, buffer: B) -> Result> { + match Protocol::from(protocol) { + Protocol::Ping => Ok(ControlPacket::PingPacket(PingPacket::new(buffer)?)), + Protocol::Pong => Ok(ControlPacket::PongPacket(PongPacket::new(buffer)?)), + Protocol::PunchRequest => Ok(ControlPacket::PunchRequest(PunchRequestPacket::new( + buffer, + )?)), + Protocol::PunchResponse => Ok(ControlPacket::PunchResponse(PunchResponsePacket::new( + buffer, + )?)), + Protocol::UnKnow(_) => Err(Error::NotSupport), + } + } +} + +/// 网络探针 +#[derive(Copy, Clone)] +pub struct PingPacket { + buffer: B, +} + +#[derive(Copy, Clone)] +pub struct PongPacket { + buffer: B, +} + +impl> PingPacket { + pub fn new(buffer: B) -> Result> { + let len = buffer.as_ref().len(); + if len != 8 + 4 { + return Err(Error::InvalidPacket); + } + Ok(PingPacket { buffer }) + } +} + +impl> PingPacket { + pub fn time(&self) -> i64 { + i64::from_be_bytes(self.buffer.as_ref()[..8].try_into().unwrap()) + } + pub fn epoch(&self) -> u32 { + u32::from_be_bytes(self.buffer.as_ref()[8..12].try_into().unwrap()) + } +} + +impl + AsMut<[u8]>> PingPacket { + pub fn set_time(&mut self, time: i64) { + self.buffer.as_mut()[..8].copy_from_slice(&time.to_be_bytes()) + } + pub fn set_epoch(&mut self, epoch: u32) { + self.buffer.as_mut()[8..12].copy_from_slice(&epoch.to_be_bytes()) + } +} + +impl> PongPacket { + pub fn new(buffer: B) -> Result> { + let len = buffer.as_ref().len(); + if len != 8 { + return Err(Error::InvalidPacket); + } + Ok(PongPacket { buffer }) + } +} + +impl> PongPacket { + pub fn time(&self) -> i64 { + i64::from_be_bytes(self.buffer.as_ref()[..8].try_into().unwrap()) + } +} + +impl + AsMut<[u8]>> PongPacket { + pub fn set_time(&mut self, time: i64) { + self.buffer.as_mut()[..8].copy_from_slice(&time.to_be_bytes()) + } +} + +pub type TurnPongPacket = TurnPingPacket; + +/// 探测目标延迟 +#[derive(Copy, Clone)] +pub struct TurnPingPacket { + buffer: B, +} + +impl> TurnPingPacket { + pub fn new(buffer: B) -> Result> { + let len = buffer.as_ref().len(); + if len != 16 { + return Err(Error::InvalidPacket); + } + Ok(TurnPingPacket { buffer }) + } +} + +impl> TurnPingPacket { + // pub fn source(&self) -> Ipv4Addr { + // let tmp:[u8;4] = self.buffer.as_ref()[..4].try_into().unwrap(); + // Ipv4Addr::from(tmp) + // } + // pub fn destination(&self) -> Ipv4Addr { + // let tmp:[u8;4] = self.buffer.as_ref()[4..8].try_into().unwrap(); + // Ipv4Addr::from(tmp) + // } + pub fn time(&self) -> i64 { + i64::from_be_bytes(self.buffer.as_ref()[8..].try_into().unwrap()) + } +} + +impl + AsMut<[u8]>> TurnPingPacket { + pub fn set_source(&mut self, source: Ipv4Addr) { + self.buffer.as_mut()[..4].copy_from_slice(&source.octets()); + } + pub fn set_destination(&mut self, destination: Ipv4Addr) { + self.buffer.as_mut()[4..8].copy_from_slice(&destination.octets()); + } + pub fn set_time(&mut self, time: i64) { + self.buffer.as_mut()[8..].copy_from_slice(&time.to_be_bytes()) + } +} + +pub type PunchResponsePacket = PunchPacket; +pub type PunchRequestPacket = PunchPacket; + +/// nat穿透 +#[derive(Clone)] +pub struct PunchPacket { + buffer: B, +} + +impl> PunchPacket { + pub fn new(buffer: B) -> Result> { + let len = buffer.as_ref().len(); + if len != 8 { + return Err(Error::InvalidPacket); + } + Ok(Self { buffer }) + } +} + +impl> PunchPacket { + pub fn source(&self) -> Ipv4Addr { + let tmp: [u8; 4] = self.buffer.as_ref()[..4].try_into().unwrap(); + Ipv4Addr::from(tmp) + } +} + +impl + AsMut<[u8]>> PunchPacket { + pub fn set_source(&mut self, source: Ipv4Addr) { + self.buffer.as_mut()[..4].copy_from_slice(&source.octets()); + } +} + +impl> fmt::Debug for PunchPacket { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("PunchPacket") + .field("source", &self.source()) + .finish() + } +} diff --git a/src/protocol/error_packet.rs b/src/protocol/error_packet.rs new file mode 100644 index 0000000..d677f5d --- /dev/null +++ b/src/protocol/error_packet.rs @@ -0,0 +1,69 @@ +use crate::error::*; + +#[derive(Eq, PartialEq, Copy, Clone, Debug)] +pub enum Protocol { + TokenError, + Disconnect, + Other(u8), +} + +impl From for Protocol { + fn from(value: u8) -> Self { + match value { + 1 => Self::TokenError, + 2 => Self::Disconnect, + val => Self::Other(val), + } + } +} + +impl Into for Protocol { + fn into(self) -> u8 { + match self { + Protocol::TokenError => 1, + Protocol::Disconnect => 2, + Protocol::Other(val) => val, + } + } +} + +pub enum InErrorPacket { + TokenError, + Disconnect, + OtherError(ErrorPacket), +} + +impl> InErrorPacket { + pub fn new(protocol: u8, buffer: B) -> Result> { + match Protocol::from(protocol) { + Protocol::TokenError => Ok(InErrorPacket::TokenError), + Protocol::Disconnect => Ok(InErrorPacket::Disconnect), + Protocol::Other(_) => Ok(InErrorPacket::OtherError(ErrorPacket::new(buffer)?)), + } + } +} + +pub struct ErrorPacket { + buffer: B, +} + +impl> ErrorPacket { + pub fn new(buffer: B) -> Result> { + Ok(Self { buffer }) + } +} + +impl> ErrorPacket { + pub fn message(&self) -> Result { + match String::from_utf8(self.buffer.as_ref().to_vec()) { + Ok(str) => Ok(str), + Err(_) => Err(Error::InvalidPacket), + } + } +} + +impl + AsMut<[u8]>> ErrorPacket { + pub fn set_message(&mut self, message: &str) { + self.buffer.as_mut().copy_from_slice(message.as_bytes()) + } +} diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs new file mode 100644 index 0000000..3f6bd86 --- /dev/null +++ b/src/protocol/mod.rs @@ -0,0 +1,145 @@ +use std::fmt; + +use crate::error::*; + +pub mod control_packet; +pub mod error_packet; +pub mod service_packet; +pub mod turn_packet; + +#[derive(Eq, PartialEq, Copy, Clone, Debug)] +pub enum Version { + V1, + UnKnow(u8), +} + +impl From for Version { + fn from(value: u8) -> Self { + match value { + 1 => Version::V1, + val => Version::UnKnow(val), + } + } +} + +impl Into for Version { + fn into(self) -> u8 { + match self { + Version::V1 => 1, + Version::UnKnow(val) => val, + } + } +} + +#[derive(Eq, PartialEq, Copy, Clone, Debug)] +pub enum Protocol { + /// 服务包 用于和服务端交互 + Service, + /// 响应异常 + Error, + /// 控制协议 + Control, + /// 转发ipv4数据 + Ipv4Turn, + OtherTurn, + UnKnow(u8), +} + +impl From for Protocol { + fn from(value: u8) -> Self { + match value { + 1 => Protocol::Service, + 2 => Protocol::Error, + 3 => Protocol::Control, + 4 => Protocol::Ipv4Turn, + 5 => Protocol::OtherTurn, + val => Protocol::UnKnow(val), + } + } +} + +impl Into for Protocol { + fn into(self) -> u8 { + match self { + Protocol::Service => 1, + Protocol::Error => 2, + Protocol::Control => 3, + Protocol::Ipv4Turn => 4, + Protocol::OtherTurn => 5, + Protocol::UnKnow(val) => val, + } + } +} + +#[derive(Copy, Clone)] +pub struct NetPacket { + buffer: B, +} + +impl> NetPacket { + pub fn new(buffer: B) -> Result> { + let len = buffer.as_ref().len(); + // 不能大于udp最大载荷长度 + if len < 4 || len > 65535 - 20 - 8 { + return Err(Error::InvalidPacket); + } + Ok(NetPacket { buffer }) + } + pub fn buffer(&self) -> &[u8] { + self.buffer.as_ref() + } + pub fn into_buffer(self) -> B { + self.buffer + } +} + +impl> NetPacket { + pub fn version(&self) -> Version { + Version::from(self.buffer.as_ref()[0]) + } + pub fn protocol(&self) -> Protocol { + Protocol::from(self.buffer.as_ref()[1]) + } + pub fn transport_protocol(&self) -> u8 { + self.buffer.as_ref()[2] + } + pub fn ttl(&self) -> u8 { + self.buffer.as_ref()[3] + } + pub fn payload(&self) -> &[u8] { + &self.buffer.as_ref()[4..] + } +} + +impl + AsMut<[u8]>> NetPacket { + pub fn set_version(&mut self, version: Version) { + self.buffer.as_mut()[0] = version.into(); + } + pub fn set_protocol(&mut self, protocol: Protocol) { + self.buffer.as_mut()[1] = protocol.into(); + } + pub fn set_transport_protocol(&mut self, transport_protocol: u8) { + self.buffer.as_mut()[2] = transport_protocol; + } + pub fn set_ttl(&mut self, ttl: u8) { + self.buffer.as_mut()[3] = ttl; + } + pub fn set_payload(&mut self, payload: &[u8]) { + self.buffer.as_mut()[4..payload.len() + 4].copy_from_slice(payload); + } + pub fn payload_mut(&mut self) -> &mut [u8] { + &mut self.buffer.as_mut()[4..] + } +} + +impl> fmt::Debug for NetPacket { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("NetPacket") + .field("version", &self.version()) + .field("protocol", &self.protocol()) + .field("transport_protocol", &self.transport_protocol()) + .field("ttl", &self.ttl()) + .field("payload", &self.payload()) + .finish() + } +} diff --git a/src/protocol/service_packet.rs b/src/protocol/service_packet.rs new file mode 100644 index 0000000..c933ac6 --- /dev/null +++ b/src/protocol/service_packet.rs @@ -0,0 +1,32 @@ +#[derive(Eq, PartialEq, Copy, Clone, Debug)] +pub enum Protocol { + /// 注册请求 + RegistrationRequest, + /// 注册响应 + RegistrationResponse, + /// 更新设备列表 + UpdateDeviceList, + UnKnow(u8), +} + +impl From for Protocol { + fn from(value: u8) -> Self { + match value { + 1 => Self::RegistrationRequest, + 2 => Self::RegistrationResponse, + 3 => Self::UpdateDeviceList, + val => Self::UnKnow(val), + } + } +} + +impl Into for Protocol { + fn into(self) -> u8 { + match self { + Self::RegistrationRequest => 1, + Self::RegistrationResponse => 2, + Self::UpdateDeviceList => 3, + Self::UnKnow(val) => val, + } + } +} diff --git a/src/protocol/turn_packet.rs b/src/protocol/turn_packet.rs new file mode 100644 index 0000000..4016825 --- /dev/null +++ b/src/protocol/turn_packet.rs @@ -0,0 +1,81 @@ +use std::fmt; +use std::net::Ipv4Addr; + +use crate::error::*; + +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub enum Protocol { + Punch, + UnKnow(u8), +} + +impl From for Protocol { + fn from(value: u8) -> Self { + match value { + 1 => Protocol::Punch, + val => Protocol::UnKnow(val), + } + } +} + +impl Into for Protocol { + fn into(self) -> u8 { + match self { + Protocol::Punch => 1, + Protocol::UnKnow(val) => val, + } + } +} + +pub struct TurnPacket { + buffer: B, +} + +impl> TurnPacket { + pub fn new(buffer: B) -> Result> { + let len = buffer.as_ref().len(); + if len <= 8 { + return Err(Error::InvalidPacket); + } + Ok(Self { buffer }) + } +} + +impl> TurnPacket { + pub fn source(&self) -> Ipv4Addr { + let tmp: [u8; 4] = self.buffer.as_ref()[..4].try_into().unwrap(); + Ipv4Addr::from(tmp) + } + pub fn destination(&self) -> Ipv4Addr { + let tmp: [u8; 4] = self.buffer.as_ref()[4..8].try_into().unwrap(); + Ipv4Addr::from(tmp) + } + pub fn payload(&self) -> &[u8] { + &self.buffer.as_ref()[8..] + } +} + +impl + AsMut<[u8]>> TurnPacket { + pub fn payload_mut(&mut self) -> &mut [u8] { + &mut self.buffer.as_mut()[8..] + } + pub fn set_source(&mut self, source: Ipv4Addr) { + self.buffer.as_mut()[..4].copy_from_slice(&source.octets()); + } + pub fn set_destination(&mut self, destination: Ipv4Addr) { + self.buffer.as_mut()[4..8].copy_from_slice(&destination.octets()); + } + pub fn set_payload(&mut self, payload: &[u8]) { + self.buffer.as_mut()[8..payload.len() + 8].copy_from_slice(payload) + } +} + +impl> fmt::Debug for TurnPacket { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("TurnPacket") + .field("source", &self.source()) + .field("destination", &self.destination()) + .field("payload", &self.payload()) + .finish() + } +} diff --git a/src/tun_device/mod.rs b/src/tun_device/mod.rs new file mode 100644 index 0000000..410b2cc --- /dev/null +++ b/src/tun_device/mod.rs @@ -0,0 +1,14 @@ + +#[cfg(any(unix))] +pub use unix::create_tun; +#[cfg(any(unix))] +pub use unix::{TunReader, TunWriter}; + +#[cfg(any(unix))] +pub mod unix; +#[cfg(target_os = "windows")] +pub mod windows; +#[cfg(target_os = "windows")] +pub use windows::create_tun; +#[cfg(target_os = "windows")] +pub use windows::{TunReader, TunWriter}; diff --git a/src/tun_device/unix.rs b/src/tun_device/unix.rs new file mode 100644 index 0000000..0f450d3 --- /dev/null +++ b/src/tun_device/unix.rs @@ -0,0 +1,155 @@ +use std::io; +use std::io::{Error, Read, Write}; +use std::net::Ipv4Addr; +use std::os::unix::process::CommandExt; +use std::process::Command; + +use bytes::BufMut; +use tun::platform::posix::{Reader, Writer}; +use tun::Device; + +pub fn create_tun( + address: Ipv4Addr, + netmask: Ipv4Addr, + gateway: Ipv4Addr, +) -> crate::error::Result<(TunWriter, TunReader)> { + let mut config = tun::Configuration::default(); + + config + .destination(gateway) + .address(address) + .netmask(netmask) + .mtu(1420) + .up(); + + #[cfg(target_os = "linux")] + config.platform(|config| { + config.packet_information(true); + }); + + let dev = tun::create(&config).unwrap(); + + let up_eth_str: String = format!("ifconfig utun3 {:?} {:?} up ", address, gateway); + let route_add_str: String = format!( + "sudo route -n add -net {:?} -netmask {:?} {:?}", + address, netmask, gateway + ); + + let up_eth_out = Command::new("sh") + .arg("-c") + .arg(up_eth_str) + .output() + .expect("sh exec error!"); + if !up_eth_out.status.success(){ + return Err(crate::error::Error::Stop(format!("设置地址失败:{:?}", up_eth_out))); + } + // println!("{:?}", up_eth_out); + let if_config_out = Command::new("sh") + .arg("-c") + .arg(route_add_str) + .output() + .expect("sh exec error!"); + if !if_config_out.status.success(){ + return Err(crate::error::Error::Stop(format!("设置路由失败:{:?}", if_config_out))); + } + // 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 (reader, writer) = dev.split(); + Ok(( + TunWriter(writer, packet_information), + TunReader(reader, packet_information), + )) +} + +pub struct TunReader(Reader, bool); + +impl TunReader { + pub fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> io::Result<&mut [u8]> { + let len = self.0.read(buf)?; + if self.1 { + Ok(&mut buf[4..len]) + } else { + Ok(&mut buf[..len]) + } + } +} + +pub struct TunWriter(Writer, bool); + +impl TunWriter { + pub fn write(&mut self, packet: &[u8]) -> io::Result<()> { + if self.1 { + let mut buf = Vec::::with_capacity(4 + packet.len()); + buf.put_u16(0); + #[cfg(any(target_os = "macos", target_os = "ios"))] + buf.put_u16(libc::PF_INET as u16); + #[cfg(any(target_os = "linux", target_os = "android"))] + buf.put_u16(libc::ETH_P_IP as u16); + buf.extend_from_slice(packet); + self.0.write_all(&buf) + } else { + self.0.write_all(packet) + } + } +} + +// pub fn main1() { +// loop { +// let len = reader.read(&mut buffer).unwrap(); +// println!("{:?}", &buffer[..len]); +// match ip::Packet::new(&buffer[4..len]) { +// Ok(ip::Packet::V4(pkt)) => { +// match icmp::Packet::new(pkt.payload()) { +// Ok(icmp) => { +// match icmp.echo() { +// Ok(icmp) => { +// println!("{:?}", icmp); +// let reply = ip::v4::Builder::default() +// .id(0x42) +// .unwrap() +// .ttl(64) +// .unwrap() +// .source(pkt.destination()) +// .unwrap() +// .destination(pkt.source()) +// .unwrap() +// .icmp() +// .unwrap() +// .echo() +// .unwrap() +// .reply() +// .unwrap() +// .identifier(icmp.identifier()) +// .unwrap() +// .sequence(icmp.sequence()) +// .unwrap() +// .payload(icmp.payload()) +// .unwrap() +// .build() +// .unwrap(); +// let l = reply.len(); +// &mut buffer[4..(l + 4)].copy_from_slice(&reply); +// // writer.write_all(&buffer[..4]).unwrap(); +// writer.write_all(&buffer[..(l + 4)]).unwrap(); +// } +// Err(_) => {} +// } +// } +// Err(_) => {} +// } +// } +// _ => {} +// } +// } +// } diff --git a/src/tun_device/windows.rs b/src/tun_device/windows.rs new file mode 100644 index 0000000..2e59f11 --- /dev/null +++ b/src/tun_device/windows.rs @@ -0,0 +1,131 @@ +use std::io; +use std::net::Ipv4Addr; +use std::sync::Arc; + +use libloading::Library; +use wintun::{Adapter, Packet, Session}; + +use crate::error::*; + +pub struct TunWriter(Arc); + +impl TunWriter { + pub fn write(&self, buf: &[u8]) -> io::Result<()> { + match self.0.allocate_send_packet(buf.len() as u16) { + Ok(mut packet) => { + packet.bytes_mut().copy_from_slice(buf); + self.0.send_packet(packet); + return Ok(()); + } + Err(_) => {} + } + return Err(io::Error::new(io::ErrorKind::Other, "send err")); + } +} + +pub struct TunReader(Arc); + +impl TunReader { + pub fn next(&self) -> io::Result { + match self.0.receive_blocking() { + Ok(packet) => { + return Ok(packet); + } + Err(_) => {} + } + return Err(io::Error::new(io::ErrorKind::Other, "read err")); + } +} + +pub fn create_tun( + address: Ipv4Addr, + netmask: Ipv4Addr, + gateway: Ipv4Addr, +) -> Result<(TunWriter, TunReader)> { + let win_tun = unsafe { + match Library::new("wintun.dll") { + Ok(library) => match wintun::load_from_library(library) { + Ok(win_tun) => win_tun, + Err(e) => { + return Err(Error::Stop(format!("{:?}", e))); + } + }, + Err(e) => { + return Err(Error::Stop(format!("{:?}", e))); + } + } + }; + let adapter = match Adapter::open(&win_tun, "Demo") { + Ok(a) => a, + Err(_) => match Adapter::create(&win_tun, "Example", "Demo", None) { + Ok(adapter) => adapter, + + Err(e) => return Err(Error::Stop(format!("{:?}", e))), + }, + }; + let index = adapter.get_adapter_index().unwrap(); + 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, + ); + // println!("{}", set_mtu); + // println!("{}", set_metric); + // println!("{}", set_address); + // 执行网卡初始化命令 + let out = std::process::Command::new("cmd") + .arg("/C") + .arg(set_mtu) + .output() + .unwrap(); + if !out.status.success() { + return Err(Error::Stop(format!("设置mtu失败:{:?}", out))); + } + let out = std::process::Command::new("cmd") + .arg("/C") + .arg(set_metric) + .output() + .unwrap(); + if !out.status.success() { + return Err(Error::Stop(format!("设置接口跃点失败:{:?}", out))); + } + let out = std::process::Command::new("cmd") + .arg("/C") + .arg(set_address) + .output() + .unwrap(); + if !out.status.success() { + return Err(Error::Stop(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 + ); + // println!("{}", set_route); + // 执行添加路由命令 + let out = std::process::Command::new("cmd") + .arg("/C") + .arg(set_route) + .output() + .unwrap(); + if !out.status.success() { + return Err(Error::Stop(format!("添加路由失败:{:?}", out))); + } + let session = Arc::new(adapter.start_session(wintun::MAX_RING_CAPACITY).unwrap()); + let reader_session = session.clone(); + Ok((TunWriter(session), TunReader(reader_session))) +}