diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index 87e63e45..38fa97ad 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -16,7 +16,7 @@ jobs: - name: Setup toolchain uses: actions-rs/toolchain@v1 with: - toolchain: nightly-2021-05-21 + toolchain: nightly-2022-01-01 override: true - name: Run cargo-tarpaulin uses: actions-rs/tarpaulin@v0.1 diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 8916d248..930aec3d 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -27,13 +27,13 @@ jobs: uses: actions/cache@v1 with: path: ~/.cargo - key: ${{ matrix.os }}-nightly-2021-05-21-cargo-v1 + key: ${{ matrix.os }}-nightly-2022-01-01-cargo-v1 restore-keys: | - ${{ matrix.os }}-nightly-2021-05-21-cargo-v1 + ${{ matrix.os }}-nightly-2022-01-01-cargo-v1 - name: Setup toolchain uses: actions-rs/toolchain@v1 with: - toolchain: nightly-2021-05-21 + toolchain: nightly-2022-01-01 target: ${{ matrix.target }} override: true - name: Run tests @@ -74,13 +74,13 @@ jobs: uses: actions/cache@v1 with: path: ~/.cargo - key: ${{ matrix.target }}-nightly-2021-05-21-cargo-v1 + key: ${{ matrix.target }}-nightly-2022-01-01-cargo-v1 restore-keys: | - ${{ matrix.target }}-nightly-2021-05-21-cargo-v1 + ${{ matrix.target }}-nightly-2022-01-01-cargo-v1 - name: Setup toolchain uses: actions-rs/toolchain@v1 with: - toolchain: nightly-2021-05-21 + toolchain: nightly-2022-01-01 target: ${{ matrix.target }} override: true - name: Build release diff --git a/.github/workflows/update_schema.yml b/.github/workflows/update_schema.yml index 46f7b7dd..14692aa6 100644 --- a/.github/workflows/update_schema.yml +++ b/.github/workflows/update_schema.yml @@ -20,13 +20,13 @@ jobs: uses: actions/cache@v1 with: path: ~/.cargo - key: ${{ matrix.os }}-nightly-2021-05-21-cargo-v1 + key: ${{ matrix.os }}-nightly-2022-01-01-cargo-v1 restore-keys: | - ${{ matrix.os }}-nightly-2021-05-21-cargo-v1 + ${{ matrix.os }}-nightly-2022-01-01-cargo-v1 - name: Setup toolchain uses: actions-rs/toolchain@v1 with: - toolchain: nightly-2021-05-21 + toolchain: nightly-2022-01-01 target: ${{ matrix.target }} override: true - name: Generate schema diff --git a/protocol/obfs/src/http_simple.rs b/protocol/obfs/src/http_simple.rs index 78f87aca..d0792c44 100644 --- a/protocol/obfs/src/http_simple.rs +++ b/protocol/obfs/src/http_simple.rs @@ -173,18 +173,6 @@ impl AsyncWrite for Connect { ) -> Poll> { Pin::new(&mut self.inner).poll_shutdown(cx) } - - fn poll_write_vectored( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - bufs: &[io::IoSlice<'_>], - ) -> Poll> { - Pin::new(&mut self.inner).poll_write_vectored(cx, bufs) - } - - fn is_write_vectored(&self) -> bool { - self.inner.is_write_vectored() - } } #[async_trait] diff --git a/protocol/raw/Cargo.toml b/protocol/raw/Cargo.toml index 714fcafb..32e6feff 100644 --- a/protocol/raw/Cargo.toml +++ b/protocol/raw/Cargo.toml @@ -26,6 +26,8 @@ features = ["async"] [target.'cfg(windows)'.dependencies] libc = "0.2" +wintun = "0.2" +once_cell = "1.7.2" [target.'cfg(unix)'.dependencies] nix = "0.23.1" diff --git a/protocol/raw/src/config.rs b/protocol/raw/src/config.rs index 5dc04792..b7d251f6 100644 --- a/protocol/raw/src/config.rs +++ b/protocol/raw/src/config.rs @@ -1,8 +1,10 @@ +use std::net::Ipv4Addr; + use rd_interface::{ config::{Config, NetRef}, prelude::*, }; -use tokio_smoltcp::smoltcp::phy::Medium; +use tokio_smoltcp::smoltcp::{phy::Medium, wire::IpCidr}; #[rd_config] #[serde(rename_all = "lowercase")] @@ -46,6 +48,7 @@ impl Default for Layer { } } +#[cfg(unix)] impl From for tun_crate::Layer { fn from(l: Layer) -> Self { match l { @@ -98,3 +101,11 @@ pub struct RawNetConfig { #[serde(default)] pub forward: bool, } + +pub struct TunTapSetup { + pub name: Option, + pub addr: Ipv4Addr, + pub destination_addr: IpCidr, + pub mtu: usize, + pub layer: Layer, +} diff --git a/protocol/raw/src/device.rs b/protocol/raw/src/device.rs index 4e200e35..f3f5e2f6 100644 --- a/protocol/raw/src/device.rs +++ b/protocol/raw/src/device.rs @@ -1,23 +1,31 @@ -use std::{net::Ipv4Addr, str::FromStr}; +use std::{io, net::Ipv4Addr, str::FromStr}; -use crate::config::{DeviceConfig, RawNetConfig, TunTap}; +use crate::config::{DeviceConfig, RawNetConfig, TunTap, TunTapSetup}; use boxed::BoxedAsyncDevice; pub use interface_info::get_interface_info; use pcap::{Capture, Device}; use rd_interface::{Error, ErrorContext, Result}; use tokio_smoltcp::{ - device::AsyncDevice, - smoltcp::wire::{EthernetAddress, IpCidr}, + device::{AsyncDevice, DeviceCapabilities}, + smoltcp::{ + phy::Checksum, + wire::{EthernetAddress, IpCidr}, + }, }; mod boxed; mod interface_info; #[cfg(unix)] mod unix; +#[cfg(unix)] +use crate::device::unix::get_tun; + +#[cfg(windows)] +mod windows; +#[cfg(windows)] +use crate::device::windows::get_tun; pub fn get_device(config: &RawNetConfig) -> Result<(EthernetAddress, BoxedAsyncDevice)> { - #[cfg(unix)] - use crate::device::unix::{get_tun, TunTapSetup}; let (ethernet_address, device) = match &config.device { DeviceConfig::String(dev) => { let ethernet_address = crate::device::get_interface_info(&dev) @@ -44,11 +52,14 @@ pub fn get_device(config: &RawNetConfig) -> Result<(EthernetAddress, BoxedAsyncD let device = Box::new(get_tun(setup)?); let ethernet_addr = match cfg.tuntap { TunTap::Tun => EthernetAddress::BROADCAST, + #[cfg(unix)] TunTap::Tap => { crate::device::get_interface_info(&device.name()) .context("Failed to get interface info")? .ethernet_address } + #[cfg(windows)] + TunTap::Tap => unreachable!(), }; (ethernet_addr, BoxedAsyncDevice(device)) @@ -80,11 +91,7 @@ fn pcap_device_by_name(name: &str) -> Result { #[cfg(unix)] pub fn get_by_device(device: Device) -> Result { - use std::io; - use tokio_smoltcp::{ - device::{AsyncCapture, DeviceCapabilities}, - smoltcp::phy::Checksum, - }; + use tokio_smoltcp::device::AsyncCapture; let cap = Capture::from_device(device.clone()) .context("Failed to capture device")? @@ -127,9 +134,9 @@ pub fn get_by_device(device: Device) -> Result { } #[cfg(windows)] -pub fn get_by_device(device: Device) -> Result { +pub fn get_by_device(device: Device) -> Result { use tokio::sync::mpsc::{Receiver, Sender}; - use tokio_smoltcp::util::ChannelCapture; + use tokio_smoltcp::device::ChannelCapture; let mut cap = Capture::from_device(device.clone()) .context("Failed to capture device")? @@ -146,7 +153,7 @@ pub fn get_by_device(device: Device) -> Result { .open() .context("Failed to open device")?; - let recv = move |tx: Sender>| loop { + let recv = move |tx: Sender>>| loop { let p = match cap.next().map(|p| p.to_vec()) { Ok(p) => p, Err(pcap::Error::TimeoutExpired) => continue, @@ -155,13 +162,22 @@ pub fn get_by_device(device: Device) -> Result { break; } }; - tx.blocking_send(p).unwrap(); + + tx.blocking_send(Ok(p)).unwrap(); }; let send = move |mut rx: Receiver>| { while let Some(pkt) = rx.blocking_recv() { send.sendpacket(pkt).unwrap(); } }; - let capture = ChannelCapture::new(recv, send); + let mut caps = DeviceCapabilities::default(); + caps.max_transmission_unit = 1500; + caps.checksum.ipv4 = Checksum::Tx; + caps.checksum.tcp = Checksum::Tx; + caps.checksum.udp = Checksum::Tx; + caps.checksum.icmpv4 = Checksum::Tx; + caps.checksum.icmpv6 = Checksum::Tx; + + let capture = ChannelCapture::new(recv, send, caps); Ok(capture) } diff --git a/protocol/raw/src/device/interface_info.rs b/protocol/raw/src/device/interface_info.rs index 1d9700ce..71ed5ba7 100644 --- a/protocol/raw/src/device/interface_info.rs +++ b/protocol/raw/src/device/interface_info.rs @@ -51,9 +51,10 @@ mod unix { #[cfg(windows)] mod windows { - use super::{Error, InterfaceInfo}; + use super::InterfaceInfo; use smoltcp::wire::EthernetAddress; - use std::{mem, ptr}; + use std::{io, mem, ptr}; + use tokio_smoltcp::smoltcp; const NO_ERROR: u32 = 0; const ERROR_INSUFFICIENT_BUFFER: u32 = 122; @@ -77,7 +78,7 @@ mod windows { return None; } - pub fn get_interface_info(name: &str) -> Result { + pub fn get_interface_info(name: &str) -> io::Result { if let Some(intf_guid) = get_guid(name) { let mut size = 0u32; let table: *mut MibIftable; @@ -96,7 +97,7 @@ mod windows { { table = mem::transmute(libc::malloc(size as libc::size_t)); } else { - return Err(Error::NotFound); + return Err(io::ErrorKind::NotFound.into()); } if GetIfTable(table, &mut size as *mut libc::c_ulong, false) == NO_ERROR { @@ -130,7 +131,7 @@ mod windows { libc::free(mem::transmute(table)); } } - Err(Error::NotFound) + Err(io::ErrorKind::NotFound.into()) } pub const MAX_INTERFACE_NAME_LEN: usize = 256; diff --git a/protocol/raw/src/device/unix.rs b/protocol/raw/src/device/unix.rs index dc16a2a2..7b65c187 100644 --- a/protocol/raw/src/device/unix.rs +++ b/protocol/raw/src/device/unix.rs @@ -5,15 +5,12 @@ use std::{ task::{Context, Poll}, }; -use crate::config::Layer; +use crate::config::TunTapSetup; use futures::{ready, Sink, SinkExt, Stream, StreamExt}; use rd_interface::{error::map_other, Result}; use tokio_smoltcp::{ device::{AsyncDevice, DeviceCapabilities, Packet}, - smoltcp::{ - phy::Checksum, - wire::{IpAddress, IpCidr}, - }, + smoltcp::{phy::Checksum, wire::IpAddress}, }; use tokio_util::codec::Framed; use tun_crate::{create_as_async, Configuration, Device, TunPacket, TunPacketCodec}; @@ -24,14 +21,6 @@ pub struct TunAsyncDevice { caps: DeviceCapabilities, } -pub struct TunTapSetup { - pub name: Option, - pub addr: Ipv4Addr, - pub destination_addr: IpCidr, - pub mtu: usize, - pub layer: Layer, -} - pub fn get_tun(cfg: TunTapSetup) -> Result { let mut config = Configuration::default(); let netmask = !0u32 >> (32 - cfg.destination_addr.prefix_len()); diff --git a/protocol/raw/src/device/windows.rs b/protocol/raw/src/device/windows.rs new file mode 100644 index 00000000..ec7ba117 --- /dev/null +++ b/protocol/raw/src/device/windows.rs @@ -0,0 +1,113 @@ +use std::{ + io::{self, Write}, + process::{Command, Stdio}, + sync::Arc, +}; + +use crate::config::{Layer, TunTapSetup}; +use once_cell::sync::OnceCell; +use rd_interface::{error::map_other, Error, Result}; +use tokio::sync::mpsc::{Receiver, Sender}; +use tokio_smoltcp::{ + device::{ChannelCapture, DeviceCapabilities}, + smoltcp::phy::Checksum, +}; +use wintun::{Adapter, Wintun}; + +static WINTUN: OnceCell = OnceCell::new(); +const POOL_NAME: &'static str = "rabbit-digger-pro"; +const DEVICE_NAME: &'static str = "rabbit digger pro"; + +fn get_wintun() -> &'static Wintun { + WINTUN.get_or_init(|| unsafe { wintun::load() }.expect("Failed to load wintun.dll")) +} + +pub fn get_tun(cfg: TunTapSetup) -> Result { + if let Layer::L2 = cfg.layer { + return Err(Error::Other("On windows only support tun".into())); + } + + let adapter = match Adapter::open(get_wintun(), DEVICE_NAME) { + Ok(a) => a, + Err(_) => Adapter::create(&get_wintun(), POOL_NAME, DEVICE_NAME, None) + .map_err(|_| rd_interface::Error::other("Failed to create wintun"))?, + }; + let s1 = Arc::new( + adapter + .start_session(wintun::MAX_RING_CAPACITY) + .map_err(|_| rd_interface::Error::other("Failed to create wintun session"))?, + ); + let s2 = s1.clone(); + + let wintun_adapter_index = adapter + .get_adapter_index() + .map_err(|_| rd_interface::Error::other("Failed to get adapter index"))?; + + run_netsh(format!("set interface {} metric=250", wintun_adapter_index))?; + run_netsh(format!( + "set address {} static {}/{} gateway={} store=active", + wintun_adapter_index, + cfg.addr, + cfg.destination_addr.prefix_len(), + cfg.destination_addr.address(), + ))?; + + let recv = move |tx: Sender>>| loop { + let p = match s1.receive_blocking().map(|p| p.bytes().to_vec()) { + Ok(p) => p, + Err(e) => { + eprintln!("Wintun recv error: {:?}", e); + break; + } + }; + tx.blocking_send(Ok(p)).unwrap(); + }; + let send = move |mut rx: Receiver>| { + while let Some(pkt) = rx.blocking_recv() { + let mut p = match s2.allocate_send_packet(pkt.len() as u16) { + Ok(p) => p, + Err(_) => { + eprintln!("Wintun send error"); + break; + } + }; + p.bytes_mut().copy_from_slice(&pkt); + s2.send_packet(p); + } + }; + + let mut caps = DeviceCapabilities::default(); + caps.medium = cfg.layer.into(); + caps.max_transmission_unit = 1500; + caps.checksum.ipv4 = Checksum::Tx; + caps.checksum.tcp = Checksum::Tx; + caps.checksum.udp = Checksum::Tx; + caps.checksum.icmpv4 = Checksum::Tx; + caps.checksum.icmpv6 = Checksum::Tx; + + let dev = ChannelCapture::new(recv, send, caps); + + Ok(dev) +} + +fn run_netsh(cmd_str: String) -> Result<()> { + let mut cmd = Command::new("netsh"); + cmd.arg("interface").arg("ip").args(cmd_str.split(" ")); + let output = cmd.stdout(Stdio::inherit()).output().map_err(map_other)?; + let status = output.status; + let stdout = output.stdout; + let stderr = output.stderr; + + if !status.success() || (!stdout.is_empty() && stdout != b"Ok.") { + tracing::error!( + "Running process: {:?} failed! Status: {:?}", + cmd_str, + status, + ); + io::stderr().write_all(&stdout)?; + io::stderr().write_all(&stderr)?; + return Err(Error::Other("Failed to run netsh".into())); + } + + Ok(()) +} diff --git a/protocol/raw/src/forward.rs b/protocol/raw/src/forward.rs index 6eb95d00..f60fee37 100644 --- a/protocol/raw/src/forward.rs +++ b/protocol/raw/src/forward.rs @@ -7,7 +7,7 @@ use rd_interface::{Arc, Context, IntoAddress, Net, Result}; use rd_std::util::{connect_tcp, forward_udp}; use tokio::{select, spawn}; use tokio_smoltcp::{ - smoltcp::wire::{IpProtocol, IpVersion}, + smoltcp::wire::{IpCidr, IpProtocol, IpVersion}, Net as SmoltcpNet, RawSocket, TcpListener, }; @@ -18,9 +18,15 @@ mod source; struct Forward { net: Net, map: MapTable, + ip_cidr: IpCidr, } -pub async fn forward_net(net: Net, smoltcp_net: Arc, map: MapTable) -> io::Result<()> { +pub async fn forward_net( + net: Net, + smoltcp_net: Arc, + map: MapTable, + ip_cidr: IpCidr, +) -> io::Result<()> { let tcp_listener = smoltcp_net .tcp_bind(SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), 1).into()) .await?; @@ -28,7 +34,7 @@ pub async fn forward_net(net: Net, smoltcp_net: Arc, map: MapTable) .raw_socket(IpVersion::Ipv4, IpProtocol::Udp) .await?; - let forward = Forward { net, map }; + let forward = Forward { net, map, ip_cidr }; let tcp_task = forward.serve_tcp(tcp_listener); let udp_task = forward.serve_udp(raw_socket); @@ -63,7 +69,7 @@ impl Forward { } } async fn serve_udp(&self, raw: RawSocket) -> Result<()> { - let source = source::Source::new(raw); + let source = source::Source::new(raw, self.ip_cidr); forward_udp::forward_udp(source, self.net.clone()).await?; diff --git a/protocol/raw/src/forward/source.rs b/protocol/raw/src/forward/source.rs index 68f1c7bb..081c6d12 100644 --- a/protocol/raw/src/forward/source.rs +++ b/protocol/raw/src/forward/source.rs @@ -1,6 +1,6 @@ use std::{ io, - net::{SocketAddr, SocketAddrV4}, + net::{IpAddr, SocketAddr, SocketAddrV4}, pin::Pin, task, }; @@ -12,7 +12,7 @@ use tokio_smoltcp::{ smoltcp::{ self, phy::ChecksumCapabilities, - wire::{IpProtocol, Ipv4Address, Ipv4Packet, Ipv4Repr, UdpPacket, UdpRepr}, + wire::{IpCidr, IpProtocol, Ipv4Address, Ipv4Packet, Ipv4Repr, UdpPacket, UdpRepr}, }, RawSocket, }; @@ -21,14 +21,16 @@ pub struct Source { raw: RawSocket, recv_buf: Box<[u8]>, send_buf: Option>, + ip_cidr: IpCidr, } impl Source { - pub fn new(raw: RawSocket) -> Source { + pub fn new(raw: RawSocket, ip_cidr: IpCidr) -> Source { Source { raw, recv_buf: Box::new([0u8; 65536]), send_buf: None, + ip_cidr, } } } @@ -40,13 +42,33 @@ impl Stream for Source { mut self: Pin<&mut Self>, cx: &mut task::Context<'_>, ) -> task::Poll> { - let Source { raw, recv_buf, .. } = &mut *self; + let Source { + raw, + recv_buf, + ip_cidr, + .. + } = &mut *self; let (from, to, data) = loop { let size = ready!(raw.poll_recv(cx, recv_buf))?; match parse_udp(&recv_buf[..size]) { - Ok(v) => break v, + Ok(v) => { + let broadcast = match ip_cidr { + IpCidr::Ipv4(v4) => v4.broadcast().map(Into::into).map(IpAddr::V4), + _ => None, + }; + + let to = v.1; + if broadcast == Some(to.ip()) + || to.ip().is_multicast() + || to.ip() == IpAddr::from(ip_cidr.address()) + { + continue; + } + + break v; + } _ => {} }; }; diff --git a/protocol/raw/src/net.rs b/protocol/raw/src/net.rs index ffdc8ef3..781387a9 100644 --- a/protocol/raw/src/net.rs +++ b/protocol/raw/src/net.rs @@ -68,7 +68,12 @@ impl RawNet { let map = device.get_map(); let smoltcp_net = Arc::new(SmoltcpNet::new(device, net_config)); - forward_handle = Some(tokio::spawn(forward_net(net, smoltcp_net.clone(), map))); + forward_handle = Some(tokio::spawn(forward_net( + net, + smoltcp_net.clone(), + map, + ip_cidr, + ))); smoltcp_net } else { Arc::new(SmoltcpNet::new(device, net_config)) diff --git a/protocol/trojan/src/client/tcp.rs b/protocol/trojan/src/client/tcp.rs index 5e4e2dc6..bb6832c6 100644 --- a/protocol/trojan/src/client/tcp.rs +++ b/protocol/trojan/src/client/tcp.rs @@ -71,18 +71,6 @@ impl AsyncWrite for TrojanTcp { ) -> task::Poll> { Pin::new(&mut self.stream).poll_shutdown(cx) } - - fn poll_write_vectored( - mut self: Pin<&mut Self>, - cx: &mut task::Context<'_>, - bufs: &[io::IoSlice<'_>], - ) -> task::Poll> { - Pin::new(&mut self.stream).poll_write_vectored(cx, bufs) - } - - fn is_write_vectored(&self) -> bool { - self.stream.is_write_vectored() - } } #[async_trait] diff --git a/protocol/trojan/src/tls/rustls.rs b/protocol/trojan/src/tls/rustls.rs index 4a021b93..c7a9c7ff 100644 --- a/protocol/trojan/src/tls/rustls.rs +++ b/protocol/trojan/src/tls/rustls.rs @@ -117,18 +117,6 @@ where fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { Pin::new(&mut self.inner).poll_shutdown(cx) } - - fn poll_write_vectored( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - bufs: &[io::IoSlice<'_>], - ) -> Poll> { - Pin::new(&mut self.inner).poll_write_vectored(cx, bufs) - } - - fn is_write_vectored(&self) -> bool { - self.inner.is_write_vectored() - } } impl PushingStream { diff --git a/rabbit-digger b/rabbit-digger index 2cfb00c7..3dd430f6 160000 --- a/rabbit-digger +++ b/rabbit-digger @@ -1 +1 @@ -Subproject commit 2cfb00c7397416dd008484f52e622e41cec17b81 +Subproject commit 3dd430f62ba047acb585d991ead8368b0a51dd79