mirror of
https://github.com/tun2proxy/tun2proxy.git
synced 2025-04-24 07:46:03 +00:00
Merge branch 'blechschmidt:master' into docker_support
This commit is contained in:
commit
0e86400d06
13 changed files with 241 additions and 104 deletions
7
.github/workflows/format-build.yml
vendored
7
.github/workflows/format-build.yml
vendored
|
@ -35,7 +35,10 @@ jobs:
|
|||
|
||||
clippy:
|
||||
name: Clippy
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
|
@ -48,3 +51,5 @@ jobs:
|
|||
with:
|
||||
command: clippy
|
||||
args: -- -D warnings
|
||||
- name: Build
|
||||
run: cargo build --verbose
|
||||
|
|
11
.github/workflows/install-cross.sh
vendored
Executable file
11
.github/workflows/install-cross.sh
vendored
Executable file
|
@ -0,0 +1,11 @@
|
|||
#!/bin/bash
|
||||
|
||||
curl -s https://api.github.com/repos/cross-rs/cross/releases/latest \
|
||||
| grep cross-x86_64-unknown-linux-gnu.tar.gz \
|
||||
| cut -d : -f 2,3 \
|
||||
| tr -d \" \
|
||||
| wget -qi -
|
||||
|
||||
tar -zxvf cross-x86_64-unknown-linux-gnu.tar.gz -C /usr/bin
|
||||
rm -f cross-x86_64-unknown-linux-gnu.tar.gz
|
||||
|
81
.github/workflows/publish-exe.yml
vendored
81
.github/workflows/publish-exe.yml
vendored
|
@ -3,28 +3,77 @@ on:
|
|||
tags:
|
||||
- "*"
|
||||
|
||||
name: Build and publish executable
|
||||
name: Publish Releases
|
||||
|
||||
jobs:
|
||||
build_publish:
|
||||
name: Build and publish executable
|
||||
runs-on: ubuntu-latest
|
||||
name: Publishing Tasks
|
||||
strategy:
|
||||
matrix:
|
||||
target:
|
||||
- x86_64-unknown-linux-gnu
|
||||
- x86_64-unknown-linux-musl
|
||||
- i686-unknown-linux-musl
|
||||
- aarch64-unknown-linux-gnu
|
||||
- armv7-unknown-linux-gnueabihf
|
||||
- x86_64-apple-darwin
|
||||
- aarch64-apple-darwin
|
||||
- x86_64-pc-windows-msvc
|
||||
- i686-pc-windows-msvc
|
||||
|
||||
include:
|
||||
- target: x86_64-unknown-linux-gnu
|
||||
host_os: ubuntu-latest
|
||||
- target: x86_64-unknown-linux-musl
|
||||
host_os: ubuntu-latest
|
||||
- target: i686-unknown-linux-musl
|
||||
host_os: ubuntu-latest
|
||||
- target: aarch64-unknown-linux-gnu
|
||||
host_os: ubuntu-latest
|
||||
- target: armv7-unknown-linux-gnueabihf
|
||||
host_os: ubuntu-latest
|
||||
- target: x86_64-apple-darwin
|
||||
host_os: macos-latest
|
||||
- target: aarch64-apple-darwin
|
||||
host_os: macos-latest
|
||||
- target: x86_64-pc-windows-msvc
|
||||
host_os: windows-latest
|
||||
- target: i686-pc-windows-msvc
|
||||
host_os: windows-latest
|
||||
|
||||
runs-on: ${{ matrix.host_os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
override: true
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --release --target x86_64-unknown-linux-gnu
|
||||
- name: Rename
|
||||
run: mkdir build && mv target/x86_64-unknown-linux-gnu/release/tun2proxy build/tun2proxy-x86_64
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Prepare
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir publishdir
|
||||
rustup target add ${{ matrix.target }}
|
||||
if [[ "${{ matrix.host_os }}" == "ubuntu-latest" ]]; then
|
||||
sudo .github/workflows/install-cross.sh
|
||||
fi
|
||||
|
||||
- name: Build
|
||||
shell: bash
|
||||
run: |
|
||||
if [[ "${{ matrix.host_os }}" == "ubuntu-latest" ]]; then
|
||||
cross build --all-features --release --target ${{ matrix.target }}
|
||||
else
|
||||
cargo build --all-features --release --target ${{ matrix.target }}
|
||||
fi
|
||||
if [[ "${{ matrix.host_os }}" == "windows-latest" ]]; then
|
||||
powershell Compress-Archive -Path target/${{ matrix.target }}/release/tun2proxy.exe -DestinationPath publishdir/tun2proxy-${{ matrix.target }}.zip
|
||||
elif [[ "${{ matrix.host_os }}" == "macos-latest" ]]; then
|
||||
zip -j publishdir/tun2proxy-${{ matrix.target }}.zip target/${{ matrix.target }}/release/tun2proxy
|
||||
elif [[ "${{ matrix.host_os }}" == "ubuntu-latest" ]]; then
|
||||
zip -j publishdir/tun2proxy-${{ matrix.target }}.zip target/${{ matrix.target }}/release/tun2proxy
|
||||
fi
|
||||
|
||||
- name: Publish
|
||||
uses: softprops/action-gh-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
files: build/*
|
||||
files: publishdir/*
|
||||
|
||||
|
|
18
Cargo.toml
18
Cargo.toml
|
@ -1,26 +1,31 @@
|
|||
[package]
|
||||
authors = ["B. Blechschmidt"]
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
name = "tun2proxy"
|
||||
version = "0.1.5"
|
||||
version = "0.1.6"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "lib"]
|
||||
|
||||
[dependencies]
|
||||
base64 = { version = "0.21" }
|
||||
clap = { version = "4.3", features = ["derive"] }
|
||||
clap = { version = "4.4", features = ["derive"] }
|
||||
ctrlc = "3.4"
|
||||
digest_auth = "0.3"
|
||||
dotenvy = "0.15"
|
||||
env_logger = "0.10"
|
||||
fork = "0.1"
|
||||
hashlink = "0.8"
|
||||
httparse = "1.8"
|
||||
libc = "0.2"
|
||||
log = "0.4"
|
||||
mio = { version = "0.8", features = ["os-poll", "net", "os-ext"] }
|
||||
nix = { version = "0.26", features = ["process", "signal"] }
|
||||
nix = { version = "0.27", features = [
|
||||
"process",
|
||||
"signal",
|
||||
"fs",
|
||||
"mount",
|
||||
"user",
|
||||
] }
|
||||
prctl = "1.0"
|
||||
smoltcp = { version = "0.10", features = ["std", "phy-tuntap_interface"] }
|
||||
socks5-impl = { version = "0.5", default-features = false }
|
||||
|
@ -29,6 +34,9 @@ trust-dns-proto = "0.23"
|
|||
unicase = "2.7"
|
||||
url = "2.4"
|
||||
|
||||
[target.'cfg(target_family="unix")'.dependencies]
|
||||
fork = "0.1"
|
||||
|
||||
[target.'cfg(target_os="android")'.dependencies]
|
||||
android_logger = "0.13"
|
||||
jni = { version = "0.21", default-features = false }
|
||||
|
|
11
README.md
11
README.md
|
@ -9,6 +9,7 @@ A tunnel interface for HTTP and SOCKS proxies on Linux based on [smoltcp](https:
|
|||
- IPv4 and IPv6 support
|
||||
- GFW evasion mechanism for certain use cases (see [issue #35](https://github.com/blechschmidt/tun2proxy/issues/35))
|
||||
- SOCKS5 UDP support
|
||||
- Native support for proxying DNS over TCP
|
||||
|
||||
## Build
|
||||
Clone the repository and `cd` into the project folder. Then run the following:
|
||||
|
@ -91,10 +92,15 @@ Usage: tun2proxy [OPTIONS] --proxy <URL>
|
|||
|
||||
Options:
|
||||
-t, --tun <name> Name of the tun interface [default: tun0]
|
||||
--tun-fd <fd> File descriptor of the tun interface
|
||||
--tun-mtu <mtu> MTU of the tun interface (only with tunnel file descriptor) [default: 1500]
|
||||
-p, --proxy <URL> Proxy URL in the form proto://[username[:password]@]host:port
|
||||
-d, --dns <method> DNS handling [default: virtual] [possible values: virtual, none]
|
||||
-d, --dns <strategy> DNS handling strategy [default: virtual] [possible values: virtual, over-tcp, direct]
|
||||
--dns-addr <IP> DNS resolver address [default: 8.8.8.8]
|
||||
-6, --ipv6-enabled IPv6 enabled
|
||||
-s, --setup <method> Routing and system setup [possible values: auto]
|
||||
--bypass-ip <IP> Public proxy IP used in routing setup which should bypassing the tunnel
|
||||
-v, --verbosity <level> Verbosity level [default: info] [possible values: off, error, warn, info, debug, trace]
|
||||
-h, --help Print help
|
||||
-V, --version Print version
|
||||
```
|
||||
|
@ -146,6 +152,3 @@ asked to open connections to IPv6 destinations. In such a case, you can disable
|
|||
either through `sysctl -w net.ipv6.conf.all.disable_ipv6=1` and `sysctl -w net.ipv6.conf.default.disable_ipv6=1`
|
||||
or through `ip -6 route del default`, which causes the `libc` resolver (and other software) to not issue DNS AAAA
|
||||
requests for IPv6 addresses.
|
||||
|
||||
## TODO
|
||||
- Native support for proxying DNS over TCP or TLS
|
||||
|
|
|
@ -52,6 +52,7 @@ pub enum Error {
|
|||
#[error("{0}")]
|
||||
String(String),
|
||||
|
||||
#[cfg(target_family = "unix")]
|
||||
#[error("nix::errno::Errno {0:?}")]
|
||||
OSError(#[from] nix::errno::Errno),
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ use crate::{
|
|||
error::Error,
|
||||
tun2proxy::{
|
||||
ConnectionInfo, ConnectionManager, Direction, IncomingDataEvent, IncomingDirection, OutgoingDataEvent,
|
||||
OutgoingDirection, TcpProxy,
|
||||
OutgoingDirection, ProxyHandler,
|
||||
},
|
||||
};
|
||||
use base64::Engine;
|
||||
|
@ -317,7 +317,7 @@ impl HttpConnection {
|
|||
}
|
||||
}
|
||||
|
||||
impl TcpProxy for HttpConnection {
|
||||
impl ProxyHandler for HttpConnection {
|
||||
fn get_connection_info(&self) -> &ConnectionInfo {
|
||||
&self.info
|
||||
}
|
||||
|
@ -395,7 +395,7 @@ pub(crate) struct HttpManager {
|
|||
}
|
||||
|
||||
impl ConnectionManager for HttpManager {
|
||||
fn new_tcp_proxy(&self, info: &ConnectionInfo, _: bool) -> Result<Box<dyn TcpProxy>, Error> {
|
||||
fn new_proxy_handler(&self, info: &ConnectionInfo, _: bool) -> Result<Box<dyn ProxyHandler>, Error> {
|
||||
if info.protocol != IpProtocol::Tcp {
|
||||
return Err("Invalid protocol".into());
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ pub struct Proxy {
|
|||
|
||||
pub enum NetworkInterface {
|
||||
Named(String),
|
||||
#[cfg(target_family = "unix")]
|
||||
Fd(std::os::fd::RawFd),
|
||||
}
|
||||
|
||||
|
@ -99,6 +100,7 @@ pub struct Options {
|
|||
virtual_dns: Option<virtdns::VirtualDns>,
|
||||
mtu: Option<usize>,
|
||||
dns_over_tcp: bool,
|
||||
dns_addr: Option<std::net::IpAddr>,
|
||||
ipv6_enabled: bool,
|
||||
}
|
||||
|
||||
|
@ -119,6 +121,11 @@ impl Options {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn with_dns_addr(mut self, addr: Option<std::net::IpAddr>) -> Self {
|
||||
self.dns_addr = addr;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_ipv6_enabled(mut self) -> Self {
|
||||
self.ipv6_enabled = true;
|
||||
self
|
||||
|
|
35
src/main.rs
35
src/main.rs
|
@ -25,13 +25,13 @@ struct Args {
|
|||
#[arg(short, long, value_parser = Proxy::from_url, value_name = "URL")]
|
||||
proxy: Proxy,
|
||||
|
||||
/// DNS handling
|
||||
#[arg(short, long, value_name = "method", value_enum, default_value = "virtual")]
|
||||
/// DNS handling strategy
|
||||
#[arg(short, long, value_name = "strategy", value_enum, default_value = "virtual")]
|
||||
dns: ArgDns,
|
||||
|
||||
/// Enable DNS over TCP
|
||||
#[arg(long)]
|
||||
dns_over_tcp: bool,
|
||||
/// DNS resolver address
|
||||
#[arg(long, value_name = "IP", default_value = "8.8.8.8")]
|
||||
dns_addr: IpAddr,
|
||||
|
||||
/// IPv6 enabled
|
||||
#[arg(short = '6', long)]
|
||||
|
@ -50,10 +50,15 @@ struct Args {
|
|||
verbosity: ArgVerbosity,
|
||||
}
|
||||
|
||||
/// DNS query handling strategy
|
||||
/// - Virtual: Intercept DNS queries and resolve them locally with a fake IP address
|
||||
/// - OverTcp: Use TCP to send DNS queries to the DNS server
|
||||
/// - Direct: Looks as general UDP traffic but change the destination to the DNS server
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, clap::ValueEnum)]
|
||||
enum ArgDns {
|
||||
Virtual,
|
||||
None,
|
||||
OverTcp,
|
||||
Direct,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, clap::ValueEnum)]
|
||||
|
@ -83,23 +88,31 @@ fn main() -> ExitCode {
|
|||
log::info!("Proxy {proxy_type} server: {addr}");
|
||||
|
||||
let mut options = Options::new();
|
||||
if args.dns == ArgDns::Virtual {
|
||||
match args.dns {
|
||||
ArgDns::Virtual => {
|
||||
options = options.with_virtual_dns();
|
||||
}
|
||||
|
||||
if args.dns_over_tcp {
|
||||
ArgDns::OverTcp => {
|
||||
options = options.with_dns_over_tcp();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
options = options.with_dns_addr(Some(args.dns_addr));
|
||||
|
||||
if args.ipv6_enabled {
|
||||
options = options.with_ipv6_enabled();
|
||||
}
|
||||
|
||||
#[allow(unused_assignments)]
|
||||
let interface = match args.tun_fd {
|
||||
None => NetworkInterface::Named(args.tun.clone()),
|
||||
Some(fd) => {
|
||||
Some(_fd) => {
|
||||
options = options.with_mtu(args.tun_mtu);
|
||||
NetworkInterface::Fd(fd)
|
||||
#[cfg(not(target_family = "unix"))]
|
||||
panic!("Not supported");
|
||||
#[cfg(target_family = "unix")]
|
||||
NetworkInterface::Fd(_fd)
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ use crate::{
|
|||
error::{Error, Result},
|
||||
tun2proxy::{
|
||||
ConnectionInfo, ConnectionManager, Direction, IncomingDataEvent, IncomingDirection, OutgoingDataEvent,
|
||||
OutgoingDirection, TcpProxy,
|
||||
OutgoingDirection, ProxyHandler,
|
||||
},
|
||||
};
|
||||
use socks5_impl::protocol::{self, handshake, password_method, Address, AuthMethod, StreamOperation, UserKey, Version};
|
||||
|
@ -268,7 +268,7 @@ impl SocksProxyImpl {
|
|||
}
|
||||
}
|
||||
|
||||
impl TcpProxy for SocksProxyImpl {
|
||||
impl ProxyHandler for SocksProxyImpl {
|
||||
fn get_connection_info(&self) -> &ConnectionInfo {
|
||||
&self.info
|
||||
}
|
||||
|
@ -346,7 +346,7 @@ pub(crate) struct SocksProxyManager {
|
|||
}
|
||||
|
||||
impl ConnectionManager for SocksProxyManager {
|
||||
fn new_tcp_proxy(&self, info: &ConnectionInfo, udp_associate: bool) -> Result<Box<dyn TcpProxy>> {
|
||||
fn new_proxy_handler(&self, info: &ConnectionInfo, udp_associate: bool) -> Result<Box<dyn ProxyHandler>> {
|
||||
use socks5_impl::protocol::Command::{Connect, UdpAssociate};
|
||||
let command = if udp_associate { UdpAssociate } else { Connect };
|
||||
let credentials = self.credentials.clone();
|
||||
|
|
143
src/tun2proxy.rs
143
src/tun2proxy.rs
|
@ -1,20 +1,32 @@
|
|||
#![allow(dead_code)]
|
||||
|
||||
use crate::{dns, error::Error, error::Result, virtdevice::VirtualTunDevice, NetworkInterface, Options};
|
||||
use mio::{event::Event, net::TcpStream, net::UdpSocket, unix::SourceFd, Events, Interest, Poll, Token};
|
||||
#[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_family = "unix")]
|
||||
use smoltcp::phy::{Device, Medium, RxToken, TxToken};
|
||||
use smoltcp::{
|
||||
iface::{Config, Interface, SocketHandle, SocketSet},
|
||||
phy::{Device, Medium, RxToken, TunTapInterface, TxToken},
|
||||
socket::{tcp, tcp::State, udp, udp::UdpMetadata},
|
||||
time::Instant,
|
||||
wire::{IpCidr, IpProtocol, Ipv4Packet, Ipv6Packet, TcpPacket, UdpPacket, UDP_HEADER_LEN},
|
||||
};
|
||||
use socks5_impl::protocol::{Address, StreamOperation, UdpHeader, UserKey};
|
||||
use std::collections::LinkedList;
|
||||
#[cfg(target_family = "unix")]
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
convert::{From, TryFrom},
|
||||
io::{Read, Write},
|
||||
net::{IpAddr, Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr},
|
||||
os::unix::io::AsRawFd,
|
||||
rc::Rc,
|
||||
str::FromStr,
|
||||
};
|
||||
|
@ -174,7 +186,7 @@ struct ConnectionState {
|
|||
smoltcp_handle: Option<SocketHandle>,
|
||||
mio_stream: TcpStream,
|
||||
token: Token,
|
||||
tcp_proxy_handler: Box<dyn TcpProxy>,
|
||||
proxy_handler: Box<dyn ProxyHandler>,
|
||||
close_state: u8,
|
||||
wait_read: bool,
|
||||
wait_write: bool,
|
||||
|
@ -183,10 +195,10 @@ struct ConnectionState {
|
|||
udp_token: Option<Token>,
|
||||
origin_dst: SocketAddr,
|
||||
udp_data_cache: LinkedList<Vec<u8>>,
|
||||
udp_over_tcp_expiry: Option<::std::time::Instant>,
|
||||
dns_over_tcp_expiry: Option<::std::time::Instant>,
|
||||
}
|
||||
|
||||
pub(crate) trait TcpProxy {
|
||||
pub(crate) trait ProxyHandler {
|
||||
fn get_connection_info(&self) -> &ConnectionInfo;
|
||||
fn push_data(&mut self, event: IncomingDataEvent<'_>) -> Result<(), Error>;
|
||||
fn consume_data(&mut self, dir: OutgoingDirection, size: usize);
|
||||
|
@ -198,7 +210,7 @@ pub(crate) trait TcpProxy {
|
|||
}
|
||||
|
||||
pub(crate) trait ConnectionManager {
|
||||
fn new_tcp_proxy(&self, info: &ConnectionInfo, udp_associate: bool) -> Result<Box<dyn TcpProxy>>;
|
||||
fn new_proxy_handler(&self, info: &ConnectionInfo, udp_associate: bool) -> Result<Box<dyn ProxyHandler>>;
|
||||
fn close_connection(&self, info: &ConnectionInfo);
|
||||
fn get_server_addr(&self) -> SocketAddr;
|
||||
fn get_credentials(&self) -> &Option<UserKey>;
|
||||
|
@ -208,7 +220,10 @@ const TUN_TOKEN: Token = Token(0);
|
|||
const EXIT_TOKEN: Token = Token(2);
|
||||
|
||||
pub struct TunToProxy<'a> {
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
tun: TunTapInterface,
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
tun: RawSocket,
|
||||
poll: Poll,
|
||||
iface: Interface,
|
||||
connection_map: HashMap<ConnectionInfo, ConnectionState>,
|
||||
|
@ -218,31 +233,53 @@ pub struct TunToProxy<'a> {
|
|||
device: VirtualTunDevice,
|
||||
options: Options,
|
||||
write_sockets: HashSet<Token>,
|
||||
#[cfg(target_family = "unix")]
|
||||
_exit_receiver: mio::unix::pipe::Receiver,
|
||||
#[cfg(target_family = "unix")]
|
||||
exit_sender: mio::unix::pipe::Sender,
|
||||
}
|
||||
|
||||
impl<'a> TunToProxy<'a> {
|
||||
pub fn new(interface: &NetworkInterface, options: Options) -> Result<Self, Error> {
|
||||
let tun = match interface {
|
||||
pub fn new(_interface: &NetworkInterface, options: Options) -> Result<Self, Error> {
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
let tun = match _interface {
|
||||
NetworkInterface::Named(name) => TunTapInterface::new(name.as_str(), Medium::Ip)?,
|
||||
NetworkInterface::Fd(fd) => TunTapInterface::from_fd(*fd, Medium::Ip, options.mtu.unwrap_or(1500))?,
|
||||
};
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
let tun = match _interface {
|
||||
NetworkInterface::Named(name) => RawSocket::new(name.as_str(), Medium::Ip)?,
|
||||
NetworkInterface::Fd(_fd) => panic!("Not supported"),
|
||||
};
|
||||
|
||||
let poll = Poll::new()?;
|
||||
|
||||
#[cfg(target_family = "unix")]
|
||||
poll.registry()
|
||||
.register(&mut SourceFd(&tun.as_raw_fd()), TUN_TOKEN, Interest::READABLE)?;
|
||||
|
||||
#[cfg(target_family = "unix")]
|
||||
let (exit_sender, mut exit_receiver) = mio::unix::pipe::new()?;
|
||||
#[cfg(target_family = "unix")]
|
||||
poll.registry()
|
||||
.register(&mut exit_receiver, EXIT_TOKEN, Interest::READABLE)?;
|
||||
|
||||
#[cfg(target_family = "unix")]
|
||||
#[rustfmt::skip]
|
||||
let config = match tun.capabilities().medium {
|
||||
Medium::Ethernet => Config::new(smoltcp::wire::EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 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")?;
|
||||
let mut iface = Interface::new(config, &mut device, Instant::now());
|
||||
|
@ -255,6 +292,7 @@ impl<'a> TunToProxy<'a> {
|
|||
iface.set_any_ip(true);
|
||||
|
||||
let tun = Self {
|
||||
#[cfg(target_family = "unix")]
|
||||
tun,
|
||||
poll,
|
||||
iface,
|
||||
|
@ -265,7 +303,9 @@ impl<'a> TunToProxy<'a> {
|
|||
device,
|
||||
options,
|
||||
write_sockets: HashSet::default(),
|
||||
#[cfg(target_family = "unix")]
|
||||
_exit_receiver: exit_receiver,
|
||||
#[cfg(target_family = "unix")]
|
||||
exit_sender,
|
||||
};
|
||||
Ok(tun)
|
||||
|
@ -286,14 +326,15 @@ impl<'a> TunToProxy<'a> {
|
|||
self.iface.poll(Instant::now(), &mut self.device, &mut self.sockets);
|
||||
|
||||
while let Some(vec) = self.device.exfiltrate_packet() {
|
||||
let slice = vec.as_slice();
|
||||
let _slice = vec.as_slice();
|
||||
|
||||
// TODO: Actual write. Replace.
|
||||
#[cfg(target_family = "unix")]
|
||||
self.tun
|
||||
.transmit(Instant::now())
|
||||
.ok_or("tx token not available")?
|
||||
.consume(slice.len(), |buf| {
|
||||
buf[..].clone_from_slice(slice);
|
||||
.consume(_slice.len(), |buf| {
|
||||
buf[..].clone_from_slice(_slice);
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
|
@ -358,10 +399,10 @@ impl<'a> TunToProxy<'a> {
|
|||
let mut closed_ends = 0;
|
||||
if (state.close_state & SERVER_WRITE_CLOSED) == SERVER_WRITE_CLOSED
|
||||
&& !state
|
||||
.tcp_proxy_handler
|
||||
.proxy_handler
|
||||
.have_data(Direction::Incoming(IncomingDirection::FromServer))
|
||||
&& !state
|
||||
.tcp_proxy_handler
|
||||
.proxy_handler
|
||||
.have_data(Direction::Outgoing(OutgoingDirection::ToClient))
|
||||
{
|
||||
if let Some(handle) = state.smoltcp_handle {
|
||||
|
@ -374,10 +415,10 @@ impl<'a> TunToProxy<'a> {
|
|||
|
||||
if (state.close_state & CLIENT_WRITE_CLOSED) == CLIENT_WRITE_CLOSED
|
||||
&& !state
|
||||
.tcp_proxy_handler
|
||||
.proxy_handler
|
||||
.have_data(Direction::Incoming(IncomingDirection::FromClient))
|
||||
&& !state
|
||||
.tcp_proxy_handler
|
||||
.proxy_handler
|
||||
.have_data(Direction::Outgoing(OutgoingDirection::ToServer))
|
||||
{
|
||||
// Close remote server
|
||||
|
@ -411,7 +452,7 @@ impl<'a> TunToProxy<'a> {
|
|||
direction: IncomingDirection::FromClient,
|
||||
buffer: data,
|
||||
};
|
||||
error = state.tcp_proxy_handler.push_data(event);
|
||||
error = state.proxy_handler.push_data(event);
|
||||
(data.len(), ())
|
||||
})?;
|
||||
}
|
||||
|
@ -464,7 +505,7 @@ impl<'a> TunToProxy<'a> {
|
|||
let mut info = info;
|
||||
let port = origin_dst.port();
|
||||
if port == DNS_PORT && info.protocol == IpProtocol::Udp && dns::addr_is_private(&origin_dst) {
|
||||
let dns_addr: SocketAddr = "8.8.8.8:53".parse()?; // TODO: Configurable
|
||||
let dns_addr: SocketAddr = (self.options.dns_addr.ok_or("dns_addr")?, DNS_PORT).into();
|
||||
info.dst = Address::from(dns_addr);
|
||||
}
|
||||
info
|
||||
|
@ -493,9 +534,9 @@ impl<'a> TunToProxy<'a> {
|
|||
if !self.connection_map.contains_key(info) {
|
||||
log::info!("DNS over TCP {} ({})", info, origin_dst);
|
||||
|
||||
let tcp_proxy_handler = manager.new_tcp_proxy(info, false)?;
|
||||
let proxy_handler = manager.new_proxy_handler(info, false)?;
|
||||
let server_addr = manager.get_server_addr();
|
||||
let state = self.create_new_tcp_connection_state(server_addr, origin_dst, tcp_proxy_handler, false)?;
|
||||
let state = self.create_new_tcp_connection_state(server_addr, origin_dst, proxy_handler, false)?;
|
||||
self.connection_map.insert(info.clone(), state);
|
||||
|
||||
// TODO: Move this 3 lines to the function end?
|
||||
|
@ -514,21 +555,21 @@ impl<'a> TunToProxy<'a> {
|
|||
|
||||
let err = "udp over tcp state not find";
|
||||
let state = self.connection_map.get_mut(info).ok_or(err)?;
|
||||
state.udp_over_tcp_expiry = Some(Self::common_udp_life_timeout());
|
||||
state.dns_over_tcp_expiry = Some(Self::common_udp_life_timeout());
|
||||
|
||||
let data_event = IncomingDataEvent {
|
||||
direction: IncomingDirection::FromClient,
|
||||
buffer: &buf,
|
||||
};
|
||||
state.tcp_proxy_handler.push_data(data_event)?;
|
||||
state.proxy_handler.push_data(data_event)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn receive_dns_over_tcp_packet_and_write_to_client(&mut self, info: &ConnectionInfo) -> Result<()> {
|
||||
let err = "udp connection state not found";
|
||||
let state = self.connection_map.get_mut(info).ok_or(err)?;
|
||||
assert!(state.udp_over_tcp_expiry.is_some());
|
||||
state.udp_over_tcp_expiry = Some(Self::common_udp_life_timeout());
|
||||
assert!(state.dns_over_tcp_expiry.is_some());
|
||||
state.dns_over_tcp_expiry = Some(Self::common_udp_life_timeout());
|
||||
|
||||
// Code similar to the code in parent function. TODO: Cleanup.
|
||||
let mut vecbuf = Vec::<u8>::new();
|
||||
|
@ -548,13 +589,13 @@ impl<'a> TunToProxy<'a> {
|
|||
direction: IncomingDirection::FromServer,
|
||||
buffer: &data[0..read],
|
||||
};
|
||||
if let Err(error) = state.tcp_proxy_handler.push_data(data_event) {
|
||||
if let Err(error) = state.proxy_handler.push_data(data_event) {
|
||||
log::error!("{}", error);
|
||||
self.remove_connection(&info.clone())?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let dns_event = state.tcp_proxy_handler.peek_data(OutgoingDirection::ToClient);
|
||||
let dns_event = state.proxy_handler.peek_data(OutgoingDirection::ToClient);
|
||||
|
||||
let mut buf = dns_event.buffer.to_vec();
|
||||
let mut to_send: LinkedList<Vec<u8>> = LinkedList::new();
|
||||
|
@ -574,9 +615,7 @@ impl<'a> TunToProxy<'a> {
|
|||
let ip = dns::extract_ipaddr_from_dns_message(&message);
|
||||
log::trace!("DNS over TCP query result: {} -> {:?}", name, ip);
|
||||
|
||||
state
|
||||
.tcp_proxy_handler
|
||||
.consume_data(OutgoingDirection::ToClient, len + 2);
|
||||
state.proxy_handler.consume_data(OutgoingDirection::ToClient, len + 2);
|
||||
|
||||
if !self.options.ipv6_enabled {
|
||||
dns::remove_ipv6_entries(&mut message);
|
||||
|
@ -597,9 +636,9 @@ impl<'a> TunToProxy<'a> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn udp_over_tcp_timeout_expired(&self, info: &ConnectionInfo) -> bool {
|
||||
fn dns_over_tcp_timeout_expired(&self, info: &ConnectionInfo) -> bool {
|
||||
if let Some(state) = self.connection_map.get(info) {
|
||||
if let Some(expiry) = state.udp_over_tcp_expiry {
|
||||
if let Some(expiry) = state.dns_over_tcp_expiry {
|
||||
return expiry < ::std::time::Instant::now();
|
||||
}
|
||||
}
|
||||
|
@ -609,8 +648,8 @@ impl<'a> TunToProxy<'a> {
|
|||
fn clearup_expired_dns_over_tcp(&mut self) -> Result<()> {
|
||||
let keys = self.connection_map.keys().cloned().collect::<Vec<_>>();
|
||||
for key in keys {
|
||||
if self.udp_over_tcp_timeout_expired(&key) {
|
||||
log::trace!("UDP over TCP timeout: {}", key);
|
||||
if self.dns_over_tcp_timeout_expired(&key) {
|
||||
log::trace!("DNS over TCP timeout: {}", key);
|
||||
self.remove_connection(&key)?;
|
||||
}
|
||||
}
|
||||
|
@ -626,9 +665,9 @@ impl<'a> TunToProxy<'a> {
|
|||
) -> Result<()> {
|
||||
if !self.connection_map.contains_key(info) {
|
||||
log::info!("UDP associate session {} ({})", info, origin_dst);
|
||||
let tcp_proxy_handler = manager.new_tcp_proxy(info, true)?;
|
||||
let proxy_handler = manager.new_proxy_handler(info, true)?;
|
||||
let server_addr = manager.get_server_addr();
|
||||
let state = self.create_new_tcp_connection_state(server_addr, origin_dst, tcp_proxy_handler, true)?;
|
||||
let state = self.create_new_tcp_connection_state(server_addr, origin_dst, proxy_handler, true)?;
|
||||
self.connection_map.insert(info.clone(), state);
|
||||
|
||||
self.expect_smoltcp_send()?;
|
||||
|
@ -648,7 +687,7 @@ impl<'a> TunToProxy<'a> {
|
|||
UdpHeader::new(0, info.dst.clone()).write_to_stream(&mut s5_udp_data)?;
|
||||
s5_udp_data.extend_from_slice(payload);
|
||||
|
||||
if let Some(udp_associate) = state.tcp_proxy_handler.get_udp_associate() {
|
||||
if let Some(udp_associate) = state.proxy_handler.get_udp_associate() {
|
||||
// UDP associate session has been established, we can send packets directly...
|
||||
if let Some(socket) = state.udp_socket.as_ref() {
|
||||
socket.send_to(&s5_udp_data, udp_associate)?;
|
||||
|
@ -677,9 +716,9 @@ impl<'a> TunToProxy<'a> {
|
|||
|
||||
if info.protocol == IpProtocol::Tcp {
|
||||
if _first_packet {
|
||||
let tcp_proxy_handler = manager.new_tcp_proxy(&info, false)?;
|
||||
let proxy_handler = manager.new_proxy_handler(&info, false)?;
|
||||
let server = manager.get_server_addr();
|
||||
let state = self.create_new_tcp_connection_state(server, origin_dst, tcp_proxy_handler, false)?;
|
||||
let state = self.create_new_tcp_connection_state(server, origin_dst, proxy_handler, false)?;
|
||||
self.connection_map.insert(info.clone(), state);
|
||||
|
||||
log::info!("Connect done {} ({})", info, origin_dst);
|
||||
|
@ -732,7 +771,7 @@ impl<'a> TunToProxy<'a> {
|
|||
&mut self,
|
||||
server_addr: SocketAddr,
|
||||
dst: SocketAddr,
|
||||
tcp_proxy_handler: Box<dyn TcpProxy>,
|
||||
proxy_handler: Box<dyn ProxyHandler>,
|
||||
udp_associate: bool,
|
||||
) -> Result<ConnectionState> {
|
||||
let mut socket = tcp::Socket::new(
|
||||
|
@ -767,7 +806,7 @@ impl<'a> TunToProxy<'a> {
|
|||
smoltcp_handle: Some(handle),
|
||||
mio_stream: client,
|
||||
token,
|
||||
tcp_proxy_handler,
|
||||
proxy_handler,
|
||||
close_state: 0,
|
||||
wait_read: true,
|
||||
wait_write: false,
|
||||
|
@ -776,7 +815,7 @@ impl<'a> TunToProxy<'a> {
|
|||
udp_token,
|
||||
origin_dst: dst,
|
||||
udp_data_cache: LinkedList::new(),
|
||||
udp_over_tcp_expiry: None,
|
||||
dns_over_tcp_expiry: None,
|
||||
};
|
||||
Ok(state)
|
||||
}
|
||||
|
@ -819,7 +858,7 @@ impl<'a> TunToProxy<'a> {
|
|||
|
||||
fn write_to_server(&mut self, info: &ConnectionInfo) -> Result<(), Error> {
|
||||
if let Some(state) = self.connection_map.get_mut(info) {
|
||||
let event = state.tcp_proxy_handler.peek_data(OutgoingDirection::ToServer);
|
||||
let event = state.proxy_handler.peek_data(OutgoingDirection::ToServer);
|
||||
let buffer_size = event.buffer.len();
|
||||
if buffer_size == 0 {
|
||||
state.wait_write = false;
|
||||
|
@ -830,9 +869,7 @@ impl<'a> TunToProxy<'a> {
|
|||
let result = state.mio_stream.write(event.buffer);
|
||||
match result {
|
||||
Ok(written) => {
|
||||
state
|
||||
.tcp_proxy_handler
|
||||
.consume_data(OutgoingDirection::ToServer, written);
|
||||
state.proxy_handler.consume_data(OutgoingDirection::ToServer, written);
|
||||
state.wait_write = written < buffer_size;
|
||||
Self::update_mio_socket_interest(&mut self.poll, state)?;
|
||||
}
|
||||
|
@ -856,7 +893,7 @@ impl<'a> TunToProxy<'a> {
|
|||
Some(handle) => handle,
|
||||
None => break,
|
||||
};
|
||||
let event = state.tcp_proxy_handler.peek_data(OutgoingDirection::ToClient);
|
||||
let event = state.proxy_handler.peek_data(OutgoingDirection::ToClient);
|
||||
let buflen = event.buffer.len();
|
||||
let consumed;
|
||||
{
|
||||
|
@ -867,9 +904,7 @@ impl<'a> TunToProxy<'a> {
|
|||
virtual_dns.touch_ip(&IpAddr::from(socket.local_endpoint().unwrap().addr));
|
||||
}
|
||||
consumed = socket.send_slice(event.buffer)?;
|
||||
state
|
||||
.tcp_proxy_handler
|
||||
.consume_data(OutgoingDirection::ToClient, consumed);
|
||||
state.proxy_handler.consume_data(OutgoingDirection::ToClient, consumed);
|
||||
self.expect_smoltcp_send()?;
|
||||
if consumed < buflen {
|
||||
self.write_sockets.insert(token);
|
||||
|
@ -892,6 +927,7 @@ impl<'a> TunToProxy<'a> {
|
|||
|
||||
fn tun_event(&mut self, event: &Event) -> Result<(), Error> {
|
||||
if event.is_readable() {
|
||||
#[cfg(target_family = "unix")]
|
||||
while let Some((rx_token, _)) = self.tun.receive(Instant::now()) {
|
||||
rx_token.consume(|frame| self.receive_tun(frame))?;
|
||||
}
|
||||
|
@ -952,7 +988,7 @@ impl<'a> TunToProxy<'a> {
|
|||
// Try to send the first UDP packets to remote SOCKS5 server for UDP associate session
|
||||
if let Some(state) = self.connection_map.get_mut(info) {
|
||||
if let Some(udp_socket) = state.udp_socket.as_ref() {
|
||||
if let Some(addr) = state.tcp_proxy_handler.get_udp_associate() {
|
||||
if let Some(addr) = state.proxy_handler.get_udp_associate() {
|
||||
// Consume udp_data_cache data
|
||||
while let Some(buf) = state.udp_data_cache.pop_front() {
|
||||
udp_socket.send_to(&buf, addr)?;
|
||||
|
@ -988,7 +1024,7 @@ impl<'a> TunToProxy<'a> {
|
|||
.connection_map
|
||||
.get(&conn_info)
|
||||
.ok_or("")?
|
||||
.tcp_proxy_handler
|
||||
.proxy_handler
|
||||
.connection_established();
|
||||
if self.options.dns_over_tcp && conn_info.dst.port() == DNS_PORT && established {
|
||||
self.receive_dns_over_tcp_packet_and_write_to_client(&conn_info)?;
|
||||
|
@ -1015,14 +1051,14 @@ impl<'a> TunToProxy<'a> {
|
|||
direction: IncomingDirection::FromServer,
|
||||
buffer: &data[0..read],
|
||||
};
|
||||
if let Err(error) = state.tcp_proxy_handler.push_data(data_event) {
|
||||
if let Err(error) = state.proxy_handler.push_data(data_event) {
|
||||
log::error!("{}", error);
|
||||
self.remove_connection(&conn_info.clone())?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// The handler request for reset the server connection
|
||||
if state.tcp_proxy_handler.reset_connection() {
|
||||
if state.proxy_handler.reset_connection() {
|
||||
_ = self.poll.registry().deregister(&mut state.mio_stream);
|
||||
// Closes the connection with the proxy
|
||||
state.mio_stream.shutdown(Shutdown::Both)?;
|
||||
|
@ -1098,6 +1134,7 @@ impl<'a> TunToProxy<'a> {
|
|||
}
|
||||
|
||||
pub fn shutdown(&mut self) -> Result<(), Error> {
|
||||
#[cfg(target_family = "unix")]
|
||||
self.exit_sender.write_all(&[1])?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#![allow(dead_code)]
|
||||
|
||||
use crate::error::Result;
|
||||
use hashlink::{linked_hash_map::RawEntryMut, LruCache};
|
||||
use smoltcp::wire::Ipv4Cidr;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#[cfg(target_os = "linux")]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
extern crate reqwest;
|
||||
|
|
Loading…
Add table
Reference in a new issue