mirror of
https://github.com/tun2proxy/tun2proxy.git
synced 2025-04-24 15:56: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:
|
clippy:
|
||||||
name: Clippy
|
name: Clippy
|
||||||
runs-on: ubuntu-latest
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- uses: actions-rs/toolchain@v1
|
- uses: actions-rs/toolchain@v1
|
||||||
|
@ -48,3 +51,5 @@ jobs:
|
||||||
with:
|
with:
|
||||||
command: clippy
|
command: clippy
|
||||||
args: -- -D warnings
|
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:
|
tags:
|
||||||
- "*"
|
- "*"
|
||||||
|
|
||||||
name: Build and publish executable
|
name: Publish Releases
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build_publish:
|
build_publish:
|
||||||
name: Build and publish executable
|
name: Publishing Tasks
|
||||||
runs-on: ubuntu-latest
|
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:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
- name: Prepare
|
||||||
profile: minimal
|
shell: bash
|
||||||
toolchain: stable
|
run: |
|
||||||
override: true
|
mkdir publishdir
|
||||||
- uses: actions-rs/cargo@v1
|
rustup target add ${{ matrix.target }}
|
||||||
with:
|
if [[ "${{ matrix.host_os }}" == "ubuntu-latest" ]]; then
|
||||||
command: build
|
sudo .github/workflows/install-cross.sh
|
||||||
args: --release --target x86_64-unknown-linux-gnu
|
fi
|
||||||
- name: Rename
|
|
||||||
run: mkdir build && mv target/x86_64-unknown-linux-gnu/release/tun2proxy build/tun2proxy-x86_64
|
- 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
|
- name: Publish
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v1
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
with:
|
with:
|
||||||
files: build/*
|
files: publishdir/*
|
||||||
|
|
||||||
|
|
18
Cargo.toml
18
Cargo.toml
|
@ -1,26 +1,31 @@
|
||||||
[package]
|
[package]
|
||||||
authors = ["B. Blechschmidt"]
|
authors = ["B. Blechschmidt"]
|
||||||
edition = "2018"
|
edition = "2021"
|
||||||
name = "tun2proxy"
|
name = "tun2proxy"
|
||||||
version = "0.1.5"
|
version = "0.1.6"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
crate-type = ["cdylib", "lib"]
|
crate-type = ["cdylib", "lib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
base64 = { version = "0.21" }
|
base64 = { version = "0.21" }
|
||||||
clap = { version = "4.3", features = ["derive"] }
|
clap = { version = "4.4", features = ["derive"] }
|
||||||
ctrlc = "3.4"
|
ctrlc = "3.4"
|
||||||
digest_auth = "0.3"
|
digest_auth = "0.3"
|
||||||
dotenvy = "0.15"
|
dotenvy = "0.15"
|
||||||
env_logger = "0.10"
|
env_logger = "0.10"
|
||||||
fork = "0.1"
|
|
||||||
hashlink = "0.8"
|
hashlink = "0.8"
|
||||||
httparse = "1.8"
|
httparse = "1.8"
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
mio = { version = "0.8", features = ["os-poll", "net", "os-ext"] }
|
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"
|
prctl = "1.0"
|
||||||
smoltcp = { version = "0.10", features = ["std", "phy-tuntap_interface"] }
|
smoltcp = { version = "0.10", features = ["std", "phy-tuntap_interface"] }
|
||||||
socks5-impl = { version = "0.5", default-features = false }
|
socks5-impl = { version = "0.5", default-features = false }
|
||||||
|
@ -29,6 +34,9 @@ trust-dns-proto = "0.23"
|
||||||
unicase = "2.7"
|
unicase = "2.7"
|
||||||
url = "2.4"
|
url = "2.4"
|
||||||
|
|
||||||
|
[target.'cfg(target_family="unix")'.dependencies]
|
||||||
|
fork = "0.1"
|
||||||
|
|
||||||
[target.'cfg(target_os="android")'.dependencies]
|
[target.'cfg(target_os="android")'.dependencies]
|
||||||
android_logger = "0.13"
|
android_logger = "0.13"
|
||||||
jni = { version = "0.21", default-features = false }
|
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
|
- IPv4 and IPv6 support
|
||||||
- GFW evasion mechanism for certain use cases (see [issue #35](https://github.com/blechschmidt/tun2proxy/issues/35))
|
- GFW evasion mechanism for certain use cases (see [issue #35](https://github.com/blechschmidt/tun2proxy/issues/35))
|
||||||
- SOCKS5 UDP support
|
- SOCKS5 UDP support
|
||||||
|
- Native support for proxying DNS over TCP
|
||||||
|
|
||||||
## Build
|
## Build
|
||||||
Clone the repository and `cd` into the project folder. Then run the following:
|
Clone the repository and `cd` into the project folder. Then run the following:
|
||||||
|
@ -91,10 +92,15 @@ Usage: tun2proxy [OPTIONS] --proxy <URL>
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-t, --tun <name> Name of the tun interface [default: tun0]
|
-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
|
-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]
|
-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
|
--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
|
-h, --help Print help
|
||||||
-V, --version Print version
|
-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`
|
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
|
or through `ip -6 route del default`, which causes the `libc` resolver (and other software) to not issue DNS AAAA
|
||||||
requests for IPv6 addresses.
|
requests for IPv6 addresses.
|
||||||
|
|
||||||
## TODO
|
|
||||||
- Native support for proxying DNS over TCP or TLS
|
|
||||||
|
|
|
@ -52,6 +52,7 @@ pub enum Error {
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
String(String),
|
String(String),
|
||||||
|
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
#[error("nix::errno::Errno {0:?}")]
|
#[error("nix::errno::Errno {0:?}")]
|
||||||
OSError(#[from] nix::errno::Errno),
|
OSError(#[from] nix::errno::Errno),
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ use crate::{
|
||||||
error::Error,
|
error::Error,
|
||||||
tun2proxy::{
|
tun2proxy::{
|
||||||
ConnectionInfo, ConnectionManager, Direction, IncomingDataEvent, IncomingDirection, OutgoingDataEvent,
|
ConnectionInfo, ConnectionManager, Direction, IncomingDataEvent, IncomingDirection, OutgoingDataEvent,
|
||||||
OutgoingDirection, TcpProxy,
|
OutgoingDirection, ProxyHandler,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use base64::Engine;
|
use base64::Engine;
|
||||||
|
@ -317,7 +317,7 @@ impl HttpConnection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TcpProxy for HttpConnection {
|
impl ProxyHandler for HttpConnection {
|
||||||
fn get_connection_info(&self) -> &ConnectionInfo {
|
fn get_connection_info(&self) -> &ConnectionInfo {
|
||||||
&self.info
|
&self.info
|
||||||
}
|
}
|
||||||
|
@ -395,7 +395,7 @@ pub(crate) struct HttpManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConnectionManager for 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 {
|
if info.protocol != IpProtocol::Tcp {
|
||||||
return Err("Invalid protocol".into());
|
return Err("Invalid protocol".into());
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ pub struct Proxy {
|
||||||
|
|
||||||
pub enum NetworkInterface {
|
pub enum NetworkInterface {
|
||||||
Named(String),
|
Named(String),
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
Fd(std::os::fd::RawFd),
|
Fd(std::os::fd::RawFd),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,6 +100,7 @@ pub struct Options {
|
||||||
virtual_dns: Option<virtdns::VirtualDns>,
|
virtual_dns: Option<virtdns::VirtualDns>,
|
||||||
mtu: Option<usize>,
|
mtu: Option<usize>,
|
||||||
dns_over_tcp: bool,
|
dns_over_tcp: bool,
|
||||||
|
dns_addr: Option<std::net::IpAddr>,
|
||||||
ipv6_enabled: bool,
|
ipv6_enabled: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,6 +121,11 @@ impl Options {
|
||||||
self
|
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 {
|
pub fn with_ipv6_enabled(mut self) -> Self {
|
||||||
self.ipv6_enabled = true;
|
self.ipv6_enabled = true;
|
||||||
self
|
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")]
|
#[arg(short, long, value_parser = Proxy::from_url, value_name = "URL")]
|
||||||
proxy: Proxy,
|
proxy: Proxy,
|
||||||
|
|
||||||
/// DNS handling
|
/// DNS handling strategy
|
||||||
#[arg(short, long, value_name = "method", value_enum, default_value = "virtual")]
|
#[arg(short, long, value_name = "strategy", value_enum, default_value = "virtual")]
|
||||||
dns: ArgDns,
|
dns: ArgDns,
|
||||||
|
|
||||||
/// Enable DNS over TCP
|
/// DNS resolver address
|
||||||
#[arg(long)]
|
#[arg(long, value_name = "IP", default_value = "8.8.8.8")]
|
||||||
dns_over_tcp: bool,
|
dns_addr: IpAddr,
|
||||||
|
|
||||||
/// IPv6 enabled
|
/// IPv6 enabled
|
||||||
#[arg(short = '6', long)]
|
#[arg(short = '6', long)]
|
||||||
|
@ -50,10 +50,15 @@ struct Args {
|
||||||
verbosity: ArgVerbosity,
|
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)]
|
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, clap::ValueEnum)]
|
||||||
enum ArgDns {
|
enum ArgDns {
|
||||||
Virtual,
|
Virtual,
|
||||||
None,
|
OverTcp,
|
||||||
|
Direct,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, clap::ValueEnum)]
|
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, clap::ValueEnum)]
|
||||||
|
@ -83,23 +88,31 @@ fn main() -> ExitCode {
|
||||||
log::info!("Proxy {proxy_type} server: {addr}");
|
log::info!("Proxy {proxy_type} server: {addr}");
|
||||||
|
|
||||||
let mut options = Options::new();
|
let mut options = Options::new();
|
||||||
if args.dns == ArgDns::Virtual {
|
match args.dns {
|
||||||
|
ArgDns::Virtual => {
|
||||||
options = options.with_virtual_dns();
|
options = options.with_virtual_dns();
|
||||||
}
|
}
|
||||||
|
ArgDns::OverTcp => {
|
||||||
if args.dns_over_tcp {
|
|
||||||
options = options.with_dns_over_tcp();
|
options = options.with_dns_over_tcp();
|
||||||
}
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
options = options.with_dns_addr(Some(args.dns_addr));
|
||||||
|
|
||||||
if args.ipv6_enabled {
|
if args.ipv6_enabled {
|
||||||
options = options.with_ipv6_enabled();
|
options = options.with_ipv6_enabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused_assignments)]
|
||||||
let interface = match args.tun_fd {
|
let interface = match args.tun_fd {
|
||||||
None => NetworkInterface::Named(args.tun.clone()),
|
None => NetworkInterface::Named(args.tun.clone()),
|
||||||
Some(fd) => {
|
Some(_fd) => {
|
||||||
options = options.with_mtu(args.tun_mtu);
|
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},
|
error::{Error, Result},
|
||||||
tun2proxy::{
|
tun2proxy::{
|
||||||
ConnectionInfo, ConnectionManager, Direction, IncomingDataEvent, IncomingDirection, OutgoingDataEvent,
|
ConnectionInfo, ConnectionManager, Direction, IncomingDataEvent, IncomingDirection, OutgoingDataEvent,
|
||||||
OutgoingDirection, TcpProxy,
|
OutgoingDirection, ProxyHandler,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use socks5_impl::protocol::{self, handshake, password_method, Address, AuthMethod, StreamOperation, UserKey, Version};
|
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 {
|
fn get_connection_info(&self) -> &ConnectionInfo {
|
||||||
&self.info
|
&self.info
|
||||||
}
|
}
|
||||||
|
@ -346,7 +346,7 @@ pub(crate) struct SocksProxyManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConnectionManager for 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};
|
use socks5_impl::protocol::Command::{Connect, UdpAssociate};
|
||||||
let command = if udp_associate { UdpAssociate } else { Connect };
|
let command = if udp_associate { UdpAssociate } else { Connect };
|
||||||
let credentials = self.credentials.clone();
|
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 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::{
|
use smoltcp::{
|
||||||
iface::{Config, Interface, SocketHandle, SocketSet},
|
iface::{Config, Interface, SocketHandle, SocketSet},
|
||||||
phy::{Device, Medium, RxToken, TunTapInterface, TxToken},
|
|
||||||
socket::{tcp, tcp::State, udp, udp::UdpMetadata},
|
socket::{tcp, tcp::State, udp, udp::UdpMetadata},
|
||||||
time::Instant,
|
time::Instant,
|
||||||
wire::{IpCidr, IpProtocol, Ipv4Packet, Ipv6Packet, TcpPacket, UdpPacket, UDP_HEADER_LEN},
|
wire::{IpCidr, IpProtocol, Ipv4Packet, Ipv6Packet, TcpPacket, UdpPacket, UDP_HEADER_LEN},
|
||||||
};
|
};
|
||||||
use socks5_impl::protocol::{Address, StreamOperation, UdpHeader, UserKey};
|
use socks5_impl::protocol::{Address, StreamOperation, UdpHeader, UserKey};
|
||||||
use std::collections::LinkedList;
|
use std::collections::LinkedList;
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
|
use std::os::unix::io::AsRawFd;
|
||||||
use std::{
|
use std::{
|
||||||
collections::{HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
convert::{From, TryFrom},
|
convert::{From, TryFrom},
|
||||||
io::{Read, Write},
|
io::{Read, Write},
|
||||||
net::{IpAddr, Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr},
|
net::{IpAddr, Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr},
|
||||||
os::unix::io::AsRawFd,
|
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
};
|
};
|
||||||
|
@ -174,7 +186,7 @@ struct ConnectionState {
|
||||||
smoltcp_handle: Option<SocketHandle>,
|
smoltcp_handle: Option<SocketHandle>,
|
||||||
mio_stream: TcpStream,
|
mio_stream: TcpStream,
|
||||||
token: Token,
|
token: Token,
|
||||||
tcp_proxy_handler: Box<dyn TcpProxy>,
|
proxy_handler: Box<dyn ProxyHandler>,
|
||||||
close_state: u8,
|
close_state: u8,
|
||||||
wait_read: bool,
|
wait_read: bool,
|
||||||
wait_write: bool,
|
wait_write: bool,
|
||||||
|
@ -183,10 +195,10 @@ struct ConnectionState {
|
||||||
udp_token: Option<Token>,
|
udp_token: Option<Token>,
|
||||||
origin_dst: SocketAddr,
|
origin_dst: SocketAddr,
|
||||||
udp_data_cache: LinkedList<Vec<u8>>,
|
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 get_connection_info(&self) -> &ConnectionInfo;
|
||||||
fn push_data(&mut self, event: IncomingDataEvent<'_>) -> Result<(), Error>;
|
fn push_data(&mut self, event: IncomingDataEvent<'_>) -> Result<(), Error>;
|
||||||
fn consume_data(&mut self, dir: OutgoingDirection, size: usize);
|
fn consume_data(&mut self, dir: OutgoingDirection, size: usize);
|
||||||
|
@ -198,7 +210,7 @@ pub(crate) trait TcpProxy {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) trait ConnectionManager {
|
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 close_connection(&self, info: &ConnectionInfo);
|
||||||
fn get_server_addr(&self) -> SocketAddr;
|
fn get_server_addr(&self) -> SocketAddr;
|
||||||
fn get_credentials(&self) -> &Option<UserKey>;
|
fn get_credentials(&self) -> &Option<UserKey>;
|
||||||
|
@ -208,7 +220,10 @@ const TUN_TOKEN: Token = Token(0);
|
||||||
const EXIT_TOKEN: Token = Token(2);
|
const EXIT_TOKEN: Token = Token(2);
|
||||||
|
|
||||||
pub struct TunToProxy<'a> {
|
pub struct TunToProxy<'a> {
|
||||||
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||||
tun: TunTapInterface,
|
tun: TunTapInterface,
|
||||||
|
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||||
|
tun: RawSocket,
|
||||||
poll: Poll,
|
poll: Poll,
|
||||||
iface: Interface,
|
iface: Interface,
|
||||||
connection_map: HashMap<ConnectionInfo, ConnectionState>,
|
connection_map: HashMap<ConnectionInfo, ConnectionState>,
|
||||||
|
@ -218,31 +233,53 @@ pub struct TunToProxy<'a> {
|
||||||
device: VirtualTunDevice,
|
device: VirtualTunDevice,
|
||||||
options: Options,
|
options: Options,
|
||||||
write_sockets: HashSet<Token>,
|
write_sockets: HashSet<Token>,
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
_exit_receiver: mio::unix::pipe::Receiver,
|
_exit_receiver: mio::unix::pipe::Receiver,
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
exit_sender: mio::unix::pipe::Sender,
|
exit_sender: mio::unix::pipe::Sender,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TunToProxy<'a> {
|
impl<'a> TunToProxy<'a> {
|
||||||
pub fn new(interface: &NetworkInterface, options: Options) -> Result<Self, Error> {
|
pub fn new(_interface: &NetworkInterface, options: Options) -> Result<Self, Error> {
|
||||||
let tun = match interface {
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||||
|
let tun = match _interface {
|
||||||
NetworkInterface::Named(name) => TunTapInterface::new(name.as_str(), Medium::Ip)?,
|
NetworkInterface::Named(name) => TunTapInterface::new(name.as_str(), Medium::Ip)?,
|
||||||
NetworkInterface::Fd(fd) => TunTapInterface::from_fd(*fd, Medium::Ip, options.mtu.unwrap_or(1500))?,
|
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()?;
|
let poll = Poll::new()?;
|
||||||
|
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
poll.registry()
|
poll.registry()
|
||||||
.register(&mut SourceFd(&tun.as_raw_fd()), TUN_TOKEN, Interest::READABLE)?;
|
.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()?;
|
let (exit_sender, mut exit_receiver) = mio::unix::pipe::new()?;
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
poll.registry()
|
poll.registry()
|
||||||
.register(&mut exit_receiver, EXIT_TOKEN, Interest::READABLE)?;
|
.register(&mut exit_receiver, EXIT_TOKEN, Interest::READABLE)?;
|
||||||
|
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
let config = match tun.capabilities().medium {
|
let config = match tun.capabilities().medium {
|
||||||
Medium::Ethernet => Config::new(smoltcp::wire::EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]).into()),
|
Medium::Ethernet => Config::new(smoltcp::wire::EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]).into()),
|
||||||
Medium::Ip => Config::new(smoltcp::wire::HardwareAddress::Ip),
|
Medium::Ip => Config::new(smoltcp::wire::HardwareAddress::Ip),
|
||||||
Medium::Ieee802154 => todo!(),
|
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());
|
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 gateway4: Ipv4Addr = Ipv4Addr::from_str("0.0.0.1")?;
|
||||||
let gateway6: Ipv6Addr = Ipv6Addr::from_str("::1")?;
|
let gateway6: Ipv6Addr = Ipv6Addr::from_str("::1")?;
|
||||||
let mut iface = Interface::new(config, &mut device, Instant::now());
|
let mut iface = Interface::new(config, &mut device, Instant::now());
|
||||||
|
@ -255,6 +292,7 @@ impl<'a> TunToProxy<'a> {
|
||||||
iface.set_any_ip(true);
|
iface.set_any_ip(true);
|
||||||
|
|
||||||
let tun = Self {
|
let tun = Self {
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
tun,
|
tun,
|
||||||
poll,
|
poll,
|
||||||
iface,
|
iface,
|
||||||
|
@ -265,7 +303,9 @@ impl<'a> TunToProxy<'a> {
|
||||||
device,
|
device,
|
||||||
options,
|
options,
|
||||||
write_sockets: HashSet::default(),
|
write_sockets: HashSet::default(),
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
_exit_receiver: exit_receiver,
|
_exit_receiver: exit_receiver,
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
exit_sender,
|
exit_sender,
|
||||||
};
|
};
|
||||||
Ok(tun)
|
Ok(tun)
|
||||||
|
@ -286,14 +326,15 @@ impl<'a> TunToProxy<'a> {
|
||||||
self.iface.poll(Instant::now(), &mut self.device, &mut self.sockets);
|
self.iface.poll(Instant::now(), &mut self.device, &mut self.sockets);
|
||||||
|
|
||||||
while let Some(vec) = self.device.exfiltrate_packet() {
|
while let Some(vec) = self.device.exfiltrate_packet() {
|
||||||
let slice = vec.as_slice();
|
let _slice = vec.as_slice();
|
||||||
|
|
||||||
// TODO: Actual write. Replace.
|
// TODO: Actual write. Replace.
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
self.tun
|
self.tun
|
||||||
.transmit(Instant::now())
|
.transmit(Instant::now())
|
||||||
.ok_or("tx token not available")?
|
.ok_or("tx token not available")?
|
||||||
.consume(slice.len(), |buf| {
|
.consume(_slice.len(), |buf| {
|
||||||
buf[..].clone_from_slice(slice);
|
buf[..].clone_from_slice(_slice);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -358,10 +399,10 @@ impl<'a> TunToProxy<'a> {
|
||||||
let mut closed_ends = 0;
|
let mut closed_ends = 0;
|
||||||
if (state.close_state & SERVER_WRITE_CLOSED) == SERVER_WRITE_CLOSED
|
if (state.close_state & SERVER_WRITE_CLOSED) == SERVER_WRITE_CLOSED
|
||||||
&& !state
|
&& !state
|
||||||
.tcp_proxy_handler
|
.proxy_handler
|
||||||
.have_data(Direction::Incoming(IncomingDirection::FromServer))
|
.have_data(Direction::Incoming(IncomingDirection::FromServer))
|
||||||
&& !state
|
&& !state
|
||||||
.tcp_proxy_handler
|
.proxy_handler
|
||||||
.have_data(Direction::Outgoing(OutgoingDirection::ToClient))
|
.have_data(Direction::Outgoing(OutgoingDirection::ToClient))
|
||||||
{
|
{
|
||||||
if let Some(handle) = state.smoltcp_handle {
|
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
|
if (state.close_state & CLIENT_WRITE_CLOSED) == CLIENT_WRITE_CLOSED
|
||||||
&& !state
|
&& !state
|
||||||
.tcp_proxy_handler
|
.proxy_handler
|
||||||
.have_data(Direction::Incoming(IncomingDirection::FromClient))
|
.have_data(Direction::Incoming(IncomingDirection::FromClient))
|
||||||
&& !state
|
&& !state
|
||||||
.tcp_proxy_handler
|
.proxy_handler
|
||||||
.have_data(Direction::Outgoing(OutgoingDirection::ToServer))
|
.have_data(Direction::Outgoing(OutgoingDirection::ToServer))
|
||||||
{
|
{
|
||||||
// Close remote server
|
// Close remote server
|
||||||
|
@ -411,7 +452,7 @@ impl<'a> TunToProxy<'a> {
|
||||||
direction: IncomingDirection::FromClient,
|
direction: IncomingDirection::FromClient,
|
||||||
buffer: data,
|
buffer: data,
|
||||||
};
|
};
|
||||||
error = state.tcp_proxy_handler.push_data(event);
|
error = state.proxy_handler.push_data(event);
|
||||||
(data.len(), ())
|
(data.len(), ())
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
|
@ -464,7 +505,7 @@ impl<'a> TunToProxy<'a> {
|
||||||
let mut info = info;
|
let mut info = info;
|
||||||
let port = origin_dst.port();
|
let port = origin_dst.port();
|
||||||
if port == DNS_PORT && info.protocol == IpProtocol::Udp && dns::addr_is_private(&origin_dst) {
|
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.dst = Address::from(dns_addr);
|
||||||
}
|
}
|
||||||
info
|
info
|
||||||
|
@ -493,9 +534,9 @@ impl<'a> TunToProxy<'a> {
|
||||||
if !self.connection_map.contains_key(info) {
|
if !self.connection_map.contains_key(info) {
|
||||||
log::info!("DNS over TCP {} ({})", info, origin_dst);
|
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 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);
|
self.connection_map.insert(info.clone(), state);
|
||||||
|
|
||||||
// TODO: Move this 3 lines to the function end?
|
// 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 err = "udp over tcp state not find";
|
||||||
let state = self.connection_map.get_mut(info).ok_or(err)?;
|
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 {
|
let data_event = IncomingDataEvent {
|
||||||
direction: IncomingDirection::FromClient,
|
direction: IncomingDirection::FromClient,
|
||||||
buffer: &buf,
|
buffer: &buf,
|
||||||
};
|
};
|
||||||
state.tcp_proxy_handler.push_data(data_event)?;
|
state.proxy_handler.push_data(data_event)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn receive_dns_over_tcp_packet_and_write_to_client(&mut self, info: &ConnectionInfo) -> Result<()> {
|
fn receive_dns_over_tcp_packet_and_write_to_client(&mut self, info: &ConnectionInfo) -> Result<()> {
|
||||||
let err = "udp connection state not found";
|
let err = "udp connection state not found";
|
||||||
let state = self.connection_map.get_mut(info).ok_or(err)?;
|
let state = self.connection_map.get_mut(info).ok_or(err)?;
|
||||||
assert!(state.udp_over_tcp_expiry.is_some());
|
assert!(state.dns_over_tcp_expiry.is_some());
|
||||||
state.udp_over_tcp_expiry = Some(Self::common_udp_life_timeout());
|
state.dns_over_tcp_expiry = Some(Self::common_udp_life_timeout());
|
||||||
|
|
||||||
// Code similar to the code in parent function. TODO: Cleanup.
|
// Code similar to the code in parent function. TODO: Cleanup.
|
||||||
let mut vecbuf = Vec::<u8>::new();
|
let mut vecbuf = Vec::<u8>::new();
|
||||||
|
@ -548,13 +589,13 @@ impl<'a> TunToProxy<'a> {
|
||||||
direction: IncomingDirection::FromServer,
|
direction: IncomingDirection::FromServer,
|
||||||
buffer: &data[0..read],
|
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);
|
log::error!("{}", error);
|
||||||
self.remove_connection(&info.clone())?;
|
self.remove_connection(&info.clone())?;
|
||||||
return Ok(());
|
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 buf = dns_event.buffer.to_vec();
|
||||||
let mut to_send: LinkedList<Vec<u8>> = LinkedList::new();
|
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);
|
let ip = dns::extract_ipaddr_from_dns_message(&message);
|
||||||
log::trace!("DNS over TCP query result: {} -> {:?}", name, ip);
|
log::trace!("DNS over TCP query result: {} -> {:?}", name, ip);
|
||||||
|
|
||||||
state
|
state.proxy_handler.consume_data(OutgoingDirection::ToClient, len + 2);
|
||||||
.tcp_proxy_handler
|
|
||||||
.consume_data(OutgoingDirection::ToClient, len + 2);
|
|
||||||
|
|
||||||
if !self.options.ipv6_enabled {
|
if !self.options.ipv6_enabled {
|
||||||
dns::remove_ipv6_entries(&mut message);
|
dns::remove_ipv6_entries(&mut message);
|
||||||
|
@ -597,9 +636,9 @@ impl<'a> TunToProxy<'a> {
|
||||||
Ok(())
|
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(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();
|
return expiry < ::std::time::Instant::now();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -609,8 +648,8 @@ impl<'a> TunToProxy<'a> {
|
||||||
fn clearup_expired_dns_over_tcp(&mut self) -> Result<()> {
|
fn clearup_expired_dns_over_tcp(&mut self) -> Result<()> {
|
||||||
let keys = self.connection_map.keys().cloned().collect::<Vec<_>>();
|
let keys = self.connection_map.keys().cloned().collect::<Vec<_>>();
|
||||||
for key in keys {
|
for key in keys {
|
||||||
if self.udp_over_tcp_timeout_expired(&key) {
|
if self.dns_over_tcp_timeout_expired(&key) {
|
||||||
log::trace!("UDP over TCP timeout: {}", key);
|
log::trace!("DNS over TCP timeout: {}", key);
|
||||||
self.remove_connection(&key)?;
|
self.remove_connection(&key)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -626,9 +665,9 @@ impl<'a> TunToProxy<'a> {
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
if !self.connection_map.contains_key(info) {
|
if !self.connection_map.contains_key(info) {
|
||||||
log::info!("UDP associate session {} ({})", info, origin_dst);
|
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 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.connection_map.insert(info.clone(), state);
|
||||||
|
|
||||||
self.expect_smoltcp_send()?;
|
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)?;
|
UdpHeader::new(0, info.dst.clone()).write_to_stream(&mut s5_udp_data)?;
|
||||||
s5_udp_data.extend_from_slice(payload);
|
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...
|
// UDP associate session has been established, we can send packets directly...
|
||||||
if let Some(socket) = state.udp_socket.as_ref() {
|
if let Some(socket) = state.udp_socket.as_ref() {
|
||||||
socket.send_to(&s5_udp_data, udp_associate)?;
|
socket.send_to(&s5_udp_data, udp_associate)?;
|
||||||
|
@ -677,9 +716,9 @@ impl<'a> TunToProxy<'a> {
|
||||||
|
|
||||||
if info.protocol == IpProtocol::Tcp {
|
if info.protocol == IpProtocol::Tcp {
|
||||||
if _first_packet {
|
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 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);
|
self.connection_map.insert(info.clone(), state);
|
||||||
|
|
||||||
log::info!("Connect done {} ({})", info, origin_dst);
|
log::info!("Connect done {} ({})", info, origin_dst);
|
||||||
|
@ -732,7 +771,7 @@ impl<'a> TunToProxy<'a> {
|
||||||
&mut self,
|
&mut self,
|
||||||
server_addr: SocketAddr,
|
server_addr: SocketAddr,
|
||||||
dst: SocketAddr,
|
dst: SocketAddr,
|
||||||
tcp_proxy_handler: Box<dyn TcpProxy>,
|
proxy_handler: Box<dyn ProxyHandler>,
|
||||||
udp_associate: bool,
|
udp_associate: bool,
|
||||||
) -> Result<ConnectionState> {
|
) -> Result<ConnectionState> {
|
||||||
let mut socket = tcp::Socket::new(
|
let mut socket = tcp::Socket::new(
|
||||||
|
@ -767,7 +806,7 @@ impl<'a> TunToProxy<'a> {
|
||||||
smoltcp_handle: Some(handle),
|
smoltcp_handle: Some(handle),
|
||||||
mio_stream: client,
|
mio_stream: client,
|
||||||
token,
|
token,
|
||||||
tcp_proxy_handler,
|
proxy_handler,
|
||||||
close_state: 0,
|
close_state: 0,
|
||||||
wait_read: true,
|
wait_read: true,
|
||||||
wait_write: false,
|
wait_write: false,
|
||||||
|
@ -776,7 +815,7 @@ impl<'a> TunToProxy<'a> {
|
||||||
udp_token,
|
udp_token,
|
||||||
origin_dst: dst,
|
origin_dst: dst,
|
||||||
udp_data_cache: LinkedList::new(),
|
udp_data_cache: LinkedList::new(),
|
||||||
udp_over_tcp_expiry: None,
|
dns_over_tcp_expiry: None,
|
||||||
};
|
};
|
||||||
Ok(state)
|
Ok(state)
|
||||||
}
|
}
|
||||||
|
@ -819,7 +858,7 @@ impl<'a> TunToProxy<'a> {
|
||||||
|
|
||||||
fn write_to_server(&mut self, info: &ConnectionInfo) -> Result<(), Error> {
|
fn write_to_server(&mut self, info: &ConnectionInfo) -> Result<(), Error> {
|
||||||
if let Some(state) = self.connection_map.get_mut(info) {
|
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();
|
let buffer_size = event.buffer.len();
|
||||||
if buffer_size == 0 {
|
if buffer_size == 0 {
|
||||||
state.wait_write = false;
|
state.wait_write = false;
|
||||||
|
@ -830,9 +869,7 @@ impl<'a> TunToProxy<'a> {
|
||||||
let result = state.mio_stream.write(event.buffer);
|
let result = state.mio_stream.write(event.buffer);
|
||||||
match result {
|
match result {
|
||||||
Ok(written) => {
|
Ok(written) => {
|
||||||
state
|
state.proxy_handler.consume_data(OutgoingDirection::ToServer, written);
|
||||||
.tcp_proxy_handler
|
|
||||||
.consume_data(OutgoingDirection::ToServer, written);
|
|
||||||
state.wait_write = written < buffer_size;
|
state.wait_write = written < buffer_size;
|
||||||
Self::update_mio_socket_interest(&mut self.poll, state)?;
|
Self::update_mio_socket_interest(&mut self.poll, state)?;
|
||||||
}
|
}
|
||||||
|
@ -856,7 +893,7 @@ impl<'a> TunToProxy<'a> {
|
||||||
Some(handle) => handle,
|
Some(handle) => handle,
|
||||||
None => break,
|
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 buflen = event.buffer.len();
|
||||||
let consumed;
|
let consumed;
|
||||||
{
|
{
|
||||||
|
@ -867,9 +904,7 @@ impl<'a> TunToProxy<'a> {
|
||||||
virtual_dns.touch_ip(&IpAddr::from(socket.local_endpoint().unwrap().addr));
|
virtual_dns.touch_ip(&IpAddr::from(socket.local_endpoint().unwrap().addr));
|
||||||
}
|
}
|
||||||
consumed = socket.send_slice(event.buffer)?;
|
consumed = socket.send_slice(event.buffer)?;
|
||||||
state
|
state.proxy_handler.consume_data(OutgoingDirection::ToClient, consumed);
|
||||||
.tcp_proxy_handler
|
|
||||||
.consume_data(OutgoingDirection::ToClient, consumed);
|
|
||||||
self.expect_smoltcp_send()?;
|
self.expect_smoltcp_send()?;
|
||||||
if consumed < buflen {
|
if consumed < buflen {
|
||||||
self.write_sockets.insert(token);
|
self.write_sockets.insert(token);
|
||||||
|
@ -892,6 +927,7 @@ impl<'a> TunToProxy<'a> {
|
||||||
|
|
||||||
fn tun_event(&mut self, event: &Event) -> Result<(), Error> {
|
fn tun_event(&mut self, event: &Event) -> Result<(), Error> {
|
||||||
if event.is_readable() {
|
if event.is_readable() {
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
while let Some((rx_token, _)) = self.tun.receive(Instant::now()) {
|
while let Some((rx_token, _)) = self.tun.receive(Instant::now()) {
|
||||||
rx_token.consume(|frame| self.receive_tun(frame))?;
|
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
|
// 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(state) = self.connection_map.get_mut(info) {
|
||||||
if let Some(udp_socket) = state.udp_socket.as_ref() {
|
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
|
// Consume udp_data_cache data
|
||||||
while let Some(buf) = state.udp_data_cache.pop_front() {
|
while let Some(buf) = state.udp_data_cache.pop_front() {
|
||||||
udp_socket.send_to(&buf, addr)?;
|
udp_socket.send_to(&buf, addr)?;
|
||||||
|
@ -988,7 +1024,7 @@ impl<'a> TunToProxy<'a> {
|
||||||
.connection_map
|
.connection_map
|
||||||
.get(&conn_info)
|
.get(&conn_info)
|
||||||
.ok_or("")?
|
.ok_or("")?
|
||||||
.tcp_proxy_handler
|
.proxy_handler
|
||||||
.connection_established();
|
.connection_established();
|
||||||
if self.options.dns_over_tcp && conn_info.dst.port() == DNS_PORT && 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)?;
|
self.receive_dns_over_tcp_packet_and_write_to_client(&conn_info)?;
|
||||||
|
@ -1015,14 +1051,14 @@ impl<'a> TunToProxy<'a> {
|
||||||
direction: IncomingDirection::FromServer,
|
direction: IncomingDirection::FromServer,
|
||||||
buffer: &data[0..read],
|
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);
|
log::error!("{}", error);
|
||||||
self.remove_connection(&conn_info.clone())?;
|
self.remove_connection(&conn_info.clone())?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
// The handler request for reset the server connection
|
// 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);
|
_ = self.poll.registry().deregister(&mut state.mio_stream);
|
||||||
// Closes the connection with the proxy
|
// Closes the connection with the proxy
|
||||||
state.mio_stream.shutdown(Shutdown::Both)?;
|
state.mio_stream.shutdown(Shutdown::Both)?;
|
||||||
|
@ -1098,6 +1134,7 @@ impl<'a> TunToProxy<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn shutdown(&mut self) -> Result<(), Error> {
|
pub fn shutdown(&mut self) -> Result<(), Error> {
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
self.exit_sender.write_all(&[1])?;
|
self.exit_sender.write_all(&[1])?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
use hashlink::{linked_hash_map::RawEntryMut, LruCache};
|
use hashlink::{linked_hash_map::RawEntryMut, LruCache};
|
||||||
use smoltcp::wire::Ipv4Cidr;
|
use smoltcp::wire::Ipv4Cidr;
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
extern crate reqwest;
|
extern crate reqwest;
|
||||||
|
|
Loading…
Add table
Reference in a new issue