diff --git a/Cargo.toml b/Cargo.toml index 416143f..0b5ea6d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,3 +50,13 @@ reqwest = { version = "0.11", default-features = false, features = [ ] } serial_test = "2.0" test-log = "0.2" + +[target.'cfg(target_os="windows")'.dependencies] +windows = { version = "0.51", features = [ + "Win32_NetworkManagement_IpHelper", + "Win32_NetworkManagement_Ndis", + "Win32_Networking_WinSock", + "Win32_Foundation", +] } +wintun = "0.3" +mio = { version = "0.8", features = ["net", "os-ext", "os-poll"] } diff --git a/src/lib.rs b/src/lib.rs index 20cef0c..78e6d0b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,6 +23,8 @@ mod tuntapinterface; mod tuntapinterfacedesc; mod virtdevice; mod virtdns; +#[cfg(target_os = "windows")] +mod wintuninterface; #[derive(Clone, Debug)] pub struct Proxy { diff --git a/src/tun2proxy.rs b/src/tun2proxy.rs index b9c9a26..2488f15 100644 --- a/src/tun2proxy.rs +++ b/src/tun2proxy.rs @@ -6,16 +6,17 @@ use crate::{dns, error::Error, error::Result, virtdevice::VirtualTunDevice, Netw #[cfg(target_family = "unix")] use mio::unix::SourceFd; use mio::{event::Event, net::TcpStream, net::UdpSocket, Events, Interest, Poll, Token}; -#[cfg(not(target_family = "unix"))] -use smoltcp::phy::DeviceCapabilities; #[cfg(any(target_os = "macos", target_os = "ios"))] use smoltcp::phy::RawSocket; // #[cfg(any(target_os = "linux", target_os = "android"))] // use smoltcp::phy::TunTapInterface; +#[cfg(target_os = "windows")] +use crate::wintuninterface::WinTunInterface; #[cfg(target_family = "unix")] -use smoltcp::phy::{Device, Medium, RxToken, TxToken}; +use smoltcp::phy::{RxToken, TxToken}; use smoltcp::{ iface::{Config, Interface, SocketHandle, SocketSet}, + phy::{Device, Medium}, socket::{tcp, tcp::State, udp, udp::UdpMetadata}, time::Instant, wire::{IpCidr, IpProtocol, Ipv4Packet, Ipv6Packet, TcpPacket, UdpPacket, UDP_HEADER_LEN}, @@ -217,6 +218,8 @@ pub struct TunToProxy<'a> { tun: TunTapInterface, #[cfg(any(target_os = "macos", target_os = "ios"))] tun: RawSocket, + #[cfg(target_os = "windows")] + tun: WinTunInterface, poll: Poll, iface: Interface, connection_map: HashMap, @@ -246,6 +249,11 @@ impl<'a> TunToProxy<'a> { NetworkInterface::Fd(_fd) => panic!("Not supported"), }; + #[cfg(target_os = "windows")] + let tun = match _interface { + NetworkInterface::Named(name) => WinTunInterface::new(name.as_str(), Medium::Ip)?, + }; + let poll = Poll::new()?; #[cfg(target_family = "unix")] @@ -258,19 +266,13 @@ impl<'a> TunToProxy<'a> { poll.registry() .register(&mut exit_receiver, EXIT_TOKEN, Interest::READABLE)?; - #[cfg(target_family = "unix")] let config = match tun.capabilities().medium { Medium::Ethernet => Config::new(smoltcp::wire::EthernetAddress([0x02, 0, 0, 0, 0, 0x01]).into()), Medium::Ip => Config::new(smoltcp::wire::HardwareAddress::Ip), Medium::Ieee802154 => todo!(), }; - #[cfg(not(target_family = "unix"))] - let config = Config::new(smoltcp::wire::HardwareAddress::Ip); - #[cfg(target_family = "unix")] let mut device = VirtualTunDevice::new(tun.capabilities()); - #[cfg(not(target_family = "unix"))] - let mut device = VirtualTunDevice::new(DeviceCapabilities::default()); let gateway4: Ipv4Addr = Ipv4Addr::from_str("0.0.0.1")?; let gateway6: Ipv6Addr = Ipv6Addr::from_str("::1")?; @@ -284,7 +286,6 @@ impl<'a> TunToProxy<'a> { iface.set_any_ip(true); let tun = Self { - #[cfg(target_family = "unix")] tun, poll, iface, diff --git a/src/wintuninterface.rs b/src/wintuninterface.rs new file mode 100644 index 0000000..99a87be --- /dev/null +++ b/src/wintuninterface.rs @@ -0,0 +1,154 @@ +use smoltcp::{ + phy::{self, Device, DeviceCapabilities, Medium}, + time::Instant, +}; +use std::{ + io, + net::{IpAddr, Ipv4Addr}, + sync::Arc, + vec::Vec, +}; + +/// A virtual TUN (IP) interface. +pub struct WinTunInterface { + inner: Arc, + mtu: usize, + medium: Medium, +} + +// impl AsRawFd for WinTunInterface { +// fn as_raw_fd(&self) -> RawFd { +// self.inner.borrow().as_raw_fd() +// } +// } + +impl WinTunInterface { + pub fn new(name: &str, medium: Medium) -> io::Result { + let wintun = unsafe { wintun::load() }.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + let tun_name = name; + let adapter = match wintun::Adapter::open(&wintun, tun_name) { + Ok(a) => a, + Err(_) => wintun::Adapter::create(&wintun, tun_name, tun_name, None) + .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?, + }; + + let address = Ipv4Addr::new(10, 1, 0, 33); + let mask = Ipv4Addr::new(255, 255, 255, 0); + let gateway = Some(IpAddr::V4(Ipv4Addr::new(10, 1, 0, 1))); + adapter + .set_network_addresses_tuple(IpAddr::V4(address), IpAddr::V4(mask), gateway) + .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + + let session = adapter + .start_session(wintun::MAX_RING_CAPACITY) + .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + let inner = Arc::new(session); + + // let inner = WinTunInterfaceDesc::new(name, medium)?; + // let mtu = inner.interface_mtu()?; + let mtu = 1500; + Ok(WinTunInterface { inner, mtu, medium }) + } +} + +impl Drop for WinTunInterface { + fn drop(&mut self) { + if let Err(e) = self.inner.shutdown() { + log::error!("phy: failed to shutdown interface: {}", e); + } + } +} + +impl Device for WinTunInterface { + type RxToken<'a> = RxToken; + type TxToken<'a> = TxToken; + + fn capabilities(&self) -> DeviceCapabilities { + let mut v = DeviceCapabilities::default(); + v.max_transmission_unit = self.mtu; + v.medium = self.medium; + v + } + + fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { + let inner = self.inner.clone(); + match inner.receive_blocking() { + Ok(read_pack) => Some(( + RxToken { + buffer: read_pack.bytes().to_vec(), + }, + TxToken { inner }, + )), + Err(err) => { + log::error!("phy: failed to receive packet: {}", err); + None + } + } + + // match inner.recv(&mut buffer[..]) { + // Ok(size) => { + // buffer.resize(size, 0); + // let rx = RxToken { buffer }; + // let tx = TxToken { + // inner: self.inner.clone(), + // }; + // Some((rx, tx)) + // } + // Err(err) if err.kind() == io::ErrorKind::WouldBlock => None, + // Err(err) => panic!("{}", err), + // } + } + + fn transmit(&mut self, _timestamp: Instant) -> Option> { + Some(TxToken { + inner: self.inner.clone(), + }) + } +} + +#[doc(hidden)] +pub struct RxToken { + buffer: Vec, +} + +impl phy::RxToken for RxToken { + fn consume(mut self, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + f(&mut self.buffer[..]) + } +} + +#[doc(hidden)] +pub struct TxToken { + inner: Arc, +} + +impl phy::TxToken for TxToken { + fn consume(self, len: usize, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + let inner = self.inner.clone(); + let mut buffer = vec![0; len]; + let result = f(&mut buffer); + + let write_pack = inner.allocate_send_packet(len as u16); + if let Ok(mut write_pack) = write_pack { + write_pack.bytes_mut().copy_from_slice(&buffer[..]); + inner.send_packet(write_pack); + } else if let Err(err) = write_pack { + log::error!("phy: failed to allocate send packet: {}", err); + } + + // match lower.send(&buffer[..]) { + // Ok(_) => {} + // Err(err) if err.kind() == io::ErrorKind::WouldBlock => { + // log::error!("phy: tx failed due to WouldBlock") + // } + // Err(err) => panic!("{}", err), + // } + result + } +}