From c61b6c74cddbc0a8511925a2c08a52159823f879 Mon Sep 17 00:00:00 2001
From: ssrlive <30760636+ssrlive@users.noreply.github.com>
Date: Sun, 23 Jul 2023 02:03:15 +0800
Subject: [PATCH 001/363] swith socks5-impl
---
Cargo.toml | 1 +
README.md | 7 ++-
src/android.rs | 3 +-
src/http.rs | 49 ++++++++-------
src/lib.rs | 29 +++------
src/main.rs | 24 +++-----
src/setup.rs | 25 ++++----
src/socks.rs | 124 ++++++++++---------------------------
src/tun2proxy.rs | 154 ++++++++++++++--------------------------------
src/virtdevice.rs | 9 +--
src/virtdns.rs | 39 ++++++------
11 files changed, 163 insertions(+), 301 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
index cedd349..11adae6 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -23,6 +23,7 @@ mio = { version = "0.8", features = ["os-poll", "net", "os-ext"] }
nix = { version = "0.26", features = ["process", "signal"] }
prctl = "1.0"
smoltcp = { version = "0.10.0", features = ["std", "phy-tuntap_interface"] }
+socks5-impl = { version = "0.4", default-features = false }
thiserror = "1.0"
unicase = "2.6.0"
url = "2.4"
diff --git a/README.md b/README.md
index bc3ab83..1925a4e 100644
--- a/README.md
+++ b/README.md
@@ -30,7 +30,7 @@ Apart from SOCKS5, SOCKS4 and HTTP are supported.
Note that if your proxy is a non-global IP address (e.g. because the proxy is provided by some tunneling tool running
locally), you will additionally need to provide the public IP address of the server through which the traffic is
-actually tunneled. In such a case, the tool will tell you to specify the address through `--setup-ip
` if you
+actually tunneled. In such a case, the tool will tell you to specify the address through `--bypass-ip ` if you
wish to make use of the automated setup feature.
## Manual Setup
@@ -40,6 +40,7 @@ A standard setup, which would route all traffic from your system through the tun
PROXY_TYPE=SOCKS5
PROXY_IP=1.2.3.4
PROXY_PORT=1080
+BYPASS_IP=123.45.67.89
# Create a tunnel interface named tun0 which your user can bind to,
# so we don't need to run tun2proxy as root.
@@ -48,7 +49,7 @@ sudo ip link set tun0 up
# To prevent a routing loop, we add a route to the proxy server that behaves
# like the default route.
-sudo ip route add "$PROXY_IP" $(ip route | grep '^default' | cut -d ' ' -f 2-)
+sudo ip route add "$BYPASS_IP" $(ip route | grep '^default' | cut -d ' ' -f 2-)
# Route all your traffic through tun0 without interfering with the default route.
sudo ip route add 128.0.0.0/1 dev tun0
@@ -92,7 +93,7 @@ Options:
-p, --proxy Proxy URL in the form proto://[username[:password]@]host:port
-d, --dns DNS handling [default: virtual] [possible values: virtual, none]
-s, --setup Routing and system setup [possible values: auto]
- --setup-ip Public proxy IP used in routing setup
+ --bypass-ip Public proxy IP used in routing setup which should bypassing the tunnel
-h, --help Print help
-V, --version Print version
```
diff --git a/src/android.rs b/src/android.rs
index 4f642c8..02d4e5b 100644
--- a/src/android.rs
+++ b/src/android.rs
@@ -1,7 +1,6 @@
#![cfg(target_os = "android")]
-use crate::tun2proxy::TunToProxy;
-use crate::{error::Error, tun_to_proxy, NetworkInterface, Options, Proxy};
+use crate::{error::Error, tun2proxy::TunToProxy, tun_to_proxy, NetworkInterface, Options, Proxy};
use jni::{
objects::{JClass, JString},
sys::{jboolean, jint},
diff --git a/src/http.rs b/src/http.rs
index 3db3787..d5ff54f 100644
--- a/src/http.rs
+++ b/src/http.rs
@@ -1,19 +1,22 @@
-use crate::error::Error;
-use crate::tun2proxy::{
- Connection, ConnectionManager, Destination, Direction, IncomingDataEvent, IncomingDirection,
- OutgoingDataEvent, OutgoingDirection, TcpProxy,
+use crate::{
+ error::Error,
+ tun2proxy::{
+ Connection, ConnectionManager, Direction, IncomingDataEvent, IncomingDirection,
+ OutgoingDataEvent, OutgoingDirection, TcpProxy,
+ },
};
-use crate::Credentials;
use base64::Engine;
use httparse::Response;
use smoltcp::wire::IpProtocol;
-use std::cell::RefCell;
-use std::collections::hash_map::RandomState;
-use std::collections::{HashMap, VecDeque};
-use std::iter::FromIterator;
-use std::net::SocketAddr;
-use std::rc::Rc;
-use std::str;
+use socks5_impl::protocol::{Address, UserKey};
+use std::{
+ cell::RefCell,
+ collections::{hash_map::RandomState, HashMap, VecDeque},
+ iter::FromIterator,
+ net::SocketAddr,
+ rc::Rc,
+ str,
+};
use unicase::UniCase;
#[derive(Eq, PartialEq, Debug)]
@@ -48,8 +51,8 @@ pub struct HttpConnection {
skip: usize,
digest_state: Rc>>,
before: bool,
- credentials: Option,
- destination: Destination,
+ credentials: Option,
+ destination: Address,
}
static PROXY_AUTHENTICATE: &str = "Proxy-Authenticate";
@@ -66,11 +69,11 @@ impl HttpConnection {
) -> Result {
let mut res = Self {
state: HttpState::ExpectResponseHeaders,
- client_inbuf: Default::default(),
- server_inbuf: Default::default(),
- client_outbuf: Default::default(),
- server_outbuf: Default::default(),
- data_buf: Default::default(),
+ client_inbuf: VecDeque::default(),
+ server_inbuf: VecDeque::default(),
+ client_outbuf: VecDeque::default(),
+ server_outbuf: VecDeque::default(),
+ data_buf: VecDeque::default(),
skip: 0,
counter: 0,
crlf_state: 0,
@@ -110,7 +113,7 @@ impl HttpConnection {
match scheme {
AuthenticationScheme::Digest => {
- let uri = format!("{}:{}", self.destination.host, self.destination.port);
+ let uri = self.destination.to_string();
let context = digest_auth::AuthContext::new_with_method(
&credentials.username,
@@ -386,7 +389,7 @@ impl TcpProxy for HttpConnection {
pub(crate) struct HttpManager {
server: SocketAddr,
- credentials: Option,
+ credentials: Option,
digest_state: Rc>>,
}
@@ -416,13 +419,13 @@ impl ConnectionManager for HttpManager {
self.server
}
- fn get_credentials(&self) -> &Option {
+ fn get_credentials(&self) -> &Option {
&self.credentials
}
}
impl HttpManager {
- pub fn new(server: SocketAddr, credentials: Option) -> Rc {
+ pub fn new(server: SocketAddr, credentials: Option) -> Rc {
Rc::new(Self {
server,
credentials,
diff --git a/src/lib.rs b/src/lib.rs
index 6d65302..2c743c0 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,6 +1,8 @@
-use crate::error::Error;
-use crate::socks::SocksVersion;
-use crate::{http::HttpManager, socks::SocksManager, tun2proxy::TunToProxy};
+use crate::{
+ error::Error, http::HttpManager, socks::SocksManager, socks::SocksVersion,
+ tun2proxy::TunToProxy,
+};
+use socks5_impl::protocol::UserKey;
use std::net::{SocketAddr, ToSocketAddrs};
mod android;
@@ -16,7 +18,7 @@ mod virtdns;
pub struct Proxy {
pub proxy_type: ProxyType,
pub addr: SocketAddr,
- pub credentials: Option,
+ pub credentials: Option,
}
pub enum NetworkInterface {
@@ -48,7 +50,7 @@ impl Proxy {
} else {
let username = String::from(url.username());
let password = String::from(url.password().unwrap_or(""));
- Some(Credentials::new(&username, &password))
+ Some(UserKey::new(username, password))
};
let scheme = url.scheme();
@@ -94,7 +96,7 @@ pub struct Options {
impl Options {
pub fn new() -> Self {
- Default::default()
+ Options::default()
}
pub fn with_virtual_dns(mut self) -> Self {
@@ -108,21 +110,6 @@ impl Options {
}
}
-#[derive(Default, Clone, Debug)]
-pub struct Credentials {
- pub(crate) username: String,
- pub(crate) password: String,
-}
-
-impl Credentials {
- pub fn new(username: &str, password: &str) -> Self {
- Self {
- username: String::from(username),
- password: String::from(password),
- }
- }
-}
-
pub fn tun_to_proxy<'a>(
interface: &NetworkInterface,
proxy: &Proxy,
diff --git a/src/main.rs b/src/main.rs
index 43b128e..bcf8519 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,12 +1,7 @@
use clap::Parser;
use env_logger::Env;
-
-use std::net::IpAddr;
-use std::process::ExitCode;
-
-use tun2proxy::error::Error;
-use tun2proxy::{main_entry, Proxy};
-use tun2proxy::{NetworkInterface, Options};
+use std::{net::IpAddr, process::ExitCode};
+use tun2proxy::{error::Error, main_entry, NetworkInterface, Options, Proxy};
#[cfg(target_os = "linux")]
use tun2proxy::setup::{get_default_cidrs, Setup};
@@ -45,9 +40,9 @@ struct Args {
#[arg(short, long, value_name = "method", value_enum)]
setup: Option,
- /// Public proxy IP used in routing setup
+ /// Public proxy IP used in routing setup which should bypassing the tunnel
#[arg(long, value_name = "IP")]
- setup_ip: Option,
+ bypass_ip: Option,
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, clap::ValueEnum)]
@@ -83,12 +78,12 @@ fn main() -> ExitCode {
}
};
- if let Err(e) = (|| -> Result<(), Error> {
+ let block = || -> Result<(), Error> {
#[cfg(target_os = "linux")]
{
let mut setup: Setup;
if args.setup == Some(ArgSetup::Auto) {
- let bypass_tun_ip = match args.setup_ip {
+ let bypass_tun_ip = match args.bypass_ip {
Some(addr) => addr,
None => args.proxy.addr.ip(),
};
@@ -96,7 +91,7 @@ fn main() -> ExitCode {
&args.tun,
&bypass_tun_ip,
get_default_cidrs(),
- args.setup_ip.is_some(),
+ args.bypass_ip.is_some(),
);
setup.configure()?;
@@ -108,10 +103,11 @@ fn main() -> ExitCode {
main_entry(&interface, &args.proxy, options)?;
Ok(())
- })() {
+ };
+ if let Err(e) = block() {
log::error!("{e}");
return ExitCode::FAILURE;
- };
+ }
ExitCode::SUCCESS
}
diff --git a/src/setup.rs b/src/setup.rs
index 7728d0f..8838ab3 100644
--- a/src/setup.rs
+++ b/src/setup.rs
@@ -1,20 +1,17 @@
#![cfg(target_os = "linux")]
use crate::error::Error;
-use smoltcp::wire::IpCidr;
-use std::convert::TryFrom;
-
-use std::ffi::OsStr;
-use std::io::BufRead;
-use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
-
-use std::os::unix::io::RawFd;
-
-use std::process::{Command, Output};
-
-use std::str::FromStr;
-
use fork::Fork;
+use smoltcp::wire::IpCidr;
+use std::{
+ convert::TryFrom,
+ ffi::OsStr,
+ io::BufRead,
+ net::{IpAddr, Ipv4Addr, Ipv6Addr},
+ os::unix::io::RawFd,
+ process::{Command, Output},
+ str::FromStr,
+};
#[derive(Clone)]
pub struct Setup {
@@ -320,7 +317,7 @@ impl Setup {
if self.tunnel_bypass_addr.is_loopback() && !self.allow_private {
log::warn!(
"The proxy address {} is a loopback address. You may need to manually \
- provide --setup-ip to specify the server IP bypassing the tunnel",
+ provide --bypass-ip to specify the server IP bypassing the tunnel",
self.tunnel_bypass_addr
)
}
diff --git a/src/socks.rs b/src/socks.rs
index ed1b477..cb460f8 100644
--- a/src/socks.rs
+++ b/src/socks.rs
@@ -1,16 +1,13 @@
-use std::collections::VecDeque;
-use std::convert::TryFrom;
-use std::net::{IpAddr, SocketAddr};
-use std::rc::Rc;
-
-use smoltcp::wire::IpProtocol;
-
-use crate::error::Error;
-use crate::tun2proxy::{
- Connection, ConnectionManager, DestinationHost, Direction, IncomingDataEvent,
- IncomingDirection, OutgoingDataEvent, OutgoingDirection, TcpProxy,
+use crate::{
+ error::Error,
+ tun2proxy::{
+ Connection, ConnectionManager, Direction, IncomingDataEvent, IncomingDirection,
+ OutgoingDataEvent, OutgoingDirection, TcpProxy,
+ },
};
-use crate::Credentials;
+use smoltcp::wire::IpProtocol;
+use socks5_impl::protocol::{self, Address, AddressType, UserKey};
+use std::{collections::VecDeque, convert::TryFrom, net::SocketAddr, rc::Rc};
#[derive(Eq, PartialEq, Debug)]
#[allow(dead_code)]
@@ -24,32 +21,6 @@ enum SocksState {
Established,
}
-#[repr(u8)]
-#[derive(Copy, Clone, PartialEq, Debug)]
-enum SocksAddressType {
- Ipv4 = 1,
- DomainName = 3,
- Ipv6 = 4,
-}
-
-impl TryFrom for SocksAddressType {
- type Error = Error;
- fn try_from(value: u8) -> Result {
- match value {
- 1 => Ok(SocksAddressType::Ipv4),
- 3 => Ok(SocksAddressType::DomainName),
- 4 => Ok(SocksAddressType::Ipv6),
- _ => Err(format!("Unknown address type: {}", value).into()),
- }
- }
-}
-
-impl From for u8 {
- fn from(value: SocksAddressType) -> Self {
- value as u8
- }
-}
-
#[repr(u8)]
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum SocksVersion {
@@ -57,15 +28,6 @@ pub enum SocksVersion {
V5 = 5,
}
-#[repr(u8)]
-#[derive(Copy, Clone, PartialEq, Debug)]
-#[allow(dead_code)]
-pub enum SocksCommand {
- Connect = 1,
- Bind = 2,
- UdpAssociate = 3,
-}
-
#[allow(dead_code)]
enum SocksAuthentication {
None = 0,
@@ -105,7 +67,7 @@ pub(crate) struct SocksConnection {
server_outbuf: VecDeque,
data_buf: VecDeque,
version: SocksVersion,
- credentials: Option,
+ credentials: Option,
}
impl SocksConnection {
@@ -133,22 +95,20 @@ impl SocksConnection {
let credentials = &self.credentials;
match self.version {
SocksVersion::V4 => {
- self.server_outbuf.extend(&[
- self.version as u8,
- SocksCommand::Connect as u8,
- (self.connection.dst.port >> 8) as u8,
- (self.connection.dst.port & 0xff) as u8,
- ]);
+ self.server_outbuf
+ .extend(&[self.version as u8, protocol::Command::Connect.into()]);
+ self.server_outbuf
+ .extend(self.connection.dst.port().to_be_bytes());
let mut ip_vec = Vec::::new();
let mut name_vec = Vec::::new();
- match &self.connection.dst.host {
- DestinationHost::Address(dst_ip) => {
- match dst_ip {
- IpAddr::V4(ip) => ip_vec.extend(ip.octets().as_ref()),
- IpAddr::V6(_) => return Err("SOCKS4 does not support IPv6".into()),
- };
+ match &self.connection.dst {
+ Address::SocketAddress(SocketAddr::V4(addr)) => {
+ ip_vec.extend(addr.ip().octets().as_ref());
}
- DestinationHost::Hostname(host) => {
+ Address::SocketAddress(SocketAddr::V6(_)) => {
+ return Err("SOCKS4 does not support IPv6".into());
+ }
+ Address::DomainAddress(host, _) => {
ip_vec.extend(&[0, 0, 0, host.len() as u8]);
name_vec.extend(host.as_bytes());
name_vec.push(0);
@@ -246,7 +206,7 @@ impl SocksConnection {
}
fn send_auth_data(&mut self) -> Result<(), Error> {
- let tmp = Credentials::default();
+ let tmp = UserKey::default();
let credentials = self.credentials.as_ref().unwrap_or(&tmp);
self.server_outbuf
.extend(&[1u8, credentials.username.len() as u8]);
@@ -287,8 +247,8 @@ impl SocksConnection {
return Err("SOCKS5 connection unsuccessful.".into());
}
- let message_length = match SocksAddressType::try_from(atyp)? {
- SocksAddressType::DomainName => {
+ let message_length = match AddressType::try_from(atyp)? {
+ AddressType::Domain => {
if self.server_inbuf.len() < 5 {
return Ok(());
}
@@ -297,8 +257,8 @@ impl SocksConnection {
}
7 + (self.server_inbuf[4] as usize)
}
- SocksAddressType::Ipv4 => 10,
- SocksAddressType::Ipv6 => 22,
+ AddressType::IPv4 => 10,
+ AddressType::IPv6 => 22,
};
self.server_inbuf.drain(0..message_length);
@@ -310,30 +270,8 @@ impl SocksConnection {
}
fn send_request(&mut self) -> Result<(), Error> {
- self.server_outbuf.extend(&[5u8, 1, 0]);
- match &self.connection.dst.host {
- DestinationHost::Address(dst_ip) => {
- let cmd = if dst_ip.is_ipv4() {
- SocksAddressType::Ipv4
- } else {
- SocksAddressType::Ipv6
- };
- self.server_outbuf.extend(&[u8::from(cmd)]);
- match dst_ip {
- IpAddr::V4(ip) => self.server_outbuf.extend(ip.octets().as_ref()),
- IpAddr::V6(ip) => self.server_outbuf.extend(ip.octets().as_ref()),
- };
- }
- DestinationHost::Hostname(host) => {
- self.server_outbuf
- .extend(&[u8::from(SocksAddressType::DomainName), host.len() as u8]);
- self.server_outbuf.extend(host.as_bytes());
- }
- }
- self.server_outbuf.extend(&[
- (self.connection.dst.port >> 8) as u8,
- (self.connection.dst.port & 0xff) as u8,
- ]);
+ protocol::Request::new(protocol::Command::Connect, self.connection.dst.clone())
+ .write_to_stream(&mut self.server_outbuf)?;
self.state = SocksState::ReceiveResponse;
self.state_change()
}
@@ -432,7 +370,7 @@ impl TcpProxy for SocksConnection {
pub struct SocksManager {
server: SocketAddr,
- credentials: Option,
+ credentials: Option,
version: SocksVersion,
}
@@ -462,7 +400,7 @@ impl ConnectionManager for SocksManager {
self.server
}
- fn get_credentials(&self) -> &Option {
+ fn get_credentials(&self) -> &Option {
&self.credentials
}
}
@@ -471,7 +409,7 @@ impl SocksManager {
pub fn new(
server: SocketAddr,
version: SocksVersion,
- credentials: Option,
+ credentials: Option,
) -> Rc {
Rc::new(Self {
server,
diff --git a/src/tun2proxy.rs b/src/tun2proxy.rs
index 6bc9529..d5af74a 100644
--- a/src/tun2proxy.rs
+++ b/src/tun2proxy.rs
@@ -1,91 +1,35 @@
-use crate::error::Error;
-use crate::virtdevice::VirtualTunDevice;
-use crate::{Credentials, NetworkInterface, Options};
-use log::{error, info};
-use mio::event::Event;
-use mio::net::TcpStream;
-use mio::unix::SourceFd;
-use mio::{Events, Interest, Poll, Token};
-use smoltcp::iface::{Config, Interface, SocketHandle, SocketSet};
-use smoltcp::phy::{Device, Medium, RxToken, TunTapInterface, TxToken};
-use smoltcp::socket::tcp::State;
-use smoltcp::socket::udp::UdpMetadata;
-use smoltcp::socket::{tcp, udp};
-use smoltcp::time::Instant;
-use smoltcp::wire::{IpCidr, IpProtocol, Ipv4Packet, Ipv6Packet, TcpPacket, UdpPacket};
-use std::collections::{HashMap, HashSet};
-use std::convert::{From, TryFrom};
-use std::io::{Read, Write};
-use std::net::Shutdown::Both;
-use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr};
-use std::os::unix::io::AsRawFd;
-use std::rc::Rc;
-use std::str::FromStr;
-
-#[derive(Hash, Clone, Eq, PartialEq, Debug)]
-pub(crate) enum DestinationHost {
- Address(IpAddr),
- Hostname(String),
-}
-
-impl std::fmt::Display for DestinationHost {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- match self {
- DestinationHost::Address(addr) => addr.fmt(f),
- DestinationHost::Hostname(name) => name.fmt(f),
- }
- }
-}
-
-#[derive(Hash, Clone, Eq, PartialEq, Debug)]
-pub(crate) struct Destination {
- pub(crate) host: DestinationHost,
- pub(crate) port: u16,
-}
-
-impl TryFrom for SocketAddr {
- type Error = Error;
- fn try_from(value: Destination) -> Result {
- let ip = match value.host {
- DestinationHost::Address(addr) => addr,
- DestinationHost::Hostname(e) => {
- return Err(e.into());
- }
- };
- Ok(SocketAddr::new(ip, value.port))
- }
-}
-
-impl From for Destination {
- fn from(addr: SocketAddr) -> Self {
- Self {
- host: DestinationHost::Address(addr.ip()),
- port: addr.port(),
- }
- }
-}
-
-impl std::fmt::Display for Destination {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- if let DestinationHost::Address(IpAddr::V6(addr)) = self.host {
- write!(f, "[{}]:{}", addr, self.port)
- } else {
- write!(f, "{}:{}", self.host, self.port)
- }
- }
-}
+use crate::{error::Error, virtdevice::VirtualTunDevice, NetworkInterface, Options};
+use mio::{event::Event, net::TcpStream, unix::SourceFd, Events, Interest, Poll, Token};
+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},
+};
+use socks5_impl::protocol::{Address, UserKey};
+use std::{
+ collections::{HashMap, HashSet},
+ convert::{From, TryFrom},
+ io::{Read, Write},
+ net::{IpAddr, Ipv4Addr, Ipv6Addr, Shutdown, Shutdown::Both, SocketAddr},
+ os::unix::io::AsRawFd,
+ rc::Rc,
+ str::FromStr,
+};
#[derive(Hash, Clone, Eq, PartialEq, Debug)]
pub(crate) struct Connection {
pub(crate) src: SocketAddr,
- pub(crate) dst: Destination,
+ pub(crate) dst: Address,
pub(crate) proto: IpProtocol,
}
impl Connection {
fn to_named(&self, name: String) -> Self {
let mut result = self.clone();
- result.dst.host = DestinationHost::Hostname(name);
+ result.dst = Address::from((name, result.dst.port()));
+ log::trace!("Replace dst \"{}\" -> \"{}\"", self.dst, result.dst);
result
}
}
@@ -160,7 +104,7 @@ fn connection_tuple(frame: &[u8]) -> Option<(Connection, bool, usize, usize)> {
if let Ok(packet) = Ipv4Packet::new_checked(frame) {
let proto = packet.next_header();
- let mut a: [u8; 4] = Default::default();
+ let mut a = [0_u8; 4];
a.copy_from_slice(packet.src_addr().as_bytes());
let src_addr = IpAddr::from(a);
a.copy_from_slice(packet.dst_addr().as_bytes());
@@ -187,7 +131,7 @@ fn connection_tuple(frame: &[u8]) -> Option<(Connection, bool, usize, usize)> {
// TODO: Support extension headers.
let proto = packet.next_header();
- let mut a: [u8; 16] = Default::default();
+ let mut a = [0_u8; 16];
a.copy_from_slice(packet.src_addr().as_bytes());
let src_addr = IpAddr::from(a);
a.copy_from_slice(packet.dst_addr().as_bytes());
@@ -241,7 +185,7 @@ pub(crate) trait ConnectionManager {
) -> Result>, Error>;
fn close_connection(&self, connection: &Connection);
fn get_server(&self) -> SocketAddr;
- fn get_credentials(&self) -> &Option;
+ fn get_credentials(&self) -> &Option;
}
const TUN_TOKEN: Token = Token(0);
@@ -354,7 +298,7 @@ impl<'a> TunToProxy<'a> {
self.token_to_connection.remove(token);
self.sockets.remove(conn.smoltcp_handle);
_ = self.poll.registry().deregister(&mut conn.mio_stream);
- info!("CLOSE {}", connection);
+ log::info!("CLOSE {}", connection);
}
Ok(())
}
@@ -479,9 +423,7 @@ impl<'a> TunToProxy<'a> {
// A raw packet was received on the tunnel interface.
fn receive_tun(&mut self, frame: &mut [u8]) -> Result<(), Error> {
- if let Some((connection, first_packet, _payload_offset, _payload_size)) =
- connection_tuple(frame)
- {
+ if let Some((connection, first_packet, offset, size)) = connection_tuple(frame) {
let resolved_conn = match &mut self.options.virtdns {
None => connection.clone(),
Some(virt_dns) => {
@@ -494,7 +436,7 @@ impl<'a> TunToProxy<'a> {
}
};
let dst = connection.dst;
- (|| -> Result<(), Error> {
+ let handler = || -> Result<(), Error> {
if resolved_conn.proto == IpProtocol::Tcp {
let cm = self.get_connection_manager(&resolved_conn);
if cm.is_none() {
@@ -540,7 +482,7 @@ impl<'a> TunToProxy<'a> {
self.connections.insert(resolved_conn.clone(), state);
- info!("CONNECT {}", resolved_conn,);
+ log::info!("CONNECT {}", resolved_conn,);
break;
}
}
@@ -562,9 +504,9 @@ impl<'a> TunToProxy<'a> {
// The connection handler builds up the connection or encapsulates the data.
// Therefore, we now expect it to write data to the server.
self.write_to_server(&resolved_conn)?;
- } else if resolved_conn.proto == IpProtocol::Udp && resolved_conn.dst.port == 53 {
+ } else if resolved_conn.proto == IpProtocol::Udp && resolved_conn.dst.port() == 53 {
if let Some(virtual_dns) = &mut self.options.virtdns {
- let payload = &frame[_payload_offset.._payload_offset + _payload_size];
+ let payload = &frame[offset..offset + size];
if let Some(response) = virtual_dns.receive_query(payload) {
let rx_buffer = udp::PacketBuffer::new(
vec![udp::PacketMetadata::EMPTY],
@@ -590,12 +532,11 @@ impl<'a> TunToProxy<'a> {
}
// Otherwise, UDP is not yet supported.
}
- Ok(())
- })()
- .or_else(|error| {
- log::error! {"{error}"}
Ok::<(), Error>(())
- })?;
+ };
+ if let Err(error) = handler() {
+ log::error!("{}", error);
+ }
}
Ok(())
}
@@ -709,7 +650,7 @@ impl<'a> TunToProxy<'a> {
.unwrap()
.get_server();
- (|| -> Result<(), Error> {
+ let mut block = || -> Result<(), Error> {
if event.is_readable() || event.is_read_closed() {
{
let state = self.connections.get_mut(&connection).ok_or(e)?;
@@ -721,7 +662,7 @@ impl<'a> TunToProxy<'a> {
Ok(read_result) => read_result,
Err(error) => {
if error.kind() != std::io::ErrorKind::WouldBlock {
- error!("Read from proxy: {}", error);
+ log::error!("Read from proxy: {}", error);
}
vecbuf.len()
}
@@ -752,7 +693,7 @@ impl<'a> TunToProxy<'a> {
// Closes the connection with the proxy
state.mio_stream.shutdown(Both)?;
- info!("RESET {}", connection);
+ log::info!("RESET {}", connection);
state.mio_stream = TcpStream::connect(server)?;
@@ -785,14 +726,13 @@ impl<'a> TunToProxy<'a> {
if event.is_writable() {
self.write_to_server(&connection)?;
}
-
- Ok(())
- })()
- .or_else(|error| {
- log::error! {"{error}"}
+ Ok::<(), Error>(())
+ };
+ if let Err(error) = block() {
+ log::error!("{}", error);
self.remove_connection(&connection)?;
- Ok(())
- })
+ }
+ Ok(())
}
fn udp_event(&mut self, _event: &Event) {}
@@ -816,10 +756,10 @@ impl<'a> TunToProxy<'a> {
self.send_to_smoltcp()?;
}
Err(e) => {
- if e.kind() != std::io::ErrorKind::Interrupted {
- return Err(e.into());
+ if e.kind() == std::io::ErrorKind::Interrupted {
+ log::warn!("Poll interrupted: \"{e}\", ignored, continue polling");
} else {
- log::warn!("Poll interrupted: {e}")
+ return Err(e.into());
}
}
}
diff --git a/src/virtdevice.rs b/src/virtdevice.rs
index c215159..fc862d9 100644
--- a/src/virtdevice.rs
+++ b/src/virtdevice.rs
@@ -1,6 +1,7 @@
-use smoltcp::phy;
-use smoltcp::phy::{Device, DeviceCapabilities};
-use smoltcp::time::Instant;
+use smoltcp::{
+ phy::{self, Device, DeviceCapabilities},
+ time::Instant,
+};
#[derive(Default)]
pub struct VirtualTunDevice {
@@ -72,7 +73,7 @@ impl VirtualTunDevice {
pub fn new(capabilities: DeviceCapabilities) -> Self {
Self {
capabilities,
- ..Default::default()
+ ..VirtualTunDevice::default()
}
}
}
diff --git a/src/virtdns.rs b/src/virtdns.rs
index 83a07fe..1da5c54 100644
--- a/src/virtdns.rs
+++ b/src/virtdns.rs
@@ -1,11 +1,12 @@
-use hashlink::linked_hash_map::RawEntryMut;
-use hashlink::LruCache;
+use hashlink::{linked_hash_map::RawEntryMut, LruCache};
use smoltcp::wire::Ipv4Cidr;
-use std::collections::HashMap;
-use std::convert::{TryFrom, TryInto};
-use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
-use std::str::FromStr;
-use std::time::{Duration, Instant};
+use std::{
+ collections::HashMap,
+ convert::{TryFrom, TryInto},
+ net::{IpAddr, Ipv4Addr, Ipv6Addr},
+ str::FromStr,
+ time::{Duration, Instant},
+};
const DNS_TTL: u8 = 30; // TTL in DNS replies in seconds
const MAPPING_TIMEOUT: u64 = 60; // Mapping timeout in seconds
@@ -43,7 +44,7 @@ impl Default for VirtualDns {
Self {
next_addr: start_addr.into(),
- name_to_ip: Default::default(),
+ name_to_ip: HashMap::default(),
network_addr: IpAddr::try_from(cidr.network().address().into_address()).unwrap(),
broadcast_addr: IpAddr::try_from(cidr.broadcast().unwrap().into_address()).unwrap(),
lru_cache: LruCache::new_unbounded(),
@@ -53,7 +54,7 @@ impl Default for VirtualDns {
impl VirtualDns {
pub fn new() -> Self {
- Default::default()
+ VirtualDns::default()
}
pub fn receive_query(&mut self, data: &[u8]) -> Option> {
@@ -66,18 +67,17 @@ impl VirtualDns {
// bit 7: Message is not truncated (0)
// bit 8: Recursion desired (1)
let is_supported_query = (data[2] & 0b11111011) == 0b00000001;
- let num_queries = (data[4] as u16) << 8 | data[5] as u16;
+ let num_queries = u16::from_be_bytes(data[4..6].try_into().ok()?);
if !is_supported_query || num_queries != 1 {
return None;
}
- let result = VirtualDns::parse_qname(data, 12);
- let (qname, offset) = result?;
+ let (qname, offset) = VirtualDns::parse_qname(data, 12)?;
if offset + 3 >= data.len() {
return None;
}
- let qtype = (data[offset] as u16) << 8 | data[offset + 1] as u16;
- let qclass = (data[offset + 2] as u16) << 8 | data[offset + 3] as u16;
+ let qtype = u16::from_be_bytes(data[offset..offset + 2].try_into().ok()?);
+ let qclass = u16::from_be_bytes(data[offset + 2..offset + 4].try_into().ok()?);
if qtype != DnsRecordType::A as u16 && qtype != DnsRecordType::AAAA as u16
|| qclass != DnsClass::IN as u16
@@ -121,7 +121,7 @@ impl VirtualDns {
0, 0, 0, DNS_TTL, // TTL
0, 4, // Data length: 4 bytes
]);
- match ip as IpAddr {
+ match ip {
IpAddr::V4(ip) => response.extend(ip.octets().as_ref()),
IpAddr::V6(ip) => response.extend(ip.octets().as_ref()),
};
@@ -191,11 +191,10 @@ impl VirtualDns {
let now = Instant::now();
loop {
- let p = self.lru_cache.iter().next();
- if p.is_none() {
- break;
- }
- let (ip, entry) = p.unwrap();
+ let (ip, entry) = match self.lru_cache.iter().next() {
+ None => break,
+ Some((ip, entry)) => (ip, entry),
+ };
if now > entry.expiry {
let name = entry.name.clone();
self.lru_cache.remove(&ip.clone());
From 6e81e78dfb2c638cecf3341aab7939fed6af6ab5 Mon Sep 17 00:00:00 2001
From: ssrlive <30760636+ssrlive@users.noreply.github.com>
Date: Mon, 24 Jul 2023 16:33:45 +0800
Subject: [PATCH 002/363] socks5 respones
---
src/socks.rs | 47 +++++++++++++++--------------------------------
1 file changed, 15 insertions(+), 32 deletions(-)
diff --git a/src/socks.rs b/src/socks.rs
index cb460f8..47e755e 100644
--- a/src/socks.rs
+++ b/src/socks.rs
@@ -6,8 +6,8 @@ use crate::{
},
};
use smoltcp::wire::IpProtocol;
-use socks5_impl::protocol::{self, Address, AddressType, UserKey};
-use std::{collections::VecDeque, convert::TryFrom, net::SocketAddr, rc::Rc};
+use socks5_impl::protocol::{self, Address, UserKey};
+use std::{collections::VecDeque, net::SocketAddr, rc::Rc};
#[derive(Eq, PartialEq, Debug)]
#[allow(dead_code)]
@@ -231,37 +231,20 @@ impl SocksConnection {
}
fn receive_connection_status(&mut self) -> Result<(), Error> {
- if self.server_inbuf.len() < 4 {
- return Ok(());
- }
- let ver = self.server_inbuf[0];
- let rep = self.server_inbuf[1];
- let _rsv = self.server_inbuf[2];
- let atyp = self.server_inbuf[3];
-
- if ver != 5 {
- return Err("SOCKS5 server replied with an unexpected version.".into());
- }
-
- if rep != 0 {
- return Err("SOCKS5 connection unsuccessful.".into());
- }
-
- let message_length = match AddressType::try_from(atyp)? {
- AddressType::Domain => {
- if self.server_inbuf.len() < 5 {
- return Ok(());
- }
- if self.server_inbuf.len() < 7 + (self.server_inbuf[4] as usize) {
- return Ok(());
- }
- 7 + (self.server_inbuf[4] as usize)
+ let response = protocol::Response::rebuild_from_stream(&mut self.server_inbuf);
+ if let Err(e) = &response {
+ if e.kind() == std::io::ErrorKind::UnexpectedEof {
+ log::trace!("Waiting for more data \"{}\"...", e);
+ return Ok(());
+ } else {
+ return Err(e.to_string().into());
}
- AddressType::IPv4 => 10,
- AddressType::IPv6 => 22,
- };
-
- self.server_inbuf.drain(0..message_length);
+ }
+ let response = response?;
+ if response.reply != protocol::Reply::Succeeded {
+ return Err(format!("SOCKS connection failed: {}", response.reply).into());
+ }
+ assert!(self.server_inbuf.is_empty());
self.server_outbuf.append(&mut self.data_buf);
self.data_buf.clear();
From a00f4b1a8b2e6c883d29dadb342600c718764e11 Mon Sep 17 00:00:00 2001
From: ssrlive <30760636+ssrlive@users.noreply.github.com>
Date: Mon, 24 Jul 2023 20:35:29 +0800
Subject: [PATCH 003/363] socks5 stuff
---
src/socks.rs | 108 +++++++++++++++++----------------------------------
1 file changed, 36 insertions(+), 72 deletions(-)
diff --git a/src/socks.rs b/src/socks.rs
index 47e755e..3c84b20 100644
--- a/src/socks.rs
+++ b/src/socks.rs
@@ -6,7 +6,7 @@ use crate::{
},
};
use smoltcp::wire::IpProtocol;
-use socks5_impl::protocol::{self, Address, UserKey};
+use socks5_impl::protocol::{self, handshake, password_method, Address, AuthMethod, UserKey};
use std::{collections::VecDeque, net::SocketAddr, rc::Rc};
#[derive(Eq, PartialEq, Debug)]
@@ -28,36 +28,6 @@ pub enum SocksVersion {
V5 = 5,
}
-#[allow(dead_code)]
-enum SocksAuthentication {
- None = 0,
- GssApi = 1,
- Password = 2,
- ChallengeHandshake = 3,
- Unassigned = 4,
- Unassigned100 = 100,
-}
-
-#[allow(dead_code)]
-#[repr(u8)]
-#[derive(Debug, Eq, PartialEq)]
-enum SocksReplies {
- Succeeded,
- GeneralFailure,
- ConnectionDisallowed,
- NetworkUnreachable,
- ConnectionRefused,
- TtlExpired,
- CommandUnsupported,
- AddressUnsupported,
-}
-
-impl std::fmt::Display for SocksReplies {
- fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
- write!(f, "{:?}", self)
- }
-}
-
pub(crate) struct SocksConnection {
connection: Connection,
state: SocksState,
@@ -129,24 +99,15 @@ impl SocksConnection {
SocksVersion::V5 => {
// Providing unassigned methods is supposed to bypass China's GFW.
// For details, refer to https://github.com/blechschmidt/tun2proxy/issues/35.
+ let mut methods = vec![
+ AuthMethod::NoAuth,
+ AuthMethod::from(4_u8),
+ AuthMethod::from(100_u8),
+ ];
if credentials.is_some() {
- self.server_outbuf.extend(&[
- self.version as u8,
- 4u8,
- SocksAuthentication::None as u8,
- SocksAuthentication::Password as u8,
- SocksAuthentication::Unassigned as u8,
- SocksAuthentication::Unassigned100 as u8,
- ]);
- } else {
- self.server_outbuf.extend(&[
- self.version as u8,
- 3u8,
- SocksAuthentication::None as u8,
- SocksAuthentication::Unassigned as u8,
- SocksAuthentication::Unassigned100 as u8,
- ]);
+ methods.push(AuthMethod::UserPass);
}
+ handshake::Request::new(methods).write_to_stream(&mut self.server_outbuf)?;
}
}
self.state = SocksState::ServerHello;
@@ -171,26 +132,26 @@ impl SocksConnection {
}
fn receive_server_hello_socks5(&mut self) -> Result<(), Error> {
- if self.server_inbuf.len() < 2 {
- return Ok(());
- }
- if self.server_inbuf[0] != 5 {
- return Err("SOCKS5 server replied with an unexpected version.".into());
+ let response = handshake::Response::rebuild_from_stream(&mut self.server_inbuf);
+ if let Err(e) = &response {
+ if e.kind() == std::io::ErrorKind::UnexpectedEof {
+ log::trace!("receive_server_hello_socks5 need more data \"{}\"...", e);
+ return Ok(());
+ } else {
+ return Err(e.to_string().into());
+ }
}
+ let respones = response?;
+ let auth_method = respones.method;
- let auth_method = self.server_inbuf[1];
-
- if auth_method != SocksAuthentication::None as u8 && self.credentials.is_none()
- || (auth_method != SocksAuthentication::None as u8
- && auth_method != SocksAuthentication::Password as u8)
+ if auth_method != AuthMethod::NoAuth && self.credentials.is_none()
+ || (auth_method != AuthMethod::NoAuth && auth_method != AuthMethod::UserPass)
&& self.credentials.is_some()
{
return Err("SOCKS5 server requires an unsupported authentication method.".into());
}
- self.server_inbuf.drain(0..2);
-
- if auth_method == SocksAuthentication::Password as u8 {
+ if auth_method == AuthMethod::UserPass {
self.state = SocksState::SendAuthData;
} else {
self.state = SocksState::SendRequest;
@@ -208,24 +169,27 @@ impl SocksConnection {
fn send_auth_data(&mut self) -> Result<(), Error> {
let tmp = UserKey::default();
let credentials = self.credentials.as_ref().unwrap_or(&tmp);
- self.server_outbuf
- .extend(&[1u8, credentials.username.len() as u8]);
- self.server_outbuf.extend(credentials.username.as_bytes());
- self.server_outbuf
- .extend(&[credentials.password.len() as u8]);
- self.server_outbuf.extend(credentials.password.as_bytes());
+ let request = password_method::Request::new(&credentials.username, &credentials.password);
+ request.write_to_stream(&mut self.server_outbuf)?;
self.state = SocksState::ReceiveAuthResponse;
self.state_change()
}
fn receive_auth_data(&mut self) -> Result<(), Error> {
- if self.server_inbuf.len() < 2 {
- return Ok(());
+ let response = password_method::Response::rebuild_from_stream(&mut self.server_inbuf);
+ if let Err(e) = &response {
+ if e.kind() == std::io::ErrorKind::UnexpectedEof {
+ log::trace!("receive_auth_data need more data \"{}\"...", e);
+ return Ok(());
+ } else {
+ return Err(e.to_string().into());
+ }
}
- if self.server_inbuf[0] != 1 || self.server_inbuf[1] != 0 {
- return Err("SOCKS authentication failed.".into());
+ assert!(self.server_inbuf.is_empty());
+ let response = response?;
+ if response.status != password_method::Status::Succeeded {
+ return Err(format!("SOCKS authentication failed: {:?}", response.status).into());
}
- self.server_inbuf.drain(0..2);
self.state = SocksState::SendRequest;
self.state_change()
}
@@ -234,7 +198,7 @@ impl SocksConnection {
let response = protocol::Response::rebuild_from_stream(&mut self.server_inbuf);
if let Err(e) = &response {
if e.kind() == std::io::ErrorKind::UnexpectedEof {
- log::trace!("Waiting for more data \"{}\"...", e);
+ log::trace!("receive_connection_status need more data \"{}\"...", e);
return Ok(());
} else {
return Err(e.to_string().into());
From 8d835dc96db3b2771d29ace6ec2fa1afa8774e81 Mon Sep 17 00:00:00 2001
From: ssrlive <30760636+ssrlive@users.noreply.github.com>
Date: Wed, 26 Jul 2023 07:01:48 +0800
Subject: [PATCH 004/363] Unexpected comsuming (#48)
---
Cargo.toml | 8 ++++++--
src/socks.rs | 22 +++++++++++++---------
2 files changed, 19 insertions(+), 11 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
index 11adae6..25a01d5 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -23,7 +23,7 @@ mio = { version = "0.8", features = ["os-poll", "net", "os-ext"] }
nix = { version = "0.26", features = ["process", "signal"] }
prctl = "1.0"
smoltcp = { version = "0.10.0", features = ["std", "phy-tuntap_interface"] }
-socks5-impl = { version = "0.4", default-features = false }
+socks5-impl = { version = "0.5", default-features = false }
thiserror = "1.0"
unicase = "2.6.0"
url = "2.4"
@@ -34,6 +34,10 @@ jni = { version = "0.21", default-features = false }
[dev-dependencies]
ctor = "0.2"
-reqwest = { version = "0.11", default-features = false, features = ["blocking", "json", "rustls-tls"] }
+reqwest = { version = "0.11", default-features = false, features = [
+ "blocking",
+ "json",
+ "rustls-tls",
+] }
serial_test = "2.0"
test-log = "0.2"
diff --git a/src/socks.rs b/src/socks.rs
index 3c84b20..6c57f26 100644
--- a/src/socks.rs
+++ b/src/socks.rs
@@ -6,7 +6,9 @@ use crate::{
},
};
use smoltcp::wire::IpProtocol;
-use socks5_impl::protocol::{self, handshake, password_method, Address, AuthMethod, UserKey};
+use socks5_impl::protocol::{
+ self, handshake, password_method, Address, AuthMethod, StreamOperation, UserKey,
+};
use std::{collections::VecDeque, net::SocketAddr, rc::Rc};
#[derive(Eq, PartialEq, Debug)]
@@ -132,16 +134,17 @@ impl SocksConnection {
}
fn receive_server_hello_socks5(&mut self) -> Result<(), Error> {
- let response = handshake::Response::rebuild_from_stream(&mut self.server_inbuf);
+ let response = handshake::Response::retrieve_from_stream(&mut self.server_inbuf.clone());
if let Err(e) = &response {
if e.kind() == std::io::ErrorKind::UnexpectedEof {
- log::trace!("receive_server_hello_socks5 need more data \"{}\"...", e);
+ log::trace!("receive_server_hello_socks5 needs more data \"{}\"...", e);
return Ok(());
} else {
return Err(e.to_string().into());
}
}
let respones = response?;
+ self.server_inbuf.drain(0..respones.len());
let auth_method = respones.method;
if auth_method != AuthMethod::NoAuth && self.credentials.is_none()
@@ -176,17 +179,18 @@ impl SocksConnection {
}
fn receive_auth_data(&mut self) -> Result<(), Error> {
- let response = password_method::Response::rebuild_from_stream(&mut self.server_inbuf);
+ use password_method::Response;
+ let response = Response::retrieve_from_stream(&mut self.server_inbuf.clone());
if let Err(e) = &response {
if e.kind() == std::io::ErrorKind::UnexpectedEof {
- log::trace!("receive_auth_data need more data \"{}\"...", e);
+ log::trace!("receive_auth_data needs more data \"{}\"...", e);
return Ok(());
} else {
return Err(e.to_string().into());
}
}
- assert!(self.server_inbuf.is_empty());
let response = response?;
+ self.server_inbuf.drain(0..response.len());
if response.status != password_method::Status::Succeeded {
return Err(format!("SOCKS authentication failed: {:?}", response.status).into());
}
@@ -195,20 +199,20 @@ impl SocksConnection {
}
fn receive_connection_status(&mut self) -> Result<(), Error> {
- let response = protocol::Response::rebuild_from_stream(&mut self.server_inbuf);
+ let response = protocol::Response::retrieve_from_stream(&mut self.server_inbuf.clone());
if let Err(e) = &response {
if e.kind() == std::io::ErrorKind::UnexpectedEof {
- log::trace!("receive_connection_status need more data \"{}\"...", e);
+ log::trace!("receive_connection_status needs more data \"{}\"...", e);
return Ok(());
} else {
return Err(e.to_string().into());
}
}
let response = response?;
+ self.server_inbuf.drain(0..response.len());
if response.reply != protocol::Reply::Succeeded {
return Err(format!("SOCKS connection failed: {}", response.reply).into());
}
- assert!(self.server_inbuf.is_empty());
self.server_outbuf.append(&mut self.data_buf);
self.data_buf.clear();
From 1031f586f7ccf1e65de8857767e6dbd07472f330 Mon Sep 17 00:00:00 2001
From: ssrlive <30760636+ssrlive@users.noreply.github.com>
Date: Sat, 5 Aug 2023 15:52:32 +0800
Subject: [PATCH 005/363] Refine code logic
---
.gitignore | 1 +
src/error.rs | 3 +
src/http.rs | 42 ++--
src/lib.rs | 47 ++--
src/socks.rs | 196 ++++++++--------
src/tun2proxy.rs | 594 +++++++++++++++++++++++------------------------
6 files changed, 435 insertions(+), 448 deletions(-)
diff --git a/.gitignore b/.gitignore
index 4ac1fec..bc020c7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+examples/
build/
tmp/
.*
diff --git a/src/error.rs b/src/error.rs
index 86596f4..1a57783 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -27,6 +27,9 @@ pub enum Error {
#[error("smoltcp::socket::tcp::SendError {0:?}")]
Send(#[from] smoltcp::socket::tcp::SendError),
+ #[error("smoltcp::wire::Error {0:?}")]
+ Wire(#[from] smoltcp::wire::Error),
+
#[error("std::str::Utf8Error {0:?}")]
Utf8(#[from] std::str::Utf8Error),
diff --git a/src/http.rs b/src/http.rs
index d5ff54f..5c5d76b 100644
--- a/src/http.rs
+++ b/src/http.rs
@@ -1,7 +1,7 @@
use crate::{
error::Error,
tun2proxy::{
- Connection, ConnectionManager, Direction, IncomingDataEvent, IncomingDirection,
+ ConnectionInfo, ConnectionManager, Direction, IncomingDataEvent, IncomingDirection,
OutgoingDataEvent, OutgoingDirection, TcpProxy,
},
};
@@ -63,8 +63,8 @@ static CONTENT_LENGTH: &str = "Content-Length";
impl HttpConnection {
fn new(
- connection: &Connection,
- manager: Rc,
+ info: &ConnectionInfo,
+ credentials: Option,
digest_state: Rc>>,
) -> Result {
let mut res = Self {
@@ -79,8 +79,8 @@ impl HttpConnection {
crlf_state: 0,
digest_state,
before: false,
- credentials: manager.get_credentials().clone(),
- destination: connection.dst.clone(),
+ credentials,
+ destination: info.dst.clone(),
};
res.send_tunnel_request()?;
@@ -394,28 +394,24 @@ pub(crate) struct HttpManager {
}
impl ConnectionManager for HttpManager {
- fn handles_connection(&self, connection: &Connection) -> bool {
- connection.proto == IpProtocol::Tcp
+ fn handles_connection(&self, info: &ConnectionInfo) -> bool {
+ info.protocol == IpProtocol::Tcp
}
- fn new_connection(
- &self,
- connection: &Connection,
- manager: Rc,
- ) -> Result>, Error> {
- if connection.proto != IpProtocol::Tcp {
- return Ok(None);
+ fn new_tcp_proxy(&self, info: &ConnectionInfo) -> Result, Error> {
+ if info.protocol != IpProtocol::Tcp {
+ return Err("Invalid protocol".into());
}
- Ok(Some(Box::new(HttpConnection::new(
- connection,
- manager,
+ Ok(Box::new(HttpConnection::new(
+ info,
+ self.credentials.clone(),
self.digest_state.clone(),
- )?)))
+ )?))
}
- fn close_connection(&self, _: &Connection) {}
+ fn close_connection(&self, _: &ConnectionInfo) {}
- fn get_server(&self) -> SocketAddr {
+ fn get_server_addr(&self) -> SocketAddr {
self.server
}
@@ -425,11 +421,11 @@ impl ConnectionManager for HttpManager {
}
impl HttpManager {
- pub fn new(server: SocketAddr, credentials: Option) -> Rc {
- Rc::new(Self {
+ pub fn new(server: SocketAddr, credentials: Option) -> Self {
+ Self {
server,
credentials,
digest_state: Rc::new(RefCell::new(None)),
- })
+ }
}
}
diff --git a/src/lib.rs b/src/lib.rs
index 2c743c0..98f69c6 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,9 +1,10 @@
-use crate::{
- error::Error, http::HttpManager, socks::SocksManager, socks::SocksVersion,
- tun2proxy::TunToProxy,
+use crate::{error::Error, http::HttpManager, socks::SocksProxyManager, tun2proxy::TunToProxy};
+use socks5_impl::protocol::{UserKey, Version};
+use std::{
+ net::{SocketAddr, ToSocketAddrs},
+ rc::Rc,
};
-use socks5_impl::protocol::UserKey;
-use std::net::{SocketAddr, ToSocketAddrs};
+use tun2proxy::ConnectionManager;
mod android;
pub mod error;
@@ -90,7 +91,7 @@ impl std::fmt::Display for ProxyType {
#[derive(Default)]
pub struct Options {
- virtdns: Option,
+ virtual_dns: Option,
mtu: Option,
}
@@ -100,7 +101,7 @@ impl Options {
}
pub fn with_virtual_dns(mut self) -> Self {
- self.virtdns = Some(virtdns::VirtualDns::new());
+ self.virtual_dns = Some(virtdns::VirtualDns::new());
self
}
@@ -116,25 +117,18 @@ pub fn tun_to_proxy<'a>(
options: Options,
) -> Result, Error> {
let mut ttp = TunToProxy::new(interface, options)?;
- match proxy.proxy_type {
- ProxyType::Socks4 => {
- ttp.add_connection_manager(SocksManager::new(
- proxy.addr,
- SocksVersion::V4,
- proxy.credentials.clone(),
- ));
- }
- ProxyType::Socks5 => {
- ttp.add_connection_manager(SocksManager::new(
- proxy.addr,
- SocksVersion::V5,
- proxy.credentials.clone(),
- ));
- }
+ let credentials = proxy.credentials.clone();
+ let server = proxy.addr;
+ let mgr = match proxy.proxy_type {
+ ProxyType::Socks4 => Rc::new(SocksProxyManager::new(server, Version::V4, credentials))
+ as Rc,
+ ProxyType::Socks5 => Rc::new(SocksProxyManager::new(server, Version::V5, credentials))
+ as Rc,
ProxyType::Http => {
- ttp.add_connection_manager(HttpManager::new(proxy.addr, proxy.credentials.clone()));
+ Rc::new(HttpManager::new(server, credentials)) as Rc
}
- }
+ };
+ ttp.add_connection_manager(mgr);
Ok(ttp)
}
@@ -143,6 +137,7 @@ pub fn main_entry(
proxy: &Proxy,
options: Options,
) -> Result<(), Error> {
- let ttp = tun_to_proxy(interface, proxy, options);
- ttp?.run()
+ let mut ttp = tun_to_proxy(interface, proxy, options)?;
+ ttp.run()?;
+ Ok(())
}
diff --git a/src/socks.rs b/src/socks.rs
index 6c57f26..d5fd6ad 100644
--- a/src/socks.rs
+++ b/src/socks.rs
@@ -1,15 +1,15 @@
use crate::{
error::Error,
tun2proxy::{
- Connection, ConnectionManager, Direction, IncomingDataEvent, IncomingDirection,
+ ConnectionInfo, ConnectionManager, Direction, IncomingDataEvent, IncomingDirection,
OutgoingDataEvent, OutgoingDirection, TcpProxy,
},
};
use smoltcp::wire::IpProtocol;
use socks5_impl::protocol::{
- self, handshake, password_method, Address, AuthMethod, StreamOperation, UserKey,
+ self, handshake, password_method, Address, AuthMethod, StreamOperation, UserKey, Version,
};
-use std::{collections::VecDeque, net::SocketAddr, rc::Rc};
+use std::{collections::VecDeque, net::SocketAddr};
#[derive(Eq, PartialEq, Debug)]
#[allow(dead_code)]
@@ -23,33 +23,28 @@ enum SocksState {
Established,
}
-#[repr(u8)]
-#[derive(Copy, Clone, PartialEq, Debug)]
-pub enum SocksVersion {
- V4 = 4,
- V5 = 5,
-}
-
-pub(crate) struct SocksConnection {
- connection: Connection,
+struct SocksProxyImpl {
+ info: ConnectionInfo,
state: SocksState,
client_inbuf: VecDeque,
server_inbuf: VecDeque,
client_outbuf: VecDeque,
server_outbuf: VecDeque,
data_buf: VecDeque,
- version: SocksVersion,
+ version: Version,
credentials: Option,
+ command: protocol::Command,
+ udp_relay_addr: Option,
}
-impl SocksConnection {
+impl SocksProxyImpl {
pub fn new(
- connection: &Connection,
- manager: Rc,
- version: SocksVersion,
+ info: &ConnectionInfo,
+ credentials: Option,
+ version: Version,
) -> Result {
let mut result = Self {
- connection: connection.clone(),
+ info: info.clone(),
state: SocksState::ServerHello,
client_inbuf: VecDeque::default(),
server_inbuf: VecDeque::default(),
@@ -57,59 +52,71 @@ impl SocksConnection {
server_outbuf: VecDeque::default(),
data_buf: VecDeque::default(),
version,
- credentials: manager.get_credentials().clone(),
+ credentials,
+ command: protocol::Command::Connect,
+ udp_relay_addr: None,
};
result.send_client_hello()?;
Ok(result)
}
- fn send_client_hello(&mut self) -> Result<(), Error> {
+ fn send_client_hello_socks4(&mut self) -> Result<(), Error> {
let credentials = &self.credentials;
- match self.version {
- SocksVersion::V4 => {
- self.server_outbuf
- .extend(&[self.version as u8, protocol::Command::Connect.into()]);
- self.server_outbuf
- .extend(self.connection.dst.port().to_be_bytes());
- let mut ip_vec = Vec::::new();
- let mut name_vec = Vec::::new();
- match &self.connection.dst {
- Address::SocketAddress(SocketAddr::V4(addr)) => {
- ip_vec.extend(addr.ip().octets().as_ref());
- }
- Address::SocketAddress(SocketAddr::V6(_)) => {
- return Err("SOCKS4 does not support IPv6".into());
- }
- Address::DomainAddress(host, _) => {
- ip_vec.extend(&[0, 0, 0, host.len() as u8]);
- name_vec.extend(host.as_bytes());
- name_vec.push(0);
- }
- }
- self.server_outbuf.extend(ip_vec);
- if let Some(credentials) = credentials {
- self.server_outbuf.extend(credentials.username.as_bytes());
- if !credentials.password.is_empty() {
- self.server_outbuf.push_back(b':');
- self.server_outbuf.extend(credentials.password.as_bytes());
- }
- }
- self.server_outbuf.push_back(0);
- self.server_outbuf.extend(name_vec);
+ self.server_outbuf
+ .extend(&[self.version as u8, protocol::Command::Connect.into()]);
+ self.server_outbuf
+ .extend(self.info.dst.port().to_be_bytes());
+ let mut ip_vec = Vec::::new();
+ let mut name_vec = Vec::::new();
+ match &self.info.dst {
+ Address::SocketAddress(SocketAddr::V4(addr)) => {
+ ip_vec.extend(addr.ip().octets().as_ref());
}
+ Address::SocketAddress(SocketAddr::V6(_)) => {
+ return Err("SOCKS4 does not support IPv6".into());
+ }
+ Address::DomainAddress(host, _) => {
+ ip_vec.extend(&[0, 0, 0, host.len() as u8]);
+ name_vec.extend(host.as_bytes());
+ name_vec.push(0);
+ }
+ }
+ self.server_outbuf.extend(ip_vec);
+ if let Some(credentials) = credentials {
+ self.server_outbuf.extend(credentials.username.as_bytes());
+ if !credentials.password.is_empty() {
+ self.server_outbuf.push_back(b':');
+ self.server_outbuf.extend(credentials.password.as_bytes());
+ }
+ }
+ self.server_outbuf.push_back(0);
+ self.server_outbuf.extend(name_vec);
+ Ok(())
+ }
- SocksVersion::V5 => {
- // Providing unassigned methods is supposed to bypass China's GFW.
- // For details, refer to https://github.com/blechschmidt/tun2proxy/issues/35.
- let mut methods = vec![
- AuthMethod::NoAuth,
- AuthMethod::from(4_u8),
- AuthMethod::from(100_u8),
- ];
- if credentials.is_some() {
- methods.push(AuthMethod::UserPass);
- }
- handshake::Request::new(methods).write_to_stream(&mut self.server_outbuf)?;
+ fn send_client_hello_socks5(&mut self) -> Result<(), Error> {
+ let credentials = &self.credentials;
+ // Providing unassigned methods is supposed to bypass China's GFW.
+ // For details, refer to https://github.com/blechschmidt/tun2proxy/issues/35.
+ let mut methods = vec![
+ AuthMethod::NoAuth,
+ AuthMethod::from(4_u8),
+ AuthMethod::from(100_u8),
+ ];
+ if credentials.is_some() {
+ methods.push(AuthMethod::UserPass);
+ }
+ handshake::Request::new(methods).write_to_stream(&mut self.server_outbuf)?;
+ Ok(())
+ }
+
+ fn send_client_hello(&mut self) -> Result<(), Error> {
+ match self.version {
+ Version::V4 => {
+ self.send_client_hello_socks4()?;
+ }
+ Version::V5 => {
+ self.send_client_hello_socks5()?;
}
}
self.state = SocksState::ServerHello;
@@ -164,8 +171,8 @@ impl SocksConnection {
fn receive_server_hello(&mut self) -> Result<(), Error> {
match self.version {
- SocksVersion::V4 => self.receive_server_hello_socks4(),
- SocksVersion::V5 => self.receive_server_hello_socks5(),
+ Version::V4 => self.receive_server_hello_socks4(),
+ Version::V5 => self.receive_server_hello_socks5(),
}
}
@@ -213,6 +220,12 @@ impl SocksConnection {
if response.reply != protocol::Reply::Succeeded {
return Err(format!("SOCKS connection failed: {}", response.reply).into());
}
+
+ if self.command == protocol::Command::UdpAssociate {
+ log::info!("UDP packet destination: {}", response.address);
+ self.udp_relay_addr = Some(response.address);
+ }
+
self.server_outbuf.append(&mut self.data_buf);
self.data_buf.clear();
@@ -220,8 +233,9 @@ impl SocksConnection {
self.state_change()
}
- fn send_request(&mut self) -> Result<(), Error> {
- protocol::Request::new(protocol::Command::Connect, self.connection.dst.clone())
+ fn send_request_socks5(&mut self) -> Result<(), Error> {
+ // self.server_outbuf.extend(&[self.version as u8, self.command as u8, 0]);
+ protocol::Request::new(protocol::Command::Connect, self.info.dst.clone())
.write_to_stream(&mut self.server_outbuf)?;
self.state = SocksState::ReceiveResponse;
self.state_change()
@@ -243,7 +257,7 @@ impl SocksConnection {
SocksState::ReceiveAuthResponse => self.receive_auth_data(),
- SocksState::SendRequest => self.send_request(),
+ SocksState::SendRequest => self.send_request_socks5(),
SocksState::ReceiveResponse => self.receive_connection_status(),
@@ -254,7 +268,7 @@ impl SocksConnection {
}
}
-impl TcpProxy for SocksConnection {
+impl TcpProxy for SocksProxyImpl {
fn push_data(&mut self, event: IncomingDataEvent<'_>) -> Result<(), Error> {
let direction = event.direction;
let buffer = event.buffer;
@@ -319,35 +333,31 @@ impl TcpProxy for SocksConnection {
}
}
-pub struct SocksManager {
+pub(crate) struct SocksProxyManager {
server: SocketAddr,
credentials: Option,
- version: SocksVersion,
+ version: Version,
}
-impl ConnectionManager for SocksManager {
- fn handles_connection(&self, connection: &Connection) -> bool {
- connection.proto == IpProtocol::Tcp
+impl ConnectionManager for SocksProxyManager {
+ fn handles_connection(&self, info: &ConnectionInfo) -> bool {
+ info.protocol == IpProtocol::Tcp
}
- fn new_connection(
- &self,
- connection: &Connection,
- manager: Rc,
- ) -> Result>, Error> {
- if connection.proto != IpProtocol::Tcp {
- return Ok(None);
+ fn new_tcp_proxy(&self, info: &ConnectionInfo) -> Result, Error> {
+ if info.protocol != IpProtocol::Tcp {
+ return Err("Invalid protocol".into());
}
- Ok(Some(Box::new(SocksConnection::new(
- connection,
- manager,
+ Ok(Box::new(SocksProxyImpl::new(
+ info,
+ self.credentials.clone(),
self.version,
- )?)))
+ )?))
}
- fn close_connection(&self, _: &Connection) {}
+ fn close_connection(&self, _: &ConnectionInfo) {}
- fn get_server(&self) -> SocketAddr {
+ fn get_server_addr(&self) -> SocketAddr {
self.server
}
@@ -356,16 +366,12 @@ impl ConnectionManager for SocksManager {
}
}
-impl SocksManager {
- pub fn new(
- server: SocketAddr,
- version: SocksVersion,
- credentials: Option,
- ) -> Rc {
- Rc::new(Self {
+impl SocksProxyManager {
+ pub(crate) fn new(server: SocketAddr, version: Version, credentials: Option) -> Self {
+ Self {
server,
credentials,
version,
- })
+ }
}
}
diff --git a/src/tun2proxy.rs b/src/tun2proxy.rs
index d5af74a..29675e2 100644
--- a/src/tun2proxy.rs
+++ b/src/tun2proxy.rs
@@ -1,11 +1,11 @@
-use crate::{error::Error, virtdevice::VirtualTunDevice, NetworkInterface, Options};
+use crate::{error::Error, error::Result, virtdevice::VirtualTunDevice, NetworkInterface, Options};
use mio::{event::Event, net::TcpStream, unix::SourceFd, Events, Interest, Poll, Token};
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},
+ wire::{IpCidr, IpProtocol, Ipv4Packet, Ipv6Packet, TcpPacket, UdpPacket, UDP_HEADER_LEN},
};
use socks5_impl::protocol::{Address, UserKey};
use std::{
@@ -19,24 +19,40 @@ use std::{
};
#[derive(Hash, Clone, Eq, PartialEq, Debug)]
-pub(crate) struct Connection {
+pub(crate) struct ConnectionInfo {
pub(crate) src: SocketAddr,
pub(crate) dst: Address,
- pub(crate) proto: IpProtocol,
+ pub(crate) protocol: IpProtocol,
}
-impl Connection {
+impl Default for ConnectionInfo {
+ fn default() -> Self {
+ Self {
+ src: SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 0),
+ dst: Address::unspecified(),
+ protocol: IpProtocol::Tcp,
+ }
+ }
+}
+
+impl ConnectionInfo {
+ #[allow(dead_code)]
+ pub fn new(src: SocketAddr, dst: Address, protocol: IpProtocol) -> Self {
+ Self { src, dst, protocol }
+ }
+
fn to_named(&self, name: String) -> Self {
let mut result = self.clone();
result.dst = Address::from((name, result.dst.port()));
- log::trace!("Replace dst \"{}\" -> \"{}\"", self.dst, result.dst);
+ // let p = self.protocol;
+ // log::trace!("{p} replace dst \"{}\" -> \"{}\"", self.dst, result.dst);
result
}
}
-impl std::fmt::Display for Connection {
+impl std::fmt::Display for ConnectionInfo {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
- write!(f, "{} -> {}", self.src, self.dst)
+ write!(f, "{} {} -> {}", self.protocol, self.src, self.dst)
}
}
@@ -60,10 +76,11 @@ pub(crate) enum Direction {
#[allow(dead_code)]
pub(crate) enum ConnectionEvent<'a> {
- NewConnection(&'a Connection),
- ConnectionClosed(&'a Connection),
+ NewConnection(&'a ConnectionInfo),
+ ConnectionClosed(&'a ConnectionInfo),
}
+#[derive(Debug)]
pub(crate) struct DataEvent<'a, T> {
pub(crate) direction: T,
pub(crate) buffer: &'a [u8],
@@ -73,95 +90,87 @@ pub(crate) type IncomingDataEvent<'a> = DataEvent<'a, IncomingDirection>;
pub(crate) type OutgoingDataEvent<'a> = DataEvent<'a, OutgoingDirection>;
fn get_transport_info(
- proto: IpProtocol,
+ protocol: IpProtocol,
transport_offset: usize,
packet: &[u8],
-) -> Option<((u16, u16), bool, usize, usize)> {
- match proto {
- IpProtocol::Udp => match UdpPacket::new_checked(packet) {
- Ok(result) => Some((
- (result.src_port(), result.dst_port()),
- false,
- transport_offset + 8,
- packet.len() - 8,
- )),
- Err(_) => None,
- },
- IpProtocol::Tcp => match TcpPacket::new_checked(packet) {
- Ok(result) => Some((
- (result.src_port(), result.dst_port()),
- result.syn() && !result.ack(),
- transport_offset + result.header_len() as usize,
- packet.len(),
- )),
- Err(_) => None,
- },
- _ => None,
+) -> Result<((u16, u16), bool, usize, usize)> {
+ match protocol {
+ IpProtocol::Udp => UdpPacket::new_checked(packet)
+ .map(|result| {
+ (
+ (result.src_port(), result.dst_port()),
+ false,
+ transport_offset + UDP_HEADER_LEN,
+ packet.len() - UDP_HEADER_LEN,
+ )
+ })
+ .map_err(|e| e.into()),
+ IpProtocol::Tcp => TcpPacket::new_checked(packet)
+ .map(|result| {
+ (
+ (result.src_port(), result.dst_port()),
+ result.syn() && !result.ack(),
+ transport_offset + result.header_len() as usize,
+ packet.len(),
+ )
+ })
+ .map_err(|e| e.into()),
+ _ => Err(format!("Unsupported protocol {protocol}").into()),
}
}
-fn connection_tuple(frame: &[u8]) -> Option<(Connection, bool, usize, usize)> {
+fn connection_tuple(frame: &[u8]) -> Result<(ConnectionInfo, bool, usize, usize)> {
if let Ok(packet) = Ipv4Packet::new_checked(frame) {
- let proto = packet.next_header();
+ let protocol = packet.next_header();
let mut a = [0_u8; 4];
a.copy_from_slice(packet.src_addr().as_bytes());
let src_addr = IpAddr::from(a);
a.copy_from_slice(packet.dst_addr().as_bytes());
let dst_addr = IpAddr::from(a);
+ let header_len = packet.header_len().into();
- return if let Some((ports, first_packet, payload_offset, payload_size)) = get_transport_info(
- proto,
- packet.header_len().into(),
- &frame[packet.header_len().into()..],
- ) {
- let connection = Connection {
- src: SocketAddr::new(src_addr, ports.0),
- dst: SocketAddr::new(dst_addr, ports.1).into(),
- proto,
- };
- Some((connection, first_packet, payload_offset, payload_size))
- } else {
- None
+ let (ports, first_packet, payload_offset, payload_size) =
+ get_transport_info(protocol, header_len, &frame[header_len..])?;
+ let info = ConnectionInfo {
+ src: SocketAddr::new(src_addr, ports.0),
+ dst: SocketAddr::new(dst_addr, ports.1).into(),
+ protocol,
};
+ return Ok((info, first_packet, payload_offset, payload_size));
}
- match Ipv6Packet::new_checked(frame) {
- Ok(packet) => {
- // TODO: Support extension headers.
- let proto = packet.next_header();
+ if let Ok(packet) = Ipv6Packet::new_checked(frame) {
+ // TODO: Support extension headers.
+ let protocol = packet.next_header();
- let mut a = [0_u8; 16];
- a.copy_from_slice(packet.src_addr().as_bytes());
- let src_addr = IpAddr::from(a);
- a.copy_from_slice(packet.dst_addr().as_bytes());
- let dst_addr = IpAddr::from(a);
+ let mut a = [0_u8; 16];
+ a.copy_from_slice(packet.src_addr().as_bytes());
+ let src_addr = IpAddr::from(a);
+ a.copy_from_slice(packet.dst_addr().as_bytes());
+ let dst_addr = IpAddr::from(a);
+ let header_len = packet.header_len();
- if let Some((ports, first_packet, payload_offset, payload_size)) =
- get_transport_info(proto, packet.header_len(), &frame[packet.header_len()..])
- {
- let connection = Connection {
- src: SocketAddr::new(src_addr, ports.0),
- dst: SocketAddr::new(dst_addr, ports.1).into(),
- proto,
- };
- Some((connection, first_packet, payload_offset, payload_size))
- } else {
- None
- }
- }
- _ => None,
+ let (ports, first_packet, payload_offset, payload_size) =
+ get_transport_info(protocol, header_len, &frame[header_len..])?;
+ let info = ConnectionInfo {
+ src: SocketAddr::new(src_addr, ports.0),
+ dst: SocketAddr::new(dst_addr, ports.1).into(),
+ protocol,
+ };
+ return Ok((info, first_packet, payload_offset, payload_size));
}
+ Err("Neither IPv6 nor IPv4 packet".into())
}
const SERVER_WRITE_CLOSED: u8 = 1;
const CLIENT_WRITE_CLOSED: u8 = 2;
-struct ConnectionState {
- smoltcp_handle: SocketHandle,
+struct TcpConnectState {
+ smoltcp_handle: Option,
mio_stream: TcpStream,
token: Token,
- handler: Box,
+ tcp_proxy_handler: Box,
close_state: u8,
wait_read: bool,
wait_write: bool,
@@ -176,30 +185,30 @@ pub(crate) trait TcpProxy {
fn reset_connection(&self) -> bool;
}
+pub(crate) trait UdpProxy {
+ fn send_frame(&mut self, destination: &Address, frame: &[u8]) -> Result<(), Error>;
+ fn receive_frame(&mut self, source: &SocketAddr, frame: &[u8]) -> Result<(), Error>;
+}
+
pub(crate) trait ConnectionManager {
- fn handles_connection(&self, connection: &Connection) -> bool;
- fn new_connection(
- &self,
- connection: &Connection,
- manager: Rc,
- ) -> Result>, Error>;
- fn close_connection(&self, connection: &Connection);
- fn get_server(&self) -> SocketAddr;
+ fn handles_connection(&self, info: &ConnectionInfo) -> bool;
+ fn new_tcp_proxy(&self, info: &ConnectionInfo) -> Result, Error>;
+ fn close_connection(&self, info: &ConnectionInfo);
+ fn get_server_addr(&self) -> SocketAddr;
fn get_credentials(&self) -> &Option;
}
const TUN_TOKEN: Token = Token(0);
-const UDP_TOKEN: Token = Token(1);
const EXIT_TOKEN: Token = Token(2);
pub struct TunToProxy<'a> {
tun: TunTapInterface,
poll: Poll,
iface: Interface,
- connections: HashMap,
+ connection_map: HashMap,
connection_managers: Vec>,
next_token: usize,
- token_to_connection: HashMap,
+ token_to_info: HashMap,
sockets: SocketSet<'a>,
device: VirtualTunDevice,
options: Options,
@@ -234,10 +243,10 @@ impl<'a> TunToProxy<'a> {
Medium::Ip => Config::new(smoltcp::wire::HardwareAddress::Ip),
Medium::Ieee802154 => todo!(),
};
- let mut virt = VirtualTunDevice::new(tun.capabilities());
+ let mut device = VirtualTunDevice::new(tun.capabilities());
let gateway4: Ipv4Addr = Ipv4Addr::from_str("0.0.0.1")?;
let gateway6: Ipv6Addr = Ipv6Addr::from_str("::1")?;
- let mut iface = Interface::new(config, &mut virt, Instant::now());
+ let mut iface = Interface::new(config, &mut device, Instant::now());
iface.update_ip_addrs(|ip_addrs| {
ip_addrs.push(IpCidr::new(gateway4.into(), 0)).unwrap();
ip_addrs.push(IpCidr::new(gateway6.into(), 0)).unwrap()
@@ -250,12 +259,12 @@ impl<'a> TunToProxy<'a> {
tun,
poll,
iface,
- connections: HashMap::default(),
+ connection_map: HashMap::default(),
next_token: usize::from(EXIT_TOKEN) + 1,
- token_to_connection: HashMap::default(),
+ token_to_info: HashMap::default(),
connection_managers: Vec::default(),
sockets: SocketSet::new([]),
- device: virt,
+ device,
options,
write_sockets: HashSet::default(),
_exit_receiver: exit_receiver,
@@ -292,28 +301,34 @@ impl<'a> TunToProxy<'a> {
Ok(())
}
- fn remove_connection(&mut self, connection: &Connection) -> Result<(), Error> {
- if let Some(mut conn) = self.connections.remove(connection) {
+ fn remove_connection(&mut self, info: &ConnectionInfo) -> Result<(), Error> {
+ if let Some(mut conn) = self.connection_map.remove(info) {
+ _ = conn.mio_stream.shutdown(Both);
+ if let Some(handle) = conn.smoltcp_handle {
+ let socket = self.sockets.get_mut::(handle);
+ socket.close();
+ self.sockets.remove(handle);
+ }
+ self.expect_smoltcp_send()?;
let token = &conn.token;
- self.token_to_connection.remove(token);
- self.sockets.remove(conn.smoltcp_handle);
+ self.token_to_info.remove(token);
_ = self.poll.registry().deregister(&mut conn.mio_stream);
- log::info!("CLOSE {}", connection);
+ log::info!("CLOSE {}", info);
}
Ok(())
}
- fn get_connection_manager(&self, connection: &Connection) -> Option> {
+ fn get_connection_manager(&self, info: &ConnectionInfo) -> Option> {
for manager in self.connection_managers.iter() {
- if manager.handles_connection(connection) {
+ if manager.handles_connection(info) {
return Some(manager.clone());
}
}
None
}
- fn check_change_close_state(&mut self, connection: &Connection) -> Result<(), Error> {
- let state = self.connections.get_mut(connection);
+ fn check_change_close_state(&mut self, info: &ConnectionInfo) -> Result<(), Error> {
+ let state = self.connection_map.get_mut(info);
if state.is_none() {
return Ok(());
}
@@ -321,23 +336,25 @@ impl<'a> TunToProxy<'a> {
let mut closed_ends = 0;
if (state.close_state & SERVER_WRITE_CLOSED) == SERVER_WRITE_CLOSED
&& !state
- .handler
+ .tcp_proxy_handler
.have_data(Direction::Incoming(IncomingDirection::FromServer))
&& !state
- .handler
+ .tcp_proxy_handler
.have_data(Direction::Outgoing(OutgoingDirection::ToClient))
{
- let socket = self.sockets.get_mut::(state.smoltcp_handle);
- socket.close();
+ if let Some(socket_handle) = state.smoltcp_handle {
+ let socket = self.sockets.get_mut::(socket_handle);
+ socket.close();
+ }
closed_ends += 1;
}
if (state.close_state & CLIENT_WRITE_CLOSED) == CLIENT_WRITE_CLOSED
&& !state
- .handler
+ .tcp_proxy_handler
.have_data(Direction::Incoming(IncomingDirection::FromClient))
&& !state
- .handler
+ .tcp_proxy_handler
.have_data(Direction::Outgoing(OutgoingDirection::ToServer))
{
_ = state.mio_stream.shutdown(Shutdown::Write);
@@ -345,20 +362,22 @@ impl<'a> TunToProxy<'a> {
}
if closed_ends == 2 {
- self.remove_connection(connection)?;
+ self.remove_connection(info)?;
}
Ok(())
}
- fn tunsocket_read_and_forward(&mut self, connection: &Connection) -> Result<(), Error> {
+ fn tunsocket_read_and_forward(&mut self, info: &ConnectionInfo) -> Result<(), Error> {
// Scope for mutable borrow of self.
{
- let state = self.connections.get_mut(connection);
- if state.is_none() {
- return Ok(());
- }
- let state = state.unwrap();
- let socket = self.sockets.get_mut::(state.smoltcp_handle);
+ let state = match self.connection_map.get_mut(info) {
+ Some(state) => state,
+ None => return Ok(()),
+ };
+ let socket = match state.smoltcp_handle {
+ Some(handle) => self.sockets.get_mut::(handle),
+ None => return Ok(()),
+ };
let mut error = Ok(());
while socket.can_recv() && error.is_ok() {
socket.recv(|data| {
@@ -366,7 +385,7 @@ impl<'a> TunToProxy<'a> {
direction: IncomingDirection::FromClient,
buffer: data,
};
- error = state.handler.push_data(event);
+ error = state.tcp_proxy_handler.push_data(event);
(data.len(), ())
})?;
}
@@ -385,20 +404,14 @@ impl<'a> TunToProxy<'a> {
self.expect_smoltcp_send()?;
}
- self.check_change_close_state(connection)?;
+ self.check_change_close_state(info)?;
Ok(())
}
- // Update the poll registry depending on the connection's event interests.
- fn update_mio_socket_interest(&mut self, connection: &Connection) -> Result<(), Error> {
- let state = self
- .connections
- .get_mut(connection)
- .ok_or("connection not found")?;
-
+ fn update_mio_socket_interest(poll: &mut Poll, state: &mut TcpConnectState) -> Result<()> {
// Maybe we did not listen for any events before. Therefore, just swallow the error.
- _ = self.poll.registry().deregister(&mut state.mio_stream);
+ _ = poll.registry().deregister(&mut state.mio_stream);
// If we do not wait for read or write events, we do not need to register them.
if !state.wait_read && !state.wait_write {
@@ -415,150 +428,131 @@ impl<'a> TunToProxy<'a> {
interest = Interest::READABLE | Interest::WRITABLE;
}
- self.poll
- .registry()
+ poll.registry()
.register(&mut state.mio_stream, state.token, interest)?;
Ok(())
}
// A raw packet was received on the tunnel interface.
fn receive_tun(&mut self, frame: &mut [u8]) -> Result<(), Error> {
- if let Some((connection, first_packet, offset, size)) = connection_tuple(frame) {
- let resolved_conn = match &mut self.options.virtdns {
- None => connection.clone(),
- Some(virt_dns) => {
- let ip = SocketAddr::try_from(connection.dst.clone())?.ip();
- virt_dns.touch_ip(&ip);
- match virt_dns.resolve_ip(&ip) {
- None => connection.clone(),
- Some(name) => connection.to_named(name.clone()),
+ let mut handler = || -> Result<(), Error> {
+ let (info, first_packet, payload_offset, payload_size) = connection_tuple(frame)?;
+ let dst = SocketAddr::try_from(&info.dst)?;
+ let connection_info = match &mut self.options.virtual_dns {
+ None => info.clone(),
+ Some(virtual_dns) => {
+ let dst_ip = dst.ip();
+ virtual_dns.touch_ip(&dst_ip);
+ match virtual_dns.resolve_ip(&dst_ip) {
+ None => info.clone(),
+ Some(name) => info.to_named(name.clone()),
}
}
};
- let dst = connection.dst;
- let handler = || -> Result<(), Error> {
- if resolved_conn.proto == IpProtocol::Tcp {
- let cm = self.get_connection_manager(&resolved_conn);
- if cm.is_none() {
- log::trace!("no connect manager");
- return Ok(());
+ log::trace!("{} ({})", connection_info, dst);
+ if connection_info.protocol == IpProtocol::Tcp {
+ let server_addr = self
+ .get_connection_manager(&connection_info)
+ .ok_or("get_connection_manager")?
+ .get_server_addr();
+ if first_packet {
+ if let Some(manager) = self.connection_managers.iter_mut().next() {
+ let tcp_proxy_handler = manager.new_tcp_proxy(&connection_info)?;
+ let mut socket = tcp::Socket::new(
+ tcp::SocketBuffer::new(vec![0; 1024 * 128]),
+ tcp::SocketBuffer::new(vec![0; 1024 * 128]),
+ );
+ socket.set_ack_delay(None);
+ socket.listen(dst)?;
+ let handle = self.sockets.add(socket);
+
+ let mut client = TcpStream::connect(server_addr)?;
+ let token = self.new_token();
+ let i = Interest::READABLE;
+ self.poll.registry().register(&mut client, token, i)?;
+
+ let state = TcpConnectState {
+ smoltcp_handle: Some(handle),
+ mio_stream: client,
+ token,
+ tcp_proxy_handler,
+ close_state: 0,
+ wait_read: true,
+ wait_write: false,
+ };
+ self.connection_map.insert(connection_info.clone(), state);
+
+ self.token_to_info.insert(token, connection_info.clone());
+
+ // log::info!("CONNECT {} ({})", connection_info, dst);
}
- let server = cm.unwrap().get_server();
- if first_packet {
- for manager in self.connection_managers.iter_mut() {
- if let Some(handler) =
- manager.new_connection(&resolved_conn, manager.clone())?
- {
- let mut socket = tcp::Socket::new(
- tcp::SocketBuffer::new(vec![0; 1024 * 128]),
- tcp::SocketBuffer::new(vec![0; 1024 * 128]),
- );
- socket.set_ack_delay(None);
- let dst = SocketAddr::try_from(dst)?;
- socket.listen(dst)?;
- let handle = self.sockets.add(socket);
-
- let client = TcpStream::connect(server)?;
-
- let token = self.new_token();
-
- let mut state = ConnectionState {
- smoltcp_handle: handle,
- mio_stream: client,
- token,
- handler,
- close_state: 0,
- wait_read: true,
- wait_write: false,
- };
-
- self.token_to_connection
- .insert(token, resolved_conn.clone());
- self.poll.registry().register(
- &mut state.mio_stream,
- token,
- Interest::READABLE,
- )?;
-
- self.connections.insert(resolved_conn.clone(), state);
-
- log::info!("CONNECT {}", resolved_conn,);
- break;
- }
- }
- } else if !self.connections.contains_key(&resolved_conn) {
- return Ok(());
- }
-
- // Inject the packet to advance the smoltcp socket state
- self.device.inject_packet(frame);
-
- // Having advanced the socket state, we expect the socket to ACK
- // Exfiltrate the response packets generated by the socket and inject them
- // into the tunnel interface.
- self.expect_smoltcp_send()?;
-
- // Read from the smoltcp socket and push the data to the connection handler.
- self.tunsocket_read_and_forward(&resolved_conn)?;
-
- // The connection handler builds up the connection or encapsulates the data.
- // Therefore, we now expect it to write data to the server.
- self.write_to_server(&resolved_conn)?;
- } else if resolved_conn.proto == IpProtocol::Udp && resolved_conn.dst.port() == 53 {
- if let Some(virtual_dns) = &mut self.options.virtdns {
- let payload = &frame[offset..offset + size];
- if let Some(response) = virtual_dns.receive_query(payload) {
- let rx_buffer = udp::PacketBuffer::new(
- vec![udp::PacketMetadata::EMPTY],
- vec![0; 4096],
- );
- let tx_buffer = udp::PacketBuffer::new(
- vec![udp::PacketMetadata::EMPTY],
- vec![0; 4096],
- );
- let mut socket = udp::Socket::new(rx_buffer, tx_buffer);
- let dst = SocketAddr::try_from(dst)?;
- socket.bind(dst)?;
- socket
- .send_slice(
- response.as_slice(),
- UdpMetadata::from(resolved_conn.src),
- )
- .expect("failed to send DNS response");
- let handle = self.sockets.add(socket);
- self.expect_smoltcp_send()?;
- self.sockets.remove(handle);
- }
- }
- // Otherwise, UDP is not yet supported.
+ } else if !self.connection_map.contains_key(&connection_info) {
+ return Ok(());
}
- Ok::<(), Error>(())
- };
- if let Err(error) = handler() {
- log::error!("{}", error);
+
+ // Inject the packet to advance the smoltcp socket state
+ self.device.inject_packet(frame);
+
+ // Having advanced the socket state, we expect the socket to ACK
+ // Exfiltrate the response packets generated by the socket and inject them
+ // into the tunnel interface.
+ self.expect_smoltcp_send()?;
+
+ // Read from the smoltcp socket and push the data to the connection handler.
+ self.tunsocket_read_and_forward(&connection_info)?;
+
+ // The connection handler builds up the connection or encapsulates the data.
+ // Therefore, we now expect it to write data to the server.
+ self.write_to_server(&connection_info)?;
+ } else if connection_info.protocol == IpProtocol::Udp {
+ let port = connection_info.dst.port();
+ if let (Some(virtual_dns), true) = (&mut self.options.virtual_dns, port == 53) {
+ let payload = &frame[payload_offset..payload_offset + payload_size];
+ if let Some(response) = virtual_dns.receive_query(payload) {
+ let rx_buffer =
+ udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 4096]);
+ let tx_buffer =
+ udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 4096]);
+ let mut socket = udp::Socket::new(rx_buffer, tx_buffer);
+ socket.bind(dst)?;
+ socket
+ .send_slice(response.as_slice(), UdpMetadata::from(connection_info.src))
+ .expect("failed to send DNS response");
+ let handle = self.sockets.add(socket);
+ self.expect_smoltcp_send()?;
+ self.sockets.remove(handle);
+ }
+ }
+ // Otherwise, UDP is not yet supported.
}
+ Ok::<(), Error>(())
+ };
+ if let Err(error) = handler() {
+ log::error!("{}", error);
}
Ok(())
}
- fn write_to_server(&mut self, connection: &Connection) -> Result<(), Error> {
- if let Some(state) = self.connections.get_mut(connection) {
- let event = state.handler.peek_data(OutgoingDirection::ToServer);
+ 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 buffer_size = event.buffer.len();
if buffer_size == 0 {
state.wait_write = false;
- self.update_mio_socket_interest(connection)?;
- self.check_change_close_state(connection)?;
+ Self::update_mio_socket_interest(&mut self.poll, state)?;
+ self.check_change_close_state(info)?;
return Ok(());
}
let result = state.mio_stream.write(event.buffer);
match result {
Ok(written) => {
state
- .handler
+ .tcp_proxy_handler
.consume_data(OutgoingDirection::ToServer, written);
state.wait_write = written < buffer_size;
- self.update_mio_socket_interest(connection)?;
+ Self::update_mio_socket_interest(&mut self.poll, state)?;
}
Err(error) if error.kind() != std::io::ErrorKind::WouldBlock => {
return Err(error.into());
@@ -566,30 +560,35 @@ impl<'a> TunToProxy<'a> {
_ => {
// WOULDBLOCK case
state.wait_write = true;
- self.update_mio_socket_interest(connection)?;
+ Self::update_mio_socket_interest(&mut self.poll, state)?;
}
}
}
- self.check_change_close_state(connection)?;
+ self.check_change_close_state(info)?;
Ok(())
}
- fn write_to_client(&mut self, token: Token, connection: &Connection) -> Result<(), Error> {
- while let Some(state) = self.connections.get_mut(connection) {
- let socket_handle = state.smoltcp_handle;
- let event = state.handler.peek_data(OutgoingDirection::ToClient);
+ fn write_to_client(&mut self, token: Token, info: &ConnectionInfo) -> Result<(), Error> {
+ while let Some(state) = self.connection_map.get_mut(info) {
+ let socket_handle = match state.smoltcp_handle {
+ Some(handle) => handle,
+ None => break,
+ };
+ let event = state
+ .tcp_proxy_handler
+ .peek_data(OutgoingDirection::ToClient);
let buflen = event.buffer.len();
let consumed;
{
let socket = self.sockets.get_mut::(socket_handle);
if socket.may_send() {
- if let Some(virtdns) = &mut self.options.virtdns {
+ if let Some(virtual_dns) = &mut self.options.virtual_dns {
// Unwrapping is fine because every smoltcp socket is bound to an.
- virtdns.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)?;
state
- .handler
+ .tcp_proxy_handler
.consume_data(OutgoingDirection::ToClient, consumed);
self.expect_smoltcp_send()?;
if consumed < buflen {
@@ -606,7 +605,7 @@ impl<'a> TunToProxy<'a> {
}
}
- self.check_change_close_state(connection)?;
+ self.check_change_close_state(info)?;
}
Ok(())
}
@@ -623,7 +622,7 @@ impl<'a> TunToProxy<'a> {
fn send_to_smoltcp(&mut self) -> Result<(), Error> {
let cloned = self.write_sockets.clone();
for token in cloned.iter() {
- if let Some(connection) = self.token_to_connection.get(token) {
+ if let Some(connection) = self.token_to_info.get(token) {
let connection = connection.clone();
if let Err(error) = self.write_to_client(*token, &connection) {
self.remove_connection(&connection)?;
@@ -636,24 +635,26 @@ impl<'a> TunToProxy<'a> {
fn mio_socket_event(&mut self, event: &Event) -> Result<(), Error> {
let e = "connection not found";
- let conn_ref = self.token_to_connection.get(&event.token());
- // We may have closed the connection in an earlier iteration over the poll
- // events, e.g. because an event through the tunnel interface indicated that the connection
- // should be closed.
- if conn_ref.is_none() {
- log::trace!("{e}");
- return Ok(());
- }
- let connection = conn_ref.unwrap().clone();
+ let conn_info = match self.token_to_info.get(&event.token()) {
+ Some(conn_info) => conn_info.clone(),
+ None => {
+ // We may have closed the connection in an earlier iteration over the poll events,
+ // e.g. because an event through the tunnel interface indicated that the connection
+ // should be closed.
+ log::trace!("{e}");
+ return Ok(());
+ }
+ };
+
let server = self
- .get_connection_manager(&connection)
- .unwrap()
- .get_server();
+ .get_connection_manager(&conn_info)
+ .ok_or(e)?
+ .get_server_addr();
let mut block = || -> Result<(), Error> {
if event.is_readable() || event.is_read_closed() {
{
- let state = self.connections.get_mut(&connection).ok_or(e)?;
+ let state = self.connection_map.get_mut(&conn_info).ok_or(e)?;
// TODO: Move this reading process to its own function.
let mut vecbuf = Vec::::new();
@@ -673,34 +674,26 @@ impl<'a> TunToProxy<'a> {
direction: IncomingDirection::FromServer,
buffer: &data[0..read],
};
- if let Err(error) = state.handler.push_data(data_event) {
- state.mio_stream.shutdown(Both)?;
- {
- let socket = self.sockets.get_mut::(
- self.connections.get(&connection).ok_or(e)?.smoltcp_handle,
- );
- socket.close();
- }
- self.expect_smoltcp_send()?;
- log::error! {"{error}"}
- self.remove_connection(&connection.clone())?;
+ if let Err(error) = state.tcp_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.handler.reset_connection() {
+ if state.tcp_proxy_handler.reset_connection() {
_ = self.poll.registry().deregister(&mut state.mio_stream);
// Closes the connection with the proxy
state.mio_stream.shutdown(Both)?;
- log::info!("RESET {}", connection);
+ log::info!("RESET {}", conn_info);
state.mio_stream = TcpStream::connect(server)?;
state.wait_read = true;
state.wait_write = true;
- self.update_mio_socket_interest(&connection)?;
+ Self::update_mio_socket_interest(&mut self.poll, state)?;
return Ok(());
}
@@ -708,61 +701,54 @@ impl<'a> TunToProxy<'a> {
if read == 0 || event.is_read_closed() {
state.wait_read = false;
state.close_state |= SERVER_WRITE_CLOSED;
- self.update_mio_socket_interest(&connection)?;
- self.check_change_close_state(&connection)?;
+ Self::update_mio_socket_interest(&mut self.poll, state)?;
+ self.check_change_close_state(&conn_info)?;
self.expect_smoltcp_send()?;
}
}
// We have read from the proxy server and pushed the data to the connection handler.
// Thus, expect data to be processed (e.g. decapsulated) and forwarded to the client.
- self.write_to_client(event.token(), &connection)?;
+ self.write_to_client(event.token(), &conn_info)?;
// The connection handler could have produced data that is to be written to the
// server.
- self.write_to_server(&connection)?;
+ self.write_to_server(&conn_info)?;
}
if event.is_writable() {
- self.write_to_server(&connection)?;
+ self.write_to_server(&conn_info)?;
}
Ok::<(), Error>(())
};
if let Err(error) = block() {
log::error!("{}", error);
- self.remove_connection(&connection)?;
+ self.remove_connection(&conn_info)?;
}
Ok(())
}
- fn udp_event(&mut self, _event: &Event) {}
-
pub fn run(&mut self) -> Result<(), Error> {
let mut events = Events::with_capacity(1024);
loop {
- match self.poll.poll(&mut events, None) {
- Ok(()) => {
- for event in events.iter() {
- match event.token() {
- EXIT_TOKEN => {
- log::info!("exiting...");
- return Ok(());
- }
- TUN_TOKEN => self.tun_event(event)?,
- UDP_TOKEN => self.udp_event(event),
- _ => self.mio_socket_event(event)?,
- }
- }
- self.send_to_smoltcp()?;
+ if let Err(err) = self.poll.poll(&mut events, None) {
+ if err.kind() == std::io::ErrorKind::Interrupted {
+ log::warn!("Poll interrupted: \"{err}\", ignored, continue polling");
+ continue;
}
- Err(e) => {
- if e.kind() == std::io::ErrorKind::Interrupted {
- log::warn!("Poll interrupted: \"{e}\", ignored, continue polling");
- } else {
- return Err(e.into());
+ return Err(err.into());
+ }
+ for event in events.iter() {
+ match event.token() {
+ EXIT_TOKEN => {
+ log::info!("Exiting tun2proxy...");
+ return Ok(());
}
+ TUN_TOKEN => self.tun_event(event)?,
+ _ => self.mio_socket_event(event)?,
}
}
+ self.send_to_smoltcp()?;
}
}
From 4ebd019cb520292868b565418fd834e1c6fb0b66 Mon Sep 17 00:00:00 2001
From: ssrlive <30760636+ssrlive@users.noreply.github.com>
Date: Sat, 5 Aug 2023 22:32:57 +0800
Subject: [PATCH 006/363] minor changes
---
src/socks.rs | 9 ---------
1 file changed, 9 deletions(-)
diff --git a/src/socks.rs b/src/socks.rs
index d5fd6ad..a8e4fde 100644
--- a/src/socks.rs
+++ b/src/socks.rs
@@ -33,8 +33,6 @@ struct SocksProxyImpl {
data_buf: VecDeque,
version: Version,
credentials: Option,
- command: protocol::Command,
- udp_relay_addr: Option,
}
impl SocksProxyImpl {
@@ -53,8 +51,6 @@ impl SocksProxyImpl {
data_buf: VecDeque::default(),
version,
credentials,
- command: protocol::Command::Connect,
- udp_relay_addr: None,
};
result.send_client_hello()?;
Ok(result)
@@ -221,11 +217,6 @@ impl SocksProxyImpl {
return Err(format!("SOCKS connection failed: {}", response.reply).into());
}
- if self.command == protocol::Command::UdpAssociate {
- log::info!("UDP packet destination: {}", response.address);
- self.udp_relay_addr = Some(response.address);
- }
-
self.server_outbuf.append(&mut self.data_buf);
self.data_buf.clear();
From 5ce2e85919a65d3ac4024b72ea7a45a46911df09 Mon Sep 17 00:00:00 2001
From: ssrlive <30760636+ssrlive@users.noreply.github.com>
Date: Sun, 6 Aug 2023 11:42:19 +0800
Subject: [PATCH 007/363] trust-dns-proto import
---
Cargo.toml | 1 +
src/dns.rs | 104 +++++++++++++++++++++++++++++++++++++++++++++++++
src/lib.rs | 1 +
src/virtdns.rs | 24 +++++++++++-
4 files changed, 128 insertions(+), 2 deletions(-)
create mode 100644 src/dns.rs
diff --git a/Cargo.toml b/Cargo.toml
index 25a01d5..7752eee 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -25,6 +25,7 @@ prctl = "1.0"
smoltcp = { version = "0.10.0", features = ["std", "phy-tuntap_interface"] }
socks5-impl = { version = "0.5", default-features = false }
thiserror = "1.0"
+trust-dns-proto = "0.22"
unicase = "2.6.0"
url = "2.4"
diff --git a/src/dns.rs b/src/dns.rs
new file mode 100644
index 0000000..f4b2404
--- /dev/null
+++ b/src/dns.rs
@@ -0,0 +1,104 @@
+#![allow(dead_code)]
+
+use std::{net::IpAddr, str::FromStr};
+use trust_dns_proto::{
+ op::{Message, ResponseCode},
+ rr::{record_type::RecordType, Name, RData, Record},
+};
+
+#[cfg(feature = "use-rand")]
+pub fn build_dns_request(
+ domain: &str,
+ query_type: RecordType,
+ used_by_tcp: bool,
+) -> Result, String> {
+ // [dependencies]
+ // rand = "0.8"
+ use rand::{rngs::StdRng, Rng, SeedableRng};
+ use trust_dns_proto::op::{header::MessageType, op_code::OpCode, query::Query};
+ let name = Name::from_str(domain).map_err(|e| e.to_string())?;
+ let query = Query::query(name, query_type);
+ let mut msg = Message::new();
+ msg.add_query(query)
+ .set_id(StdRng::from_entropy().gen())
+ .set_op_code(OpCode::Query)
+ .set_message_type(MessageType::Query)
+ .set_recursion_desired(true);
+ let mut msg_buf = msg.to_vec().map_err(|e| e.to_string())?;
+ if used_by_tcp {
+ let mut buf = (msg_buf.len() as u16).to_be_bytes().to_vec();
+ buf.append(&mut msg_buf);
+ Ok(buf)
+ } else {
+ Ok(msg_buf)
+ }
+}
+
+pub fn build_dns_response(
+ mut request: Message,
+ domain: &str,
+ ip: IpAddr,
+ ttl: u32,
+) -> Result {
+ let record = match ip {
+ IpAddr::V4(ip) => {
+ let mut record = Record::with(Name::from_str(domain)?, RecordType::A, ttl);
+ record.set_data(Some(RData::A(ip)));
+ record
+ }
+ IpAddr::V6(ip) => {
+ let mut record = Record::with(Name::from_str(domain)?, RecordType::AAAA, ttl);
+ record.set_data(Some(RData::AAAA(ip)));
+ record
+ }
+ };
+ request.add_answer(record);
+ Ok(request)
+}
+
+pub fn extract_ipaddr_from_dns_message(message: &Message) -> Result {
+ if message.response_code() != ResponseCode::NoError {
+ return Err(format!("{:?}", message.response_code()));
+ }
+ let mut cname = None;
+ for answer in message.answers() {
+ match answer
+ .data()
+ .ok_or("DNS response not contains answer data")?
+ {
+ RData::A(addr) => {
+ return Ok(IpAddr::V4(*addr));
+ }
+ RData::AAAA(addr) => {
+ return Ok(IpAddr::V6(*addr));
+ }
+ RData::CNAME(name) => {
+ cname = Some(name.to_utf8());
+ }
+ _ => {}
+ }
+ }
+ if let Some(cname) = cname {
+ return Err(cname);
+ }
+ Err(format!("{:?}", message.answers()))
+}
+
+pub fn extract_domain_from_dns_message(message: &Message) -> Result {
+ let query = message.queries().get(0).ok_or("DnsRequest no query body")?;
+ let name = query.name().to_string();
+ Ok(name)
+}
+
+pub fn parse_data_to_dns_message(data: &[u8], used_by_tcp: bool) -> Result {
+ if used_by_tcp {
+ if data.len() < 2 {
+ return Err("invalid dns data".into());
+ }
+ let len = u16::from_be_bytes([data[0], data[1]]) as usize;
+ let data = data.get(2..len + 2).ok_or("invalid dns data")?;
+ return parse_data_to_dns_message(data, false);
+ }
+ let message = Message::from_vec(data).map_err(|e| e.to_string())?;
+ Ok(message)
+}
diff --git a/src/lib.rs b/src/lib.rs
index 98f69c6..84c3d11 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -7,6 +7,7 @@ use std::{
use tun2proxy::ConnectionManager;
mod android;
+mod dns;
pub mod error;
mod http;
pub mod setup;
diff --git a/src/virtdns.rs b/src/virtdns.rs
index 1da5c54..0d13aa6 100644
--- a/src/virtdns.rs
+++ b/src/virtdns.rs
@@ -8,7 +8,6 @@ use std::{
time::{Duration, Instant},
};
-const DNS_TTL: u8 = 30; // TTL in DNS replies in seconds
const MAPPING_TIMEOUT: u64 = 60; // Mapping timeout in seconds
#[derive(Eq, PartialEq, Debug)]
@@ -57,6 +56,23 @@ impl VirtualDns {
VirtualDns::default()
}
+ // /*
+ pub fn receive_query(&mut self, data: &[u8]) -> Option> {
+ use crate::dns;
+ let mut dns_block = || {
+ let message = dns::parse_data_to_dns_message(data, false)?;
+ let qname = dns::extract_domain_from_dns_message(&message)?;
+ if let Some(ip) = self.allocate_ip(qname.clone()) {
+ let message = dns::build_dns_response(message, &qname, ip, 5)?;
+ message.to_vec()
+ } else {
+ Err("Virtual IP space for DNS exhausted".into())
+ }
+ };
+ dns_block().ok()
+ }
+ // */
+ /*
pub fn receive_query(&mut self, data: &[u8]) -> Option> {
if data.len() < 17 {
return None;
@@ -109,6 +125,8 @@ impl VirtualDns {
response[8] = 0;
response[9] = 0;
+ const DNS_TTL: u8 = 30; // TTL in DNS replies in seconds
+
// additional section
response[10] = 0;
response[11] = 0;
@@ -138,7 +156,7 @@ impl VirtualDns {
}
Some(response)
}
-
+ // */
fn increment_ip(addr: IpAddr) -> Option {
let mut ip_bytes = match addr as IpAddr {
IpAddr::V4(ip) => Vec::::from(ip.octets()),
@@ -239,6 +257,7 @@ impl VirtualDns {
}
}
+ /*
/// Parse a non-root DNS qname at a specific offset and return the name along with its size.
/// DNS packet parsing should be continued after the name.
fn parse_qname(data: &[u8], mut offset: usize) -> Option<(String, usize)> {
@@ -277,4 +296,5 @@ impl VirtualDns {
Some((qname, offset))
}
+ // */
}
From 30d7217374eb84cec8a8b6dec4484f08cd8bb49c Mon Sep 17 00:00:00 2001
From: ssrlive <30760636+ssrlive@users.noreply.github.com>
Date: Sun, 6 Aug 2023 13:48:56 +0800
Subject: [PATCH 008/363] refine VirtualDns
---
src/error.rs | 6 +++
src/lib.rs | 8 +++-
src/tun2proxy.rs | 3 +-
src/virtdns.rs | 116 ++++++++++++++++++++---------------------------
4 files changed, 63 insertions(+), 70 deletions(-)
diff --git a/src/error.rs b/src/error.rs
index 1a57783..b0c916d 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -33,6 +33,12 @@ pub enum Error {
#[error("std::str::Utf8Error {0:?}")]
Utf8(#[from] std::str::Utf8Error),
+ #[error("TryFromSliceError {0:?}")]
+ TryFromSlice(#[from] std::array::TryFromSliceError),
+
+ #[error("ProtoError {0:?}")]
+ ProtoError(#[from] trust_dns_proto::error::ProtoError),
+
#[cfg(target_os = "android")]
#[error("jni::errors::Error {0:?}")]
Jni(#[from] jni::errors::Error),
diff --git a/src/lib.rs b/src/lib.rs
index 84c3d11..9588a4f 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,10 +1,14 @@
-use crate::{error::Error, http::HttpManager, socks::SocksProxyManager, tun2proxy::TunToProxy};
+use crate::{
+ error::Error,
+ http::HttpManager,
+ socks::SocksProxyManager,
+ tun2proxy::{ConnectionManager, TunToProxy},
+};
use socks5_impl::protocol::{UserKey, Version};
use std::{
net::{SocketAddr, ToSocketAddrs},
rc::Rc,
};
-use tun2proxy::ConnectionManager;
mod android;
mod dns;
diff --git a/src/tun2proxy.rs b/src/tun2proxy.rs
index 29675e2..8e99aff 100644
--- a/src/tun2proxy.rs
+++ b/src/tun2proxy.rs
@@ -508,7 +508,8 @@ impl<'a> TunToProxy<'a> {
let port = connection_info.dst.port();
if let (Some(virtual_dns), true) = (&mut self.options.virtual_dns, port == 53) {
let payload = &frame[payload_offset..payload_offset + payload_size];
- if let Some(response) = virtual_dns.receive_query(payload) {
+ let response = virtual_dns.receive_query(payload)?;
+ {
let rx_buffer =
udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 4096]);
let tx_buffer =
diff --git a/src/virtdns.rs b/src/virtdns.rs
index 0d13aa6..6f8949a 100644
--- a/src/virtdns.rs
+++ b/src/virtdns.rs
@@ -1,3 +1,4 @@
+use crate::error::Result;
use hashlink::{linked_hash_map::RawEntryMut, LruCache};
use smoltcp::wire::Ipv4Cidr;
use std::{
@@ -10,19 +11,6 @@ use std::{
const MAPPING_TIMEOUT: u64 = 60; // Mapping timeout in seconds
-#[derive(Eq, PartialEq, Debug)]
-#[allow(dead_code, clippy::upper_case_acronyms)]
-enum DnsRecordType {
- A = 1,
- AAAA = 28,
-}
-
-#[derive(Eq, PartialEq, Debug)]
-#[allow(dead_code)]
-enum DnsClass {
- IN = 1,
-}
-
struct NameCacheEntry {
name: String,
expiry: Instant,
@@ -57,25 +45,32 @@ impl VirtualDns {
}
// /*
- pub fn receive_query(&mut self, data: &[u8]) -> Option> {
+ pub fn receive_query(&mut self, data: &[u8]) -> Result> {
use crate::dns;
- let mut dns_block = || {
- let message = dns::parse_data_to_dns_message(data, false)?;
- let qname = dns::extract_domain_from_dns_message(&message)?;
- if let Some(ip) = self.allocate_ip(qname.clone()) {
- let message = dns::build_dns_response(message, &qname, ip, 5)?;
- message.to_vec()
- } else {
- Err("Virtual IP space for DNS exhausted".into())
- }
- };
- dns_block().ok()
+ let message = dns::parse_data_to_dns_message(data, false)?;
+ let qname = dns::extract_domain_from_dns_message(&message)?;
+ let ip = self.allocate_ip(qname.clone())?;
+ let message = dns::build_dns_response(message, &qname, ip, 5)?;
+ Ok(message.to_vec()?)
}
// */
/*
- pub fn receive_query(&mut self, data: &[u8]) -> Option> {
+ pub fn receive_query(&mut self, data: &[u8]) -> Result> {
+ #[derive(Eq, PartialEq, Debug)]
+ #[allow(dead_code, clippy::upper_case_acronyms)]
+ enum DnsRecordType {
+ A = 1,
+ AAAA = 28,
+ }
+
+ #[derive(Eq, PartialEq, Debug)]
+ #[allow(dead_code)]
+ enum DnsClass {
+ IN = 1,
+ }
+
if data.len() < 17 {
- return None;
+ return Err("Invalid DNS query".into());
}
// bit 1: Message is a query (0)
// bits 2 - 5: Standard query opcode (0)
@@ -83,22 +78,22 @@ impl VirtualDns {
// bit 7: Message is not truncated (0)
// bit 8: Recursion desired (1)
let is_supported_query = (data[2] & 0b11111011) == 0b00000001;
- let num_queries = u16::from_be_bytes(data[4..6].try_into().ok()?);
+ let num_queries = u16::from_be_bytes(data[4..6].try_into()?);
if !is_supported_query || num_queries != 1 {
- return None;
+ return Err("Invalid DNS query".into());
}
- let (qname, offset) = VirtualDns::parse_qname(data, 12)?;
+ let (qname, offset) = VirtualDns::parse_qname(data, 12).ok_or("parse_qname")?;
if offset + 3 >= data.len() {
- return None;
+ return Err("Invalid DNS query".into());
}
- let qtype = u16::from_be_bytes(data[offset..offset + 2].try_into().ok()?);
- let qclass = u16::from_be_bytes(data[offset + 2..offset + 4].try_into().ok()?);
+ let qtype = u16::from_be_bytes(data[offset..offset + 2].try_into()?);
+ let qclass = u16::from_be_bytes(data[offset + 2..offset + 4].try_into()?);
if qtype != DnsRecordType::A as u16 && qtype != DnsRecordType::AAAA as u16
|| qclass != DnsClass::IN as u16
{
- return None;
+ return Err("Invalid DNS query".into());
}
if qtype == DnsRecordType::A as u16 {
@@ -131,7 +126,7 @@ impl VirtualDns {
response[10] = 0;
response[11] = 0;
if qtype == DnsRecordType::A as u16 {
- if let Some(ip) = self.allocate_ip(qname) {
+ if let Ok(ip) = self.allocate_ip(qname) {
response.extend(&[
0xc0, 0x0c, // Question name pointer
0, 1, // Record type: A
@@ -154,10 +149,10 @@ impl VirtualDns {
} else {
response[7] = 0; // No answers
}
- Some(response)
+ Ok(response)
}
// */
- fn increment_ip(addr: IpAddr) -> Option {
+ fn increment_ip(addr: IpAddr) -> Result {
let mut ip_bytes = match addr as IpAddr {
IpAddr::V4(ip) => Vec::::from(ip.octets()),
IpAddr::V6(ip) => Vec::::from(ip.octets()),
@@ -176,36 +171,29 @@ impl VirtualDns {
}
}
let addr = if addr.is_ipv4() {
- let bytes: [u8; 4] = ip_bytes.as_slice().try_into().ok()?;
+ let bytes: [u8; 4] = ip_bytes.as_slice().try_into()?;
IpAddr::V4(Ipv4Addr::from(bytes))
} else {
- let bytes: [u8; 16] = ip_bytes.as_slice().try_into().ok()?;
+ let bytes: [u8; 16] = ip_bytes.as_slice().try_into()?;
IpAddr::V6(Ipv6Addr::from(bytes))
};
- Some(addr)
+ Ok(addr)
}
// This is to be called whenever we receive or send a packet on the socket
// which connects the tun interface to the client, so existing IP address to name
// mappings to not expire as long as the connection is active.
- pub fn touch_ip(&mut self, addr: &IpAddr) -> bool {
- match self.lru_cache.get_mut(addr) {
- None => false,
- Some(entry) => {
- entry.expiry = Instant::now() + Duration::from_secs(MAPPING_TIMEOUT);
- true
- }
- }
+ pub fn touch_ip(&mut self, addr: &IpAddr) {
+ _ = self.lru_cache.get_mut(addr).map(|entry| {
+ entry.expiry = Instant::now() + Duration::from_secs(MAPPING_TIMEOUT);
+ });
}
pub fn resolve_ip(&mut self, addr: &IpAddr) -> Option<&String> {
- match self.lru_cache.get(addr) {
- None => None,
- Some(entry) => Some(&entry.name),
- }
+ self.lru_cache.get(addr).map(|entry| &entry.name)
}
- fn allocate_ip(&mut self, name: String) -> Option {
+ fn allocate_ip(&mut self, name: String) -> Result {
let now = Instant::now();
loop {
@@ -223,9 +211,9 @@ impl VirtualDns {
}
if let Some(ip) = self.name_to_ip.get(&name) {
- let result = Some(*ip);
- self.touch_ip(&ip.clone());
- return result;
+ let ip = *ip;
+ self.touch_ip(&ip);
+ return Ok(ip);
}
let started_at = self.next_addr;
@@ -235,16 +223,10 @@ impl VirtualDns {
self.lru_cache.raw_entry_mut().from_key(&self.next_addr)
{
let expiry = Instant::now() + Duration::from_secs(MAPPING_TIMEOUT);
- vacant.insert(
- self.next_addr,
- NameCacheEntry {
- name: name.clone(),
- expiry,
- },
- );
- // e.insert(name.clone());
- self.name_to_ip.insert(name, self.next_addr);
- return Some(self.next_addr);
+ let name0 = name.clone();
+ vacant.insert(self.next_addr, NameCacheEntry { name, expiry });
+ self.name_to_ip.insert(name0, self.next_addr);
+ return Ok(self.next_addr);
}
self.next_addr = Self::increment_ip(self.next_addr)?;
if self.next_addr == self.broadcast_addr {
@@ -252,7 +234,7 @@ impl VirtualDns {
self.next_addr = self.network_addr;
}
if self.next_addr == started_at {
- return None;
+ return Err("Virtual IP space for DNS exhausted".into());
}
}
}
From 5bb4bbf022475f636fa70d9849abffe50013d2cd Mon Sep 17 00:00:00 2001
From: ssrlive <30760636+ssrlive@users.noreply.github.com>
Date: Sun, 6 Aug 2023 21:36:00 +0800
Subject: [PATCH 009/363] remove raw dns parse code
---
src/virtdns.rs | 140 -------------------------------------------------
1 file changed, 140 deletions(-)
diff --git a/src/virtdns.rs b/src/virtdns.rs
index 6f8949a..9e13e05 100644
--- a/src/virtdns.rs
+++ b/src/virtdns.rs
@@ -44,7 +44,6 @@ impl VirtualDns {
VirtualDns::default()
}
- // /*
pub fn receive_query(&mut self, data: &[u8]) -> Result> {
use crate::dns;
let message = dns::parse_data_to_dns_message(data, false)?;
@@ -53,105 +52,7 @@ impl VirtualDns {
let message = dns::build_dns_response(message, &qname, ip, 5)?;
Ok(message.to_vec()?)
}
- // */
- /*
- pub fn receive_query(&mut self, data: &[u8]) -> Result> {
- #[derive(Eq, PartialEq, Debug)]
- #[allow(dead_code, clippy::upper_case_acronyms)]
- enum DnsRecordType {
- A = 1,
- AAAA = 28,
- }
- #[derive(Eq, PartialEq, Debug)]
- #[allow(dead_code)]
- enum DnsClass {
- IN = 1,
- }
-
- if data.len() < 17 {
- return Err("Invalid DNS query".into());
- }
- // bit 1: Message is a query (0)
- // bits 2 - 5: Standard query opcode (0)
- // bit 6: Unused
- // bit 7: Message is not truncated (0)
- // bit 8: Recursion desired (1)
- let is_supported_query = (data[2] & 0b11111011) == 0b00000001;
- let num_queries = u16::from_be_bytes(data[4..6].try_into()?);
- if !is_supported_query || num_queries != 1 {
- return Err("Invalid DNS query".into());
- }
-
- let (qname, offset) = VirtualDns::parse_qname(data, 12).ok_or("parse_qname")?;
- if offset + 3 >= data.len() {
- return Err("Invalid DNS query".into());
- }
- let qtype = u16::from_be_bytes(data[offset..offset + 2].try_into()?);
- let qclass = u16::from_be_bytes(data[offset + 2..offset + 4].try_into()?);
-
- if qtype != DnsRecordType::A as u16 && qtype != DnsRecordType::AAAA as u16
- || qclass != DnsClass::IN as u16
- {
- return Err("Invalid DNS query".into());
- }
-
- if qtype == DnsRecordType::A as u16 {
- log::info!("DNS query: {}", qname);
- }
-
- let mut response = Vec::::new();
- response.extend(&data[0..offset + 4]);
- response[2] |= 0x80; // Message is a response
- response[3] |= 0x80; // Recursion available
-
- // Record count of the answer section:
- // We only send an answer record for A queries, assuming that IPv4 is supported everywhere.
- // This way, we do not have to handle two IP spaces for the virtual DNS feature.
- response[6] = 0;
- response[7] = if qtype == DnsRecordType::A as u16 {
- 1
- } else {
- 0
- };
-
- // Zero count of other sections:
- // authority section
- response[8] = 0;
- response[9] = 0;
-
- const DNS_TTL: u8 = 30; // TTL in DNS replies in seconds
-
- // additional section
- response[10] = 0;
- response[11] = 0;
- if qtype == DnsRecordType::A as u16 {
- if let Ok(ip) = self.allocate_ip(qname) {
- response.extend(&[
- 0xc0, 0x0c, // Question name pointer
- 0, 1, // Record type: A
- 0, 1, // Class: IN
- 0, 0, 0, DNS_TTL, // TTL
- 0, 4, // Data length: 4 bytes
- ]);
- match ip {
- IpAddr::V4(ip) => response.extend(ip.octets().as_ref()),
- IpAddr::V6(ip) => response.extend(ip.octets().as_ref()),
- };
- } else {
- log::error!("Virtual IP space for DNS exhausted");
- response[7] = 0; // No answers
-
- // Set rcode to SERVFAIL
- response[3] &= 0xf0;
- response[3] |= 2;
- }
- } else {
- response[7] = 0; // No answers
- }
- Ok(response)
- }
- // */
fn increment_ip(addr: IpAddr) -> Result {
let mut ip_bytes = match addr as IpAddr {
IpAddr::V4(ip) => Vec::::from(ip.octets()),
@@ -238,45 +139,4 @@ impl VirtualDns {
}
}
}
-
- /*
- /// Parse a non-root DNS qname at a specific offset and return the name along with its size.
- /// DNS packet parsing should be continued after the name.
- fn parse_qname(data: &[u8], mut offset: usize) -> Option<(String, usize)> {
- // Since we only parse qnames and qnames can't point anywhere,
- // we do not support pointers. (0xC0 is a bitmask for pointer detection.)
- let label_type = data[offset] & 0xC0;
- if label_type != 0x00 {
- return None;
- }
-
- let mut qname = String::from("");
- loop {
- if offset >= data.len() {
- return None;
- }
- let label_len = data[offset];
- if label_len == 0 {
- if qname.is_empty() {
- return None;
- }
- offset += 1;
- break;
- }
- if !qname.is_empty() {
- qname.push('.');
- }
- for _ in 0..label_len {
- offset += 1;
- if offset >= data.len() {
- return None;
- }
- qname.push(data[offset] as char);
- }
- offset += 1;
- }
-
- Some((qname, offset))
- }
- // */
}
From 41feb84c29abf7f7619312a3bbc17634fed6ccef Mon Sep 17 00:00:00 2001
From: ssrlive <30760636+ssrlive@users.noreply.github.com>
Date: Sun, 6 Aug 2023 22:39:00 +0800
Subject: [PATCH 010/363] publish script
---
.github/workflows/publish-exe.yml | 11 ++++-------
Cargo.toml | 2 +-
2 files changed, 5 insertions(+), 8 deletions(-)
diff --git a/.github/workflows/publish-exe.yml b/.github/workflows/publish-exe.yml
index 918c177..8b19a38 100644
--- a/.github/workflows/publish-exe.yml
+++ b/.github/workflows/publish-exe.yml
@@ -1,7 +1,7 @@
on:
push:
- branches: [master]
- workflow_dispatch:
+ tags:
+ - "*"
name: Build and publish executable
@@ -24,10 +24,7 @@ jobs:
run: mkdir build && mv target/x86_64-unknown-linux-gnu/release/tun2proxy build/tun2proxy-x86_64
- name: Publish
uses: softprops/action-gh-release@v1
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
- name: Automated build of ${{ github.sha }}
files: build/*
- draft: false
- prerelease: true
- body: This is an automated build of commit ${{ github.sha }}.
- tag_name: r${{ github.sha }}
\ No newline at end of file
diff --git a/Cargo.toml b/Cargo.toml
index 7752eee..a5ec577 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -2,7 +2,7 @@
authors = ["B. Blechschmidt"]
edition = "2018"
name = "tun2proxy"
-version = "0.1.1"
+version = "0.1.2"
[lib]
crate-type = ["cdylib", "lib"]
From c8b13fc4044764243e78138ab0f1a1d2c2bd3de0 Mon Sep 17 00:00:00 2001
From: ssrlive <30760636+ssrlive@users.noreply.github.com>
Date: Mon, 7 Aug 2023 12:29:36 +0800
Subject: [PATCH 011/363] receive_tun
---
src/tun2proxy.rs | 23 +++++++++++++++++++----
1 file changed, 19 insertions(+), 4 deletions(-)
diff --git a/src/tun2proxy.rs b/src/tun2proxy.rs
index 8e99aff..46608e5 100644
--- a/src/tun2proxy.rs
+++ b/src/tun2proxy.rs
@@ -449,15 +449,19 @@ impl<'a> TunToProxy<'a> {
}
}
};
- log::trace!("{} ({})", connection_info, dst);
if connection_info.protocol == IpProtocol::Tcp {
let server_addr = self
.get_connection_manager(&connection_info)
.ok_or("get_connection_manager")?
.get_server_addr();
if first_packet {
- if let Some(manager) = self.connection_managers.iter_mut().next() {
- let tcp_proxy_handler = manager.new_tcp_proxy(&connection_info)?;
+ let mut done = false;
+ for manager in self.connection_managers.iter_mut() {
+ let tcp_proxy_handler = manager.new_tcp_proxy(&connection_info);
+ if tcp_proxy_handler.is_err() {
+ continue;
+ }
+ let tcp_proxy_handler = tcp_proxy_handler?;
let mut socket = tcp::Socket::new(
tcp::SocketBuffer::new(vec![0; 1024 * 128]),
tcp::SocketBuffer::new(vec![0; 1024 * 128]),
@@ -484,10 +488,18 @@ impl<'a> TunToProxy<'a> {
self.token_to_info.insert(token, connection_info.clone());
- // log::info!("CONNECT {} ({})", connection_info, dst);
+ log::info!("Connect done {} ({})", connection_info, dst);
+ done = true;
+ break;
+ }
+ if !done {
+ log::debug!("No connection manager for {} ({})", connection_info, dst);
}
} else if !self.connection_map.contains_key(&connection_info) {
+ log::debug!("Not found {} ({})", connection_info, dst);
return Ok(());
+ } else {
+ log::trace!("Subsequent packet {} ({})", connection_info, dst);
}
// Inject the packet to advance the smoltcp socket state
@@ -505,6 +517,7 @@ impl<'a> TunToProxy<'a> {
// Therefore, we now expect it to write data to the server.
self.write_to_server(&connection_info)?;
} else if connection_info.protocol == IpProtocol::Udp {
+ log::trace!("{} ({})", connection_info, dst);
let port = connection_info.dst.port();
if let (Some(virtual_dns), true) = (&mut self.options.virtual_dns, port == 53) {
let payload = &frame[payload_offset..payload_offset + payload_size];
@@ -525,6 +538,8 @@ impl<'a> TunToProxy<'a> {
}
}
// Otherwise, UDP is not yet supported.
+ } else {
+ log::warn!("Unsupported protocol: {} ({})", connection_info, dst);
}
Ok::<(), Error>(())
};
From 3b9207fb7af0620c445909f8f84e6da93007c2c6 Mon Sep 17 00:00:00 2001
From: ssrlive <30760636+ssrlive@users.noreply.github.com>
Date: Tue, 8 Aug 2023 09:16:57 +0800
Subject: [PATCH 012/363] fixing get_transport_info
---
src/tun2proxy.rs | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/src/tun2proxy.rs b/src/tun2proxy.rs
index 46608e5..bd68a76 100644
--- a/src/tun2proxy.rs
+++ b/src/tun2proxy.rs
@@ -107,15 +107,16 @@ fn get_transport_info(
.map_err(|e| e.into()),
IpProtocol::Tcp => TcpPacket::new_checked(packet)
.map(|result| {
+ let header_len = result.header_len() as usize;
(
(result.src_port(), result.dst_port()),
result.syn() && !result.ack(),
- transport_offset + result.header_len() as usize,
- packet.len(),
+ transport_offset + header_len,
+ packet.len() - header_len,
)
})
.map_err(|e| e.into()),
- _ => Err(format!("Unsupported protocol {protocol}").into()),
+ _ => Err(format!("Unsupported protocol {protocol} in IP packet").into()),
}
}
From b92f2efd81fe41ac2f1e27fe9a2ee22fb26ef820 Mon Sep 17 00:00:00 2001
From: ssrlive <30760636+ssrlive@users.noreply.github.com>
Date: Tue, 8 Aug 2023 09:20:43 +0800
Subject: [PATCH 013/363] remove .expect call
---
src/error.rs | 3 +++
src/tun2proxy.rs | 5 ++---
2 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/src/error.rs b/src/error.rs
index b0c916d..bae52ba 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -27,6 +27,9 @@ pub enum Error {
#[error("smoltcp::socket::tcp::SendError {0:?}")]
Send(#[from] smoltcp::socket::tcp::SendError),
+ #[error("smoltcp::socket::udp::SendError {0:?}")]
+ UdpSend(#[from] smoltcp::socket::udp::SendError),
+
#[error("smoltcp::wire::Error {0:?}")]
Wire(#[from] smoltcp::wire::Error),
diff --git a/src/tun2proxy.rs b/src/tun2proxy.rs
index bd68a76..b2dd44a 100644
--- a/src/tun2proxy.rs
+++ b/src/tun2proxy.rs
@@ -530,9 +530,8 @@ impl<'a> TunToProxy<'a> {
udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 4096]);
let mut socket = udp::Socket::new(rx_buffer, tx_buffer);
socket.bind(dst)?;
- socket
- .send_slice(response.as_slice(), UdpMetadata::from(connection_info.src))
- .expect("failed to send DNS response");
+ let meta = UdpMetadata::from(connection_info.src);
+ socket.send_slice(response.as_slice(), meta)?;
let handle = self.sockets.add(socket);
self.expect_smoltcp_send()?;
self.sockets.remove(handle);
From 4d9b10fd1c27e9045a1308d202c5fa9ef4350803 Mon Sep 17 00:00:00 2001
From: ssrlive <30760636+ssrlive@users.noreply.github.com>
Date: Tue, 8 Aug 2023 11:37:24 +0800
Subject: [PATCH 014/363] verbosity parameter
---
src/main.rs | 19 +++++++++++++++++--
1 file changed, 17 insertions(+), 2 deletions(-)
diff --git a/src/main.rs b/src/main.rs
index bcf8519..ad86438 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,5 +1,4 @@
use clap::Parser;
-use env_logger::Env;
use std::{net::IpAddr, process::ExitCode};
use tun2proxy::{error::Error, main_entry, NetworkInterface, Options, Proxy};
@@ -43,6 +42,10 @@ struct Args {
/// Public proxy IP used in routing setup which should bypassing the tunnel
#[arg(long, value_name = "IP")]
bypass_ip: Option,
+
+ /// Verbosity level
+ #[arg(short, long, value_name = "level", value_enum, default_value = "info")]
+ verbosity: ArgVerbosity,
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, clap::ValueEnum)]
@@ -56,11 +59,23 @@ enum ArgSetup {
Auto,
}
+#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, clap::ValueEnum)]
+enum ArgVerbosity {
+ Off,
+ Error,
+ Warn,
+ Info,
+ Debug,
+ Trace,
+}
+
fn main() -> ExitCode {
dotenvy::dotenv().ok();
- env_logger::Builder::from_env(Env::default().default_filter_or("info")).init();
let args = Args::parse();
+ let default = format!("{}={:?}", module_path!(), args.verbosity);
+ env_logger::Builder::from_env(env_logger::Env::default().default_filter_or(default)).init();
+
let addr = args.proxy.addr;
let proxy_type = args.proxy.proxy_type;
log::info!("Proxy {proxy_type} server: {addr}");
From ff9c258fbd68ccfa60521f76fdea999b1ef3623e Mon Sep 17 00:00:00 2001
From: ssrlive <30760636+ssrlive@users.noreply.github.com>
Date: Tue, 8 Aug 2023 23:45:16 +0800
Subject: [PATCH 015/363] re-formatting with max_width = 120
---
rustfmt.toml | 1 +
src/android.rs | 9 ++-------
src/dns.rs | 18 +++---------------
src/http.rs | 31 ++++++++++++-------------------
src/lib.rs | 17 +++++------------
src/main.rs | 15 ++-------------
src/setup.rs | 46 +++++++---------------------------------------
src/socks.rs | 32 +++++++++-----------------------
src/tun2proxy.rs | 43 +++++++++++++------------------------------
src/virtdns.rs | 4 +---
tests/proxy.rs | 39 +++++++++------------------------------
11 files changed, 64 insertions(+), 191 deletions(-)
create mode 100644 rustfmt.toml
diff --git a/rustfmt.toml b/rustfmt.toml
new file mode 100644
index 0000000..7530651
--- /dev/null
+++ b/rustfmt.toml
@@ -0,0 +1 @@
+max_width = 120
diff --git a/src/android.rs b/src/android.rs
index 02d4e5b..b1b6ca2 100644
--- a/src/android.rs
+++ b/src/android.rs
@@ -23,9 +23,7 @@ pub unsafe extern "C" fn Java_com_github_shadowsocks_bg_Tun2proxy_run(
) -> jint {
let log_level = if verbose != 0 { "trace" } else { "info" };
let filter_str = &format!("off,tun2proxy={log_level}");
- let filter = android_logger::FilterBuilder::new()
- .parse(filter_str)
- .build();
+ let filter = android_logger::FilterBuilder::new().parse(filter_str).build();
android_logger::init_once(
android_logger::Config::default()
.with_tag("tun2proxy")
@@ -61,10 +59,7 @@ pub unsafe extern "C" fn Java_com_github_shadowsocks_bg_Tun2proxy_run(
///
/// Shutdown tun2proxy
#[no_mangle]
-pub unsafe extern "C" fn Java_com_github_shadowsocks_bg_Tun2proxy_stop(
- _env: JNIEnv,
- _clazz: JClass,
-) -> jint {
+pub unsafe extern "C" fn Java_com_github_shadowsocks_bg_Tun2proxy_stop(_env: JNIEnv, _: JClass) -> jint {
match &mut TUN_TO_PROXY {
None => {
log::error!("tun2proxy not started");
diff --git a/src/dns.rs b/src/dns.rs
index f4b2404..2ae1528 100644
--- a/src/dns.rs
+++ b/src/dns.rs
@@ -7,11 +7,7 @@ use trust_dns_proto::{
};
#[cfg(feature = "use-rand")]
-pub fn build_dns_request(
- domain: &str,
- query_type: RecordType,
- used_by_tcp: bool,
-) -> Result, String> {
+pub fn build_dns_request(domain: &str, query_type: RecordType, used_by_tcp: bool) -> Result, String> {
// [dependencies]
// rand = "0.8"
use rand::{rngs::StdRng, Rng, SeedableRng};
@@ -34,12 +30,7 @@ pub fn build_dns_request(
}
}
-pub fn build_dns_response(
- mut request: Message,
- domain: &str,
- ip: IpAddr,
- ttl: u32,
-) -> Result {
+pub fn build_dns_response(mut request: Message, domain: &str, ip: IpAddr, ttl: u32) -> Result {
let record = match ip {
IpAddr::V4(ip) => {
let mut record = Record::with(Name::from_str(domain)?, RecordType::A, ttl);
@@ -62,10 +53,7 @@ pub fn extract_ipaddr_from_dns_message(message: &Message) -> Result {
return Ok(IpAddr::V4(*addr));
}
diff --git a/src/http.rs b/src/http.rs
index 5c5d76b..b7ddfce 100644
--- a/src/http.rs
+++ b/src/http.rs
@@ -1,8 +1,8 @@
use crate::{
error::Error,
tun2proxy::{
- ConnectionInfo, ConnectionManager, Direction, IncomingDataEvent, IncomingDirection,
- OutgoingDataEvent, OutgoingDirection, TcpProxy,
+ ConnectionInfo, ConnectionManager, Direction, IncomingDataEvent, IncomingDirection, OutgoingDataEvent,
+ OutgoingDirection, TcpProxy,
},
};
use base64::Engine;
@@ -89,11 +89,9 @@ impl HttpConnection {
fn send_tunnel_request(&mut self) -> Result<(), Error> {
self.server_outbuf.extend(b"CONNECT ");
- self.server_outbuf
- .extend(self.destination.to_string().as_bytes());
+ self.server_outbuf.extend(self.destination.to_string().as_bytes());
self.server_outbuf.extend(b" HTTP/1.1\r\nHost: ");
- self.server_outbuf
- .extend(self.destination.to_string().as_bytes());
+ self.server_outbuf.extend(self.destination.to_string().as_bytes());
self.server_outbuf.extend(b"\r\n");
self.send_auth_data(if self.digest_state.borrow().is_none() {
@@ -126,14 +124,8 @@ impl HttpConnection {
let mut state = self.digest_state.borrow_mut();
let response = state.as_mut().unwrap().respond(&context)?;
- self.server_outbuf.extend(
- format!(
- "{}: {}\r\n",
- PROXY_AUTHORIZATION,
- response.to_header_string()
- )
- .as_bytes(),
- );
+ self.server_outbuf
+ .extend(format!("{}: {}\r\n", PROXY_AUTHORIZATION, response.to_header_string()).as_bytes());
}
AuthenticationScheme::Basic => {
let cred = format!("{}:{}", credentials.username, credentials.password);
@@ -198,8 +190,11 @@ impl HttpConnection {
}
if status_code != 407 {
- let e =
- format!("Expected success status code. Server replied with {status_code} [Reason: {}].", res.reason.unwrap());
+ let e = format!(
+ "Expected success status code. Server replied with {} [Reason: {}].",
+ status_code,
+ res.reason.unwrap()
+ );
return Err(e.into());
}
@@ -371,9 +366,7 @@ impl TcpProxy for HttpConnection {
match dir {
Direction::Incoming(incoming) => match incoming {
IncomingDirection::FromServer => !self.server_inbuf.is_empty(),
- IncomingDirection::FromClient => {
- !self.client_inbuf.is_empty() || !self.data_buf.is_empty()
- }
+ IncomingDirection::FromClient => !self.client_inbuf.is_empty() || !self.data_buf.is_empty(),
},
Direction::Outgoing(outgoing) => match outgoing {
OutgoingDirection::ToServer => !self.server_outbuf.is_empty(),
diff --git a/src/lib.rs b/src/lib.rs
index 9588a4f..01ea7ba 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -124,24 +124,17 @@ pub fn tun_to_proxy<'a>(
let mut ttp = TunToProxy::new(interface, options)?;
let credentials = proxy.credentials.clone();
let server = proxy.addr;
+ #[rustfmt::skip]
let mgr = match proxy.proxy_type {
- ProxyType::Socks4 => Rc::new(SocksProxyManager::new(server, Version::V4, credentials))
- as Rc,
- ProxyType::Socks5 => Rc::new(SocksProxyManager::new(server, Version::V5, credentials))
- as Rc,
- ProxyType::Http => {
- Rc::new(HttpManager::new(server, credentials)) as Rc
- }
+ ProxyType::Socks4 => Rc::new(SocksProxyManager::new(server, Version::V4, credentials)) as Rc,
+ ProxyType::Socks5 => Rc::new(SocksProxyManager::new(server, Version::V5, credentials)) as Rc,
+ ProxyType::Http => Rc::new(HttpManager::new(server, credentials)) as Rc,
};
ttp.add_connection_manager(mgr);
Ok(ttp)
}
-pub fn main_entry(
- interface: &NetworkInterface,
- proxy: &Proxy,
- options: Options,
-) -> Result<(), Error> {
+pub fn main_entry(interface: &NetworkInterface, proxy: &Proxy, options: Options) -> Result<(), Error> {
let mut ttp = tun_to_proxy(interface, proxy, options)?;
ttp.run()?;
Ok(())
diff --git a/src/main.rs b/src/main.rs
index ad86438..1502599 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -26,13 +26,7 @@ struct Args {
proxy: Proxy,
/// DNS handling
- #[arg(
- short,
- long,
- value_name = "method",
- value_enum,
- default_value = "virtual"
- )]
+ #[arg(short, long, value_name = "method", value_enum, default_value = "virtual")]
dns: ArgDns,
/// Routing and system setup
@@ -102,12 +96,7 @@ fn main() -> ExitCode {
Some(addr) => addr,
None => args.proxy.addr.ip(),
};
- setup = Setup::new(
- &args.tun,
- &bypass_tun_ip,
- get_default_cidrs(),
- args.bypass_ip.is_some(),
- );
+ setup = Setup::new(&args.tun, &bypass_tun_ip, get_default_cidrs(), args.bypass_ip.is_some());
setup.configure()?;
diff --git a/src/setup.rs b/src/setup.rs
index 8838ab3..c273ea5 100644
--- a/src/setup.rs
+++ b/src/setup.rs
@@ -59,13 +59,7 @@ where
cmdline.append(&mut args);
let command = cmdline.as_slice().join(" ");
match String::from_utf8(output.stderr.clone()) {
- Ok(output) => Err(format!(
- "[{}] Command `{}` failed: {}",
- nix::unistd::getpid(),
- command,
- output
- )
- .into()),
+ Ok(output) => Err(format!("[{}] Command `{}` failed: {}", nix::unistd::getpid(), command, output).into()),
Err(_) => Err(format!(
"Command `{:?}` failed with exit code {}",
command,
@@ -126,14 +120,7 @@ impl Setup {
}
let (addr_str, prefix_len_str) = match dst_str.split_once(['/']) {
- None => (
- dst_str,
- if self.tunnel_bypass_addr.is_ipv6() {
- "128"
- } else {
- "32"
- },
- ),
+ None => (dst_str, if self.tunnel_bypass_addr.is_ipv6() { "128" } else { "32" }),
Some((addr_str, prefix_len_str)) => (addr_str, prefix_len_str),
};
@@ -215,13 +202,8 @@ impl Setup {
fn shutdown(&mut self) -> Result<(), Error> {
self.set_up = false;
- log::info!(
- "[{}] Restoring network configuration",
- nix::unistd::getpid()
- );
- let _ = Command::new("ip")
- .args(["link", "del", self.tun.as_str()])
- .output();
+ log::info!("[{}] Restoring network configuration", nix::unistd::getpid());
+ let _ = Command::new("ip").args(["link", "del", self.tun.as_str()]).output();
if self.delete_proxy_route {
let _ = Command::new("ip")
.args(["route", "del", self.tunnel_bypass_addr.to_string().as_str()])
@@ -235,15 +217,7 @@ impl Setup {
if let Err(e) = (|| -> Result<(), Error> {
nix::unistd::close(read_from_child)?;
run_iproute(
- [
- "ip",
- "tuntap",
- "add",
- "name",
- self.tun.as_str(),
- "mode",
- "tun",
- ],
+ ["ip", "tuntap", "add", "name", self.tun.as_str(), "mode", "tun"],
"failed to create tunnel device",
true,
)?;
@@ -306,10 +280,7 @@ impl Setup {
}
pub fn configure(&mut self) -> Result<(), Error> {
- log::info!(
- "[{}] Setting up network configuration",
- nix::unistd::getpid()
- );
+ log::info!("[{}] Setting up network configuration", nix::unistd::getpid());
if nix::unistd::getuid() != 0.into() {
return Err("Automatic setup requires root privileges".into());
}
@@ -345,10 +316,7 @@ impl Setup {
}
pub fn restore(&mut self) -> Result<(), Error> {
- nix::sys::signal::kill(
- nix::unistd::Pid::from_raw(self.child),
- nix::sys::signal::SIGINT,
- )?;
+ nix::sys::signal::kill(nix::unistd::Pid::from_raw(self.child), nix::sys::signal::SIGINT)?;
nix::sys::wait::waitpid(nix::unistd::Pid::from_raw(self.child), None)?;
Ok(())
}
diff --git a/src/socks.rs b/src/socks.rs
index a8e4fde..9888415 100644
--- a/src/socks.rs
+++ b/src/socks.rs
@@ -1,14 +1,12 @@
use crate::{
error::Error,
tun2proxy::{
- ConnectionInfo, ConnectionManager, Direction, IncomingDataEvent, IncomingDirection,
- OutgoingDataEvent, OutgoingDirection, TcpProxy,
+ ConnectionInfo, ConnectionManager, Direction, IncomingDataEvent, IncomingDirection, OutgoingDataEvent,
+ OutgoingDirection, TcpProxy,
},
};
use smoltcp::wire::IpProtocol;
-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};
use std::{collections::VecDeque, net::SocketAddr};
#[derive(Eq, PartialEq, Debug)]
@@ -36,11 +34,7 @@ struct SocksProxyImpl {
}
impl SocksProxyImpl {
- pub fn new(
- info: &ConnectionInfo,
- credentials: Option,
- version: Version,
- ) -> Result {
+ fn new(info: &ConnectionInfo, credentials: Option, version: Version) -> Result {
let mut result = Self {
info: info.clone(),
state: SocksState::ServerHello,
@@ -60,8 +54,7 @@ impl SocksProxyImpl {
let credentials = &self.credentials;
self.server_outbuf
.extend(&[self.version as u8, protocol::Command::Connect.into()]);
- self.server_outbuf
- .extend(self.info.dst.port().to_be_bytes());
+ self.server_outbuf.extend(self.info.dst.port().to_be_bytes());
let mut ip_vec = Vec::::new();
let mut name_vec = Vec::::new();
match &self.info.dst {
@@ -94,11 +87,7 @@ impl SocksProxyImpl {
let credentials = &self.credentials;
// Providing unassigned methods is supposed to bypass China's GFW.
// For details, refer to https://github.com/blechschmidt/tun2proxy/issues/35.
- let mut methods = vec![
- AuthMethod::NoAuth,
- AuthMethod::from(4_u8),
- AuthMethod::from(100_u8),
- ];
+ let mut methods = vec![AuthMethod::NoAuth, AuthMethod::from(4_u8), AuthMethod::from(100_u8)];
if credentials.is_some() {
methods.push(AuthMethod::UserPass);
}
@@ -151,8 +140,7 @@ impl SocksProxyImpl {
let auth_method = respones.method;
if auth_method != AuthMethod::NoAuth && self.credentials.is_none()
- || (auth_method != AuthMethod::NoAuth && auth_method != AuthMethod::UserPass)
- && self.credentials.is_some()
+ || (auth_method != AuthMethod::NoAuth && auth_method != AuthMethod::UserPass) && self.credentials.is_some()
{
return Err("SOCKS5 server requires an unsupported authentication method.".into());
}
@@ -240,7 +228,7 @@ impl SocksProxyImpl {
Ok(())
}
- pub fn state_change(&mut self) -> Result<(), Error> {
+ fn state_change(&mut self) -> Result<(), Error> {
match self.state {
SocksState::ServerHello => self.receive_server_hello(),
@@ -308,9 +296,7 @@ impl TcpProxy for SocksProxyImpl {
match dir {
Direction::Incoming(incoming) => match incoming {
IncomingDirection::FromServer => !self.server_inbuf.is_empty(),
- IncomingDirection::FromClient => {
- !self.client_inbuf.is_empty() || !self.data_buf.is_empty()
- }
+ IncomingDirection::FromClient => !self.client_inbuf.is_empty() || !self.data_buf.is_empty(),
},
Direction::Outgoing(outgoing) => match outgoing {
OutgoingDirection::ToServer => !self.server_outbuf.is_empty(),
diff --git a/src/tun2proxy.rs b/src/tun2proxy.rs
index b2dd44a..1a39e4d 100644
--- a/src/tun2proxy.rs
+++ b/src/tun2proxy.rs
@@ -222,25 +222,19 @@ impl<'a> TunToProxy<'a> {
pub fn new(interface: &NetworkInterface, options: Options) -> Result {
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))?
- }
+ NetworkInterface::Fd(fd) => TunTapInterface::from_fd(*fd, Medium::Ip, options.mtu.unwrap_or(1500))?,
};
let poll = Poll::new()?;
- poll.registry().register(
- &mut SourceFd(&tun.as_raw_fd()),
- TUN_TOKEN,
- Interest::READABLE,
- )?;
+ poll.registry()
+ .register(&mut SourceFd(&tun.as_raw_fd()), TUN_TOKEN, Interest::READABLE)?;
let (exit_sender, mut exit_receiver) = mio::unix::pipe::new()?;
poll.registry()
.register(&mut exit_receiver, EXIT_TOKEN, Interest::READABLE)?;
+ #[rustfmt::skip]
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::Ieee802154 => todo!(),
};
@@ -285,8 +279,7 @@ impl<'a> TunToProxy<'a> {
}
fn expect_smoltcp_send(&mut self) -> Result<(), Error> {
- 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() {
let slice = vec.as_slice();
@@ -314,7 +307,7 @@ impl<'a> TunToProxy<'a> {
let token = &conn.token;
self.token_to_info.remove(token);
_ = self.poll.registry().deregister(&mut conn.mio_stream);
- log::info!("CLOSE {}", info);
+ log::info!("Close {}", info);
}
Ok(())
}
@@ -429,8 +422,7 @@ impl<'a> TunToProxy<'a> {
interest = Interest::READABLE | Interest::WRITABLE;
}
- poll.registry()
- .register(&mut state.mio_stream, state.token, interest)?;
+ poll.registry().register(&mut state.mio_stream, state.token, interest)?;
Ok(())
}
@@ -524,10 +516,8 @@ impl<'a> TunToProxy<'a> {
let payload = &frame[payload_offset..payload_offset + payload_size];
let response = virtual_dns.receive_query(payload)?;
{
- let rx_buffer =
- udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 4096]);
- let tx_buffer =
- udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 4096]);
+ let rx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 4096]);
+ let tx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 4096]);
let mut socket = udp::Socket::new(rx_buffer, tx_buffer);
socket.bind(dst)?;
let meta = UdpMetadata::from(connection_info.src);
@@ -551,9 +541,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.tcp_proxy_handler.peek_data(OutgoingDirection::ToServer);
let buffer_size = event.buffer.len();
if buffer_size == 0 {
state.wait_write = false;
@@ -590,9 +578,7 @@ impl<'a> TunToProxy<'a> {
Some(handle) => handle,
None => break,
};
- let event = state
- .tcp_proxy_handler
- .peek_data(OutgoingDirection::ToClient);
+ let event = state.tcp_proxy_handler.peek_data(OutgoingDirection::ToClient);
let buflen = event.buffer.len();
let consumed;
{
@@ -662,10 +648,7 @@ impl<'a> TunToProxy<'a> {
}
};
- let server = self
- .get_connection_manager(&conn_info)
- .ok_or(e)?
- .get_server_addr();
+ let server = self.get_connection_manager(&conn_info).ok_or(e)?.get_server_addr();
let mut block = || -> Result<(), Error> {
if event.is_readable() || event.is_read_closed() {
diff --git a/src/virtdns.rs b/src/virtdns.rs
index 9e13e05..148b45c 100644
--- a/src/virtdns.rs
+++ b/src/virtdns.rs
@@ -120,9 +120,7 @@ impl VirtualDns {
let started_at = self.next_addr;
loop {
- if let RawEntryMut::Vacant(vacant) =
- self.lru_cache.raw_entry_mut().from_key(&self.next_addr)
- {
+ if let RawEntryMut::Vacant(vacant) = self.lru_cache.raw_entry_mut().from_key(&self.next_addr) {
let expiry = Instant::now() + Duration::from_secs(MAPPING_TIMEOUT);
let name0 = name.clone();
vacant.insert(self.next_addr, NameCacheEntry { name, expiry });
diff --git a/tests/proxy.rs b/tests/proxy.rs
index 8e1f71c..a7274a2 100644
--- a/tests/proxy.rs
+++ b/tests/proxy.rs
@@ -22,8 +22,7 @@ mod tests {
static TUN_TEST_DEVICE: &str = "tun0";
fn proxy_from_env(env_var: &str) -> Result {
- let url =
- env::var(env_var).map_err(|_| format!("{env_var} environment variable not found"))?;
+ let url = env::var(env_var).map_err(|_| format!("{env_var} environment variable not found"))?;
Proxy::from_url(url.as_str()).map_err(|_| format!("{env_var} URL cannot be parsed"))
}
@@ -71,15 +70,13 @@ mod tests {
Ok(ip_str) => IpAddr::from_str(ip_str.as_str()).unwrap(),
};
- let mut setup =
- Setup::new(TUN_TEST_DEVICE, &bypass_ip, get_default_cidrs(), false);
+ let mut setup = Setup::new(TUN_TEST_DEVICE, &bypass_ip, get_default_cidrs(), false);
setup.configure().unwrap();
match fork::fork() {
Ok(Fork::Parent(child)) => {
test_function();
- signal::kill(Pid::from_raw(child), signal::SIGINT)
- .expect("failed to kill child");
+ signal::kill(Pid::from_raw(child), signal::SIGINT).expect("failed to kill child");
setup.restore().unwrap();
}
Ok(Fork::Child) => {
@@ -109,59 +106,41 @@ mod tests {
#[test_log::test]
fn test_socks4() {
require_var("SOCKS4_SERVER");
- run_test(
- |test| test.proxy.proxy_type == ProxyType::Socks4,
- request_ip_host_http,
- )
+ run_test(|test| test.proxy.proxy_type == ProxyType::Socks4, request_ip_host_http)
}
#[serial]
#[test_log::test]
fn test_socks5() {
require_var("SOCKS5_SERVER");
- run_test(
- |test| test.proxy.proxy_type == ProxyType::Socks5,
- request_ip_host_http,
- )
+ run_test(|test| test.proxy.proxy_type == ProxyType::Socks5, request_ip_host_http)
}
#[serial]
#[test_log::test]
fn test_http() {
require_var("HTTP_SERVER");
- run_test(
- |test| test.proxy.proxy_type == ProxyType::Http,
- request_ip_host_http,
- )
+ run_test(|test| test.proxy.proxy_type == ProxyType::Http, request_ip_host_http)
}
#[serial]
#[test_log::test]
fn test_socks4_dns() {
require_var("SOCKS4_SERVER");
- run_test(
- |test| test.proxy.proxy_type == ProxyType::Socks4,
- request_example_https,
- )
+ run_test(|test| test.proxy.proxy_type == ProxyType::Socks4, request_example_https)
}
#[serial]
#[test_log::test]
fn test_socks5_dns() {
require_var("SOCKS5_SERVER");
- run_test(
- |test| test.proxy.proxy_type == ProxyType::Socks5,
- request_example_https,
- )
+ run_test(|test| test.proxy.proxy_type == ProxyType::Socks5, request_example_https)
}
#[serial]
#[test_log::test]
fn test_http_dns() {
require_var("HTTP_SERVER");
- run_test(
- |test| test.proxy.proxy_type == ProxyType::Http,
- request_example_https,
- )
+ run_test(|test| test.proxy.proxy_type == ProxyType::Http, request_example_https)
}
}
From 3720c41a6b96db807e841b026c45ab807f22ca87 Mon Sep 17 00:00:00 2001
From: ssrlive <30760636+ssrlive@users.noreply.github.com>
Date: Wed, 9 Aug 2023 00:02:33 +0800
Subject: [PATCH 016/363] minor changes
---
src/socks.rs | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/socks.rs b/src/socks.rs
index 9888415..3c71e2a 100644
--- a/src/socks.rs
+++ b/src/socks.rs
@@ -87,7 +87,12 @@ impl SocksProxyImpl {
let credentials = &self.credentials;
// Providing unassigned methods is supposed to bypass China's GFW.
// For details, refer to https://github.com/blechschmidt/tun2proxy/issues/35.
- let mut methods = vec![AuthMethod::NoAuth, AuthMethod::from(4_u8), AuthMethod::from(100_u8)];
+ #[rustfmt::skip]
+ let mut methods = vec![
+ AuthMethod::NoAuth,
+ AuthMethod::from(4_u8),
+ AuthMethod::from(100_u8),
+ ];
if credentials.is_some() {
methods.push(AuthMethod::UserPass);
}
From ca5b550e44395a43e2812f15739fa05b15ecb28d Mon Sep 17 00:00:00 2001
From: ssrlive <30760636+ssrlive@users.noreply.github.com>
Date: Wed, 9 Aug 2023 00:38:32 +0800
Subject: [PATCH 017/363] reformatting code
---
src/tun2proxy.rs | 14 ++++++--------
1 file changed, 6 insertions(+), 8 deletions(-)
diff --git a/src/tun2proxy.rs b/src/tun2proxy.rs
index 1a39e4d..87d88df 100644
--- a/src/tun2proxy.rs
+++ b/src/tun2proxy.rs
@@ -413,14 +413,12 @@ impl<'a> TunToProxy<'a> {
}
// This ugliness is due to the way Interest is implemented (as a NonZeroU8 wrapper).
- let interest;
- if state.wait_read && !state.wait_write {
- interest = Interest::READABLE;
- } else if state.wait_write && !state.wait_read {
- interest = Interest::WRITABLE;
- } else {
- interest = Interest::READABLE | Interest::WRITABLE;
- }
+ let interest = match (state.wait_read, state.wait_write) {
+ (true, false) => Interest::READABLE,
+ (false, true) => Interest::WRITABLE,
+ (true, true) => Interest::READABLE | Interest::WRITABLE,
+ (false, false) => Interest::READABLE | Interest::WRITABLE,
+ };
poll.registry().register(&mut state.mio_stream, state.token, interest)?;
Ok(())
From 64ab4b503c45901242455a86613da892e8c63944 Mon Sep 17 00:00:00 2001
From: ssrlive <30760636+ssrlive@users.noreply.github.com>
Date: Wed, 9 Aug 2023 14:31:33 +0800
Subject: [PATCH 018/363] minor changes
---
src/tun2proxy.rs | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/src/tun2proxy.rs b/src/tun2proxy.rs
index 87d88df..1bb0170 100644
--- a/src/tun2proxy.rs
+++ b/src/tun2proxy.rs
@@ -416,8 +416,7 @@ impl<'a> TunToProxy<'a> {
let interest = match (state.wait_read, state.wait_write) {
(true, false) => Interest::READABLE,
(false, true) => Interest::WRITABLE,
- (true, true) => Interest::READABLE | Interest::WRITABLE,
- (false, false) => Interest::READABLE | Interest::WRITABLE,
+ _ => Interest::READABLE | Interest::WRITABLE,
};
poll.registry().register(&mut state.mio_stream, state.token, interest)?;
From 855aaa04faf3ca67072f768e298cd00fababe778 Mon Sep 17 00:00:00 2001
From: ssrlive <30760636+ssrlive@users.noreply.github.com>
Date: Wed, 9 Aug 2023 16:50:59 +0800
Subject: [PATCH 019/363] read code
---
src/socks.rs | 15 +++++++--------
src/tun2proxy.rs | 20 ++++++++++----------
src/virtdevice.rs | 1 +
3 files changed, 18 insertions(+), 18 deletions(-)
diff --git a/src/socks.rs b/src/socks.rs
index 3c71e2a..ec4bf39 100644
--- a/src/socks.rs
+++ b/src/socks.rs
@@ -194,6 +194,13 @@ impl SocksProxyImpl {
self.state_change()
}
+ fn send_request_socks5(&mut self) -> Result<(), Error> {
+ protocol::Request::new(protocol::Command::Connect, self.info.dst.clone())
+ .write_to_stream(&mut self.server_outbuf)?;
+ self.state = SocksState::ReceiveResponse;
+ self.state_change()
+ }
+
fn receive_connection_status(&mut self) -> Result<(), Error> {
let response = protocol::Response::retrieve_from_stream(&mut self.server_inbuf.clone());
if let Err(e) = &response {
@@ -217,14 +224,6 @@ impl SocksProxyImpl {
self.state_change()
}
- fn send_request_socks5(&mut self) -> Result<(), Error> {
- // self.server_outbuf.extend(&[self.version as u8, self.command as u8, 0]);
- protocol::Request::new(protocol::Command::Connect, self.info.dst.clone())
- .write_to_stream(&mut self.server_outbuf)?;
- self.state = SocksState::ReceiveResponse;
- self.state_change()
- }
-
fn relay_traffic(&mut self) -> Result<(), Error> {
self.client_outbuf.extend(self.server_inbuf.iter());
self.server_outbuf.extend(self.client_inbuf.iter());
diff --git a/src/tun2proxy.rs b/src/tun2proxy.rs
index 1bb0170..8be7f06 100644
--- a/src/tun2proxy.rs
+++ b/src/tun2proxy.rs
@@ -12,7 +12,7 @@ use std::{
collections::{HashMap, HashSet},
convert::{From, TryFrom},
io::{Read, Write},
- net::{IpAddr, Ipv4Addr, Ipv6Addr, Shutdown, Shutdown::Both, SocketAddr},
+ net::{IpAddr, Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr},
os::unix::io::AsRawFd,
rc::Rc,
str::FromStr,
@@ -278,6 +278,7 @@ impl<'a> TunToProxy<'a> {
self.connection_managers.push(manager);
}
+ /// Read data from virtual device (remote server) and inject it into tun interface.
fn expect_smoltcp_send(&mut self) -> Result<(), Error> {
self.iface.poll(Instant::now(), &mut self.device, &mut self.sockets);
@@ -297,7 +298,7 @@ impl<'a> TunToProxy<'a> {
fn remove_connection(&mut self, info: &ConnectionInfo) -> Result<(), Error> {
if let Some(mut conn) = self.connection_map.remove(info) {
- _ = conn.mio_stream.shutdown(Both);
+ _ = conn.mio_stream.shutdown(Shutdown::Both);
if let Some(handle) = conn.smoltcp_handle {
let socket = self.sockets.get_mut::(handle);
socket.close();
@@ -322,11 +323,10 @@ impl<'a> TunToProxy<'a> {
}
fn check_change_close_state(&mut self, info: &ConnectionInfo) -> Result<(), Error> {
- let state = self.connection_map.get_mut(info);
- if state.is_none() {
- return Ok(());
- }
- let state = state.unwrap();
+ let state = match self.connection_map.get_mut(info) {
+ None => return Ok(()),
+ Some(state) => state,
+ };
let mut closed_ends = 0;
if (state.close_state & SERVER_WRITE_CLOSED) == SERVER_WRITE_CLOSED
&& !state
@@ -492,7 +492,7 @@ impl<'a> TunToProxy<'a> {
log::trace!("Subsequent packet {} ({})", connection_info, dst);
}
- // Inject the packet to advance the smoltcp socket state
+ // Inject the packet to advance the remote proxy server smoltcp socket state
self.device.inject_packet(frame);
// Having advanced the socket state, we expect the socket to ACK
@@ -509,8 +509,8 @@ impl<'a> TunToProxy<'a> {
} else if connection_info.protocol == IpProtocol::Udp {
log::trace!("{} ({})", connection_info, dst);
let port = connection_info.dst.port();
+ let payload = &frame[payload_offset..payload_offset + payload_size];
if let (Some(virtual_dns), true) = (&mut self.options.virtual_dns, port == 53) {
- let payload = &frame[payload_offset..payload_offset + payload_size];
let response = virtual_dns.receive_query(payload)?;
{
let rx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 4096]);
@@ -680,7 +680,7 @@ impl<'a> TunToProxy<'a> {
if state.tcp_proxy_handler.reset_connection() {
_ = self.poll.registry().deregister(&mut state.mio_stream);
// Closes the connection with the proxy
- state.mio_stream.shutdown(Both)?;
+ state.mio_stream.shutdown(Shutdown::Both)?;
log::info!("RESET {}", conn_info);
diff --git a/src/virtdevice.rs b/src/virtdevice.rs
index fc862d9..721466c 100644
--- a/src/virtdevice.rs
+++ b/src/virtdevice.rs
@@ -3,6 +3,7 @@ use smoltcp::{
time::Instant,
};
+/// Virtual device representing the remote proxy server.
#[derive(Default)]
pub struct VirtualTunDevice {
capabilities: DeviceCapabilities,
From 507def8f294236a1019f0e14e27cf3a7ae720a80 Mon Sep 17 00:00:00 2001
From: ssrlive <30760636+ssrlive@users.noreply.github.com>
Date: Thu, 10 Aug 2023 11:18:15 +0800
Subject: [PATCH 020/363] create_new_tcp_proxy_connection
---
src/tun2proxy.rs | 64 ++++++++++++++++++++++++++++++------------------
1 file changed, 40 insertions(+), 24 deletions(-)
diff --git a/src/tun2proxy.rs b/src/tun2proxy.rs
index 8be7f06..e33f43b 100644
--- a/src/tun2proxy.rs
+++ b/src/tun2proxy.rs
@@ -452,31 +452,12 @@ impl<'a> TunToProxy<'a> {
continue;
}
let tcp_proxy_handler = tcp_proxy_handler?;
- let mut socket = tcp::Socket::new(
- tcp::SocketBuffer::new(vec![0; 1024 * 128]),
- tcp::SocketBuffer::new(vec![0; 1024 * 128]),
- );
- socket.set_ack_delay(None);
- socket.listen(dst)?;
- let handle = self.sockets.add(socket);
-
- let mut client = TcpStream::connect(server_addr)?;
- let token = self.new_token();
- let i = Interest::READABLE;
- self.poll.registry().register(&mut client, token, i)?;
-
- let state = TcpConnectState {
- smoltcp_handle: Some(handle),
- mio_stream: client,
- token,
+ self.create_new_tcp_proxy_connection(
+ server_addr,
+ dst,
tcp_proxy_handler,
- close_state: 0,
- wait_read: true,
- wait_write: false,
- };
- self.connection_map.insert(connection_info.clone(), state);
-
- self.token_to_info.insert(token, connection_info.clone());
+ connection_info.clone(),
+ )?;
log::info!("Connect done {} ({})", connection_info, dst);
done = true;
@@ -536,6 +517,41 @@ impl<'a> TunToProxy<'a> {
Ok(())
}
+ fn create_new_tcp_proxy_connection(
+ &mut self,
+ server_addr: SocketAddr,
+ dst: SocketAddr,
+ tcp_proxy_handler: Box,
+ connection_info: ConnectionInfo,
+ ) -> Result<()> {
+ let mut socket = tcp::Socket::new(
+ tcp::SocketBuffer::new(vec![0; 1024 * 128]),
+ tcp::SocketBuffer::new(vec![0; 1024 * 128]),
+ );
+ socket.set_ack_delay(None);
+ socket.listen(dst)?;
+ let handle = self.sockets.add(socket);
+
+ let mut client = TcpStream::connect(server_addr)?;
+ let token = self.new_token();
+ let i = Interest::READABLE;
+ self.poll.registry().register(&mut client, token, i)?;
+
+ let state = TcpConnectState {
+ smoltcp_handle: Some(handle),
+ mio_stream: client,
+ token,
+ tcp_proxy_handler,
+ close_state: 0,
+ wait_read: true,
+ wait_write: false,
+ };
+ self.connection_map.insert(connection_info.clone(), state);
+
+ self.token_to_info.insert(token, connection_info.clone());
+ Ok(())
+ }
+
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);
From 94835c41a4f45583a631b1e21f376ca89794838f Mon Sep 17 00:00:00 2001
From: ssrlive <30760636+ssrlive@users.noreply.github.com>
Date: Thu, 10 Aug 2023 12:57:35 +0800
Subject: [PATCH 021/363] UDP Associate
---
src/http.rs | 2 +-
src/socks.rs | 26 ++++++++++++++++++++------
src/tun2proxy.rs | 12 +++++++++---
3 files changed, 30 insertions(+), 10 deletions(-)
diff --git a/src/http.rs b/src/http.rs
index b7ddfce..d3ca650 100644
--- a/src/http.rs
+++ b/src/http.rs
@@ -391,7 +391,7 @@ impl ConnectionManager for HttpManager {
info.protocol == IpProtocol::Tcp
}
- fn new_tcp_proxy(&self, info: &ConnectionInfo) -> Result, Error> {
+ fn new_tcp_proxy(&self, info: &ConnectionInfo, _: bool) -> Result, Error> {
if info.protocol != IpProtocol::Tcp {
return Err("Invalid protocol".into());
}
diff --git a/src/socks.rs b/src/socks.rs
index ec4bf39..4d01125 100644
--- a/src/socks.rs
+++ b/src/socks.rs
@@ -1,5 +1,5 @@
use crate::{
- error::Error,
+ error::{Error, Result},
tun2proxy::{
ConnectionInfo, ConnectionManager, Direction, IncomingDataEvent, IncomingDirection, OutgoingDataEvent,
OutgoingDirection, TcpProxy,
@@ -7,7 +7,7 @@ use crate::{
};
use smoltcp::wire::IpProtocol;
use socks5_impl::protocol::{self, handshake, password_method, Address, AuthMethod, StreamOperation, UserKey, Version};
-use std::{collections::VecDeque, net::SocketAddr};
+use std::{collections::VecDeque, convert::TryFrom, net::SocketAddr};
#[derive(Eq, PartialEq, Debug)]
#[allow(dead_code)]
@@ -31,10 +31,17 @@ struct SocksProxyImpl {
data_buf: VecDeque,
version: Version,
credentials: Option,
+ command: protocol::Command,
+ udp_associate: Option,
}
impl SocksProxyImpl {
- fn new(info: &ConnectionInfo, credentials: Option, version: Version) -> Result {
+ fn new(
+ info: &ConnectionInfo,
+ credentials: Option,
+ version: Version,
+ command: protocol::Command,
+ ) -> Result {
let mut result = Self {
info: info.clone(),
state: SocksState::ServerHello,
@@ -45,6 +52,8 @@ impl SocksProxyImpl {
data_buf: VecDeque::default(),
version,
credentials,
+ command,
+ udp_associate: None,
};
result.send_client_hello()?;
Ok(result)
@@ -195,8 +204,7 @@ impl SocksProxyImpl {
}
fn send_request_socks5(&mut self) -> Result<(), Error> {
- protocol::Request::new(protocol::Command::Connect, self.info.dst.clone())
- .write_to_stream(&mut self.server_outbuf)?;
+ protocol::Request::new(self.command, self.info.dst.clone()).write_to_stream(&mut self.server_outbuf)?;
self.state = SocksState::ReceiveResponse;
self.state_change()
}
@@ -216,6 +224,9 @@ impl SocksProxyImpl {
if response.reply != protocol::Reply::Succeeded {
return Err(format!("SOCKS connection failed: {}", response.reply).into());
}
+ if self.command == protocol::Command::UdpAssociate {
+ self.udp_associate = Some(SocketAddr::try_from(response.address)?);
+ }
self.server_outbuf.append(&mut self.data_buf);
self.data_buf.clear();
@@ -325,14 +336,17 @@ impl ConnectionManager for SocksProxyManager {
info.protocol == IpProtocol::Tcp
}
- fn new_tcp_proxy(&self, info: &ConnectionInfo) -> Result, Error> {
+ fn new_tcp_proxy(&self, info: &ConnectionInfo, udp_associate: bool) -> Result> {
if info.protocol != IpProtocol::Tcp {
return Err("Invalid protocol".into());
}
+ use socks5_impl::protocol::Command::{Connect, UdpAssociate};
+ let command = if udp_associate { UdpAssociate } else { Connect };
Ok(Box::new(SocksProxyImpl::new(
info,
self.credentials.clone(),
self.version,
+ command,
)?))
}
diff --git a/src/tun2proxy.rs b/src/tun2proxy.rs
index e33f43b..0cabacf 100644
--- a/src/tun2proxy.rs
+++ b/src/tun2proxy.rs
@@ -193,7 +193,7 @@ pub(crate) trait UdpProxy {
pub(crate) trait ConnectionManager {
fn handles_connection(&self, info: &ConnectionInfo) -> bool;
- fn new_tcp_proxy(&self, info: &ConnectionInfo) -> Result, Error>;
+ fn new_tcp_proxy(&self, info: &ConnectionInfo, udp_associate: bool) -> Result, Error>;
fn close_connection(&self, info: &ConnectionInfo);
fn get_server_addr(&self) -> SocketAddr;
fn get_credentials(&self) -> &Option;
@@ -447,7 +447,7 @@ impl<'a> TunToProxy<'a> {
if first_packet {
let mut done = false;
for manager in self.connection_managers.iter_mut() {
- let tcp_proxy_handler = manager.new_tcp_proxy(&connection_info);
+ let tcp_proxy_handler = manager.new_tcp_proxy(&connection_info, false);
if tcp_proxy_handler.is_err() {
continue;
}
@@ -504,8 +504,14 @@ impl<'a> TunToProxy<'a> {
self.expect_smoltcp_send()?;
self.sockets.remove(handle);
}
+ } else {
+ // Another UDP packet
+ let cm = self.get_connection_manager(&connection_info);
+ if cm.is_none() {
+ return Ok(());
+ }
+ // TODO: Handle UDP packets
}
- // Otherwise, UDP is not yet supported.
} else {
log::warn!("Unsupported protocol: {} ({})", connection_info, dst);
}
From 489d5fec001baee6d5fc29fab403cb00aa8d7988 Mon Sep 17 00:00:00 2001
From: ssrlive <30760636+ssrlive@users.noreply.github.com>
Date: Thu, 10 Aug 2023 14:17:07 +0800
Subject: [PATCH 022/363] ConnectionInfo
---
src/http.rs | 16 ++++++++++------
src/socks.rs | 4 ++++
src/tun2proxy.rs | 22 +++++++++++-----------
3 files changed, 25 insertions(+), 17 deletions(-)
diff --git a/src/http.rs b/src/http.rs
index d3ca650..05f72d4 100644
--- a/src/http.rs
+++ b/src/http.rs
@@ -8,7 +8,7 @@ use crate::{
use base64::Engine;
use httparse::Response;
use smoltcp::wire::IpProtocol;
-use socks5_impl::protocol::{Address, UserKey};
+use socks5_impl::protocol::UserKey;
use std::{
cell::RefCell,
collections::{hash_map::RandomState, HashMap, VecDeque},
@@ -52,7 +52,7 @@ pub struct HttpConnection {
digest_state: Rc>>,
before: bool,
credentials: Option,
- destination: Address,
+ info: ConnectionInfo,
}
static PROXY_AUTHENTICATE: &str = "Proxy-Authenticate";
@@ -80,7 +80,7 @@ impl HttpConnection {
digest_state,
before: false,
credentials,
- destination: info.dst.clone(),
+ info: info.clone(),
};
res.send_tunnel_request()?;
@@ -89,9 +89,9 @@ impl HttpConnection {
fn send_tunnel_request(&mut self) -> Result<(), Error> {
self.server_outbuf.extend(b"CONNECT ");
- self.server_outbuf.extend(self.destination.to_string().as_bytes());
+ self.server_outbuf.extend(self.info.dst.to_string().as_bytes());
self.server_outbuf.extend(b" HTTP/1.1\r\nHost: ");
- self.server_outbuf.extend(self.destination.to_string().as_bytes());
+ self.server_outbuf.extend(self.info.dst.to_string().as_bytes());
self.server_outbuf.extend(b"\r\n");
self.send_auth_data(if self.digest_state.borrow().is_none() {
@@ -111,7 +111,7 @@ impl HttpConnection {
match scheme {
AuthenticationScheme::Digest => {
- let uri = self.destination.to_string();
+ let uri = self.info.dst.to_string();
let context = digest_auth::AuthContext::new_with_method(
&credentials.username,
@@ -318,6 +318,10 @@ impl HttpConnection {
}
impl TcpProxy for HttpConnection {
+ fn get_connection_info(&self) -> &ConnectionInfo {
+ &self.info
+ }
+
fn push_data(&mut self, event: IncomingDataEvent<'_>) -> Result<(), Error> {
let direction = event.direction;
let buffer = event.buffer;
diff --git a/src/socks.rs b/src/socks.rs
index 4d01125..02b893d 100644
--- a/src/socks.rs
+++ b/src/socks.rs
@@ -263,6 +263,10 @@ impl SocksProxyImpl {
}
impl TcpProxy for SocksProxyImpl {
+ fn get_connection_info(&self) -> &ConnectionInfo {
+ &self.info
+ }
+
fn push_data(&mut self, event: IncomingDataEvent<'_>) -> Result<(), Error> {
let direction = event.direction;
let buffer = event.buffer;
diff --git a/src/tun2proxy.rs b/src/tun2proxy.rs
index 0cabacf..33743b5 100644
--- a/src/tun2proxy.rs
+++ b/src/tun2proxy.rs
@@ -18,7 +18,7 @@ use std::{
str::FromStr,
};
-#[derive(Hash, Clone, Eq, PartialEq, Debug)]
+#[derive(Hash, Clone, Eq, PartialEq, PartialOrd, Ord, Debug)]
pub(crate) struct ConnectionInfo {
pub(crate) src: SocketAddr,
pub(crate) dst: Address,
@@ -36,7 +36,6 @@ impl Default for ConnectionInfo {
}
impl ConnectionInfo {
- #[allow(dead_code)]
pub fn new(src: SocketAddr, dst: Address, protocol: IpProtocol) -> Self {
Self { src, dst, protocol }
}
@@ -133,11 +132,11 @@ fn connection_tuple(frame: &[u8]) -> Result<(ConnectionInfo, bool, usize, usize)
let (ports, first_packet, payload_offset, payload_size) =
get_transport_info(protocol, header_len, &frame[header_len..])?;
- let info = ConnectionInfo {
- src: SocketAddr::new(src_addr, ports.0),
- dst: SocketAddr::new(dst_addr, ports.1).into(),
+ let info = ConnectionInfo::new(
+ SocketAddr::new(src_addr, ports.0),
+ SocketAddr::new(dst_addr, ports.1).into(),
protocol,
- };
+ );
return Ok((info, first_packet, payload_offset, payload_size));
}
@@ -154,11 +153,11 @@ fn connection_tuple(frame: &[u8]) -> Result<(ConnectionInfo, bool, usize, usize)
let (ports, first_packet, payload_offset, payload_size) =
get_transport_info(protocol, header_len, &frame[header_len..])?;
- let info = ConnectionInfo {
- src: SocketAddr::new(src_addr, ports.0),
- dst: SocketAddr::new(dst_addr, ports.1).into(),
+ let info = ConnectionInfo::new(
+ SocketAddr::new(src_addr, ports.0),
+ SocketAddr::new(dst_addr, ports.1).into(),
protocol,
- };
+ );
return Ok((info, first_packet, payload_offset, payload_size));
}
Err("Neither IPv6 nor IPv4 packet".into())
@@ -178,6 +177,7 @@ struct TcpConnectState {
}
pub(crate) trait TcpProxy {
+ fn get_connection_info(&self) -> &ConnectionInfo;
fn push_data(&mut self, event: IncomingDataEvent<'_>) -> Result<(), Error>;
fn consume_data(&mut self, dir: OutgoingDirection, size: usize);
fn peek_data(&mut self, dir: OutgoingDirection) -> OutgoingDataEvent;
@@ -193,7 +193,7 @@ pub(crate) trait UdpProxy {
pub(crate) trait ConnectionManager {
fn handles_connection(&self, info: &ConnectionInfo) -> bool;
- fn new_tcp_proxy(&self, info: &ConnectionInfo, udp_associate: bool) -> Result, Error>;
+ fn new_tcp_proxy(&self, info: &ConnectionInfo, udp_associate: bool) -> Result>;
fn close_connection(&self, info: &ConnectionInfo);
fn get_server_addr(&self) -> SocketAddr;
fn get_credentials(&self) -> &Option;
From 57851f029e0bbdf3dae89fc02ac175d7120b4ea1 Mon Sep 17 00:00:00 2001
From: ssrlive <30760636+ssrlive@users.noreply.github.com>
Date: Fri, 11 Aug 2023 01:13:22 +0800
Subject: [PATCH 023/363] token_to_info removed
---
src/tun2proxy.rs | 54 ++++++++++++++++++++++++++++--------------------
1 file changed, 32 insertions(+), 22 deletions(-)
diff --git a/src/tun2proxy.rs b/src/tun2proxy.rs
index 33743b5..e54e7cd 100644
--- a/src/tun2proxy.rs
+++ b/src/tun2proxy.rs
@@ -209,7 +209,6 @@ pub struct TunToProxy<'a> {
connection_map: HashMap,
connection_managers: Vec>,
next_token: usize,
- token_to_info: HashMap,
sockets: SocketSet<'a>,
device: VirtualTunDevice,
options: Options,
@@ -256,7 +255,6 @@ impl<'a> TunToProxy<'a> {
iface,
connection_map: HashMap::default(),
next_token: usize::from(EXIT_TOKEN) + 1,
- token_to_info: HashMap::default(),
connection_managers: Vec::default(),
sockets: SocketSet::new([]),
device,
@@ -296,18 +294,27 @@ impl<'a> TunToProxy<'a> {
Ok(())
}
+ fn find_info_by_token(&self, token: Token) -> Option<&ConnectionInfo> {
+ self.connection_map
+ .iter()
+ .find_map(|(info, state)| if state.token == token { Some(info) } else { None })
+ }
+
+ /// Destroy connection state machine
fn remove_connection(&mut self, info: &ConnectionInfo) -> Result<(), Error> {
- if let Some(mut conn) = self.connection_map.remove(info) {
- _ = conn.mio_stream.shutdown(Shutdown::Both);
- if let Some(handle) = conn.smoltcp_handle {
+ if let Some(mut state) = self.connection_map.remove(info) {
+ _ = state.mio_stream.shutdown(Shutdown::Both);
+ if let Some(handle) = state.smoltcp_handle {
let socket = self.sockets.get_mut::(handle);
socket.close();
self.sockets.remove(handle);
}
+
+ // FIXME: Does this line should be moved up to the beginning of this function?
self.expect_smoltcp_send()?;
- let token = &conn.token;
- self.token_to_info.remove(token);
- _ = self.poll.registry().deregister(&mut conn.mio_stream);
+
+ _ = self.poll.registry().deregister(&mut state.mio_stream);
+
log::info!("Close {}", info);
}
Ok(())
@@ -322,10 +329,11 @@ impl<'a> TunToProxy<'a> {
None
}
+ /// Scan connection state machine and check if any connection should be closed.
fn check_change_close_state(&mut self, info: &ConnectionInfo) -> Result<(), Error> {
let state = match self.connection_map.get_mut(info) {
- None => return Ok(()),
Some(state) => state,
+ None => return Ok(()),
};
let mut closed_ends = 0;
if (state.close_state & SERVER_WRITE_CLOSED) == SERVER_WRITE_CLOSED
@@ -336,8 +344,9 @@ impl<'a> TunToProxy<'a> {
.tcp_proxy_handler
.have_data(Direction::Outgoing(OutgoingDirection::ToClient))
{
- if let Some(socket_handle) = state.smoltcp_handle {
- let socket = self.sockets.get_mut::(socket_handle);
+ if let Some(handle) = state.smoltcp_handle {
+ // Close tun interface
+ let socket = self.sockets.get_mut::(handle);
socket.close();
}
closed_ends += 1;
@@ -351,17 +360,20 @@ impl<'a> TunToProxy<'a> {
.tcp_proxy_handler
.have_data(Direction::Outgoing(OutgoingDirection::ToServer))
{
+ // Close remote server
_ = state.mio_stream.shutdown(Shutdown::Write);
closed_ends += 1;
}
if closed_ends == 2 {
+ // Close connection state machine
self.remove_connection(info)?;
}
Ok(())
}
fn tunsocket_read_and_forward(&mut self, info: &ConnectionInfo) -> Result<(), Error> {
+ // 1. Read data from tun and write to proxy handler (remote server).
// Scope for mutable borrow of self.
{
let state = match self.connection_map.get_mut(info) {
@@ -393,10 +405,10 @@ impl<'a> TunToProxy<'a> {
// need to send data.
state.close_state |= CLIENT_WRITE_CLOSED;
}
-
- // Expect ACKs etc. from smoltcp sockets.
- self.expect_smoltcp_send()?;
}
+ // 2. Write data from proxy handler (remote server) to tun.
+ // Expect ACKs etc. from smoltcp sockets.
+ self.expect_smoltcp_send()?;
self.check_change_close_state(info)?;
@@ -554,7 +566,6 @@ impl<'a> TunToProxy<'a> {
};
self.connection_map.insert(connection_info.clone(), state);
- self.token_to_info.insert(token, connection_info.clone());
Ok(())
}
@@ -593,7 +604,7 @@ impl<'a> TunToProxy<'a> {
fn write_to_client(&mut self, token: Token, info: &ConnectionInfo) -> Result<(), Error> {
while let Some(state) = self.connection_map.get_mut(info) {
- let socket_handle = match state.smoltcp_handle {
+ let handle = match state.smoltcp_handle {
Some(handle) => handle,
None => break,
};
@@ -601,7 +612,7 @@ impl<'a> TunToProxy<'a> {
let buflen = event.buffer.len();
let consumed;
{
- let socket = self.sockets.get_mut::(socket_handle);
+ let socket = self.sockets.get_mut::(handle);
if socket.may_send() {
if let Some(virtual_dns) = &mut self.options.virtual_dns {
// Unwrapping is fine because every smoltcp socket is bound to an.
@@ -641,11 +652,10 @@ impl<'a> TunToProxy<'a> {
}
fn send_to_smoltcp(&mut self) -> Result<(), Error> {
- let cloned = self.write_sockets.clone();
- for token in cloned.iter() {
- if let Some(connection) = self.token_to_info.get(token) {
+ for token in self.write_sockets.clone().into_iter() {
+ if let Some(connection) = self.find_info_by_token(token) {
let connection = connection.clone();
- if let Err(error) = self.write_to_client(*token, &connection) {
+ if let Err(error) = self.write_to_client(token, &connection) {
self.remove_connection(&connection)?;
log::error!("Write to client: {}: ", error);
}
@@ -656,7 +666,7 @@ impl<'a> TunToProxy<'a> {
fn mio_socket_event(&mut self, event: &Event) -> Result<(), Error> {
let e = "connection not found";
- let conn_info = match self.token_to_info.get(&event.token()) {
+ let conn_info = match self.find_info_by_token(event.token()) {
Some(conn_info) => conn_info.clone(),
None => {
// We may have closed the connection in an earlier iteration over the poll events,
From d00a18c8650bda19de400bc730a14c5c98b72ea8 Mon Sep 17 00:00:00 2001
From: ssrlive <30760636+ssrlive@users.noreply.github.com>
Date: Fri, 11 Aug 2023 15:06:21 +0800
Subject: [PATCH 024/363] create_new_tcp_connection_state
---
src/tun2proxy.rs | 27 +++++++++++++--------------
1 file changed, 13 insertions(+), 14 deletions(-)
diff --git a/src/tun2proxy.rs b/src/tun2proxy.rs
index e54e7cd..4bdf1a8 100644
--- a/src/tun2proxy.rs
+++ b/src/tun2proxy.rs
@@ -464,12 +464,8 @@ impl<'a> TunToProxy<'a> {
continue;
}
let tcp_proxy_handler = tcp_proxy_handler?;
- self.create_new_tcp_proxy_connection(
- server_addr,
- dst,
- tcp_proxy_handler,
- connection_info.clone(),
- )?;
+ let state = self.create_new_tcp_connection_state(server_addr, dst, tcp_proxy_handler)?;
+ self.connection_map.insert(connection_info.clone(), state);
log::info!("Connect done {} ({})", connection_info, dst);
done = true;
@@ -518,10 +514,16 @@ impl<'a> TunToProxy<'a> {
}
} else {
// Another UDP packet
- let cm = self.get_connection_manager(&connection_info);
- if cm.is_none() {
+ let manager = self.get_connection_manager(&connection_info);
+ if manager.is_none() {
return Ok(());
}
+ let manager = manager.ok_or("")?;
+ let server_addr = manager.get_server_addr();
+ let tcp_proxy_handler = manager.new_tcp_proxy(&connection_info, true)?;
+ let state = self.create_new_tcp_connection_state(server_addr, dst, tcp_proxy_handler)?;
+ self.connection_map.insert(connection_info.clone(), state);
+
// TODO: Handle UDP packets
}
} else {
@@ -535,13 +537,12 @@ impl<'a> TunToProxy<'a> {
Ok(())
}
- fn create_new_tcp_proxy_connection(
+ fn create_new_tcp_connection_state(
&mut self,
server_addr: SocketAddr,
dst: SocketAddr,
tcp_proxy_handler: Box,
- connection_info: ConnectionInfo,
- ) -> Result<()> {
+ ) -> Result {
let mut socket = tcp::Socket::new(
tcp::SocketBuffer::new(vec![0; 1024 * 128]),
tcp::SocketBuffer::new(vec![0; 1024 * 128]),
@@ -564,9 +565,7 @@ impl<'a> TunToProxy<'a> {
wait_read: true,
wait_write: false,
};
- self.connection_map.insert(connection_info.clone(), state);
-
- Ok(())
+ Ok(state)
}
fn write_to_server(&mut self, info: &ConnectionInfo) -> Result<(), Error> {
From 46ca342aba1a5ba285645a289916a5479f4102f1 Mon Sep 17 00:00:00 2001
From: ssrlive <30760636+ssrlive@users.noreply.github.com>
Date: Fri, 11 Aug 2023 15:33:04 +0800
Subject: [PATCH 025/363] connection_managers renamed to connection_manager
---
src/http.rs | 4 ----
src/lib.rs | 2 +-
src/socks.rs | 8 -------
src/tun2proxy.rs | 62 +++++++++++++++---------------------------------
4 files changed, 20 insertions(+), 56 deletions(-)
diff --git a/src/http.rs b/src/http.rs
index 05f72d4..880e3da 100644
--- a/src/http.rs
+++ b/src/http.rs
@@ -391,10 +391,6 @@ pub(crate) struct HttpManager {
}
impl ConnectionManager for HttpManager {
- fn handles_connection(&self, info: &ConnectionInfo) -> bool {
- info.protocol == IpProtocol::Tcp
- }
-
fn new_tcp_proxy(&self, info: &ConnectionInfo, _: bool) -> Result, Error> {
if info.protocol != IpProtocol::Tcp {
return Err("Invalid protocol".into());
diff --git a/src/lib.rs b/src/lib.rs
index 01ea7ba..bdbf7a0 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -130,7 +130,7 @@ pub fn tun_to_proxy<'a>(
ProxyType::Socks5 => Rc::new(SocksProxyManager::new(server, Version::V5, credentials)) as Rc,
ProxyType::Http => Rc::new(HttpManager::new(server, credentials)) as Rc,
};
- ttp.add_connection_manager(mgr);
+ ttp.set_connection_manager(Some(mgr));
Ok(ttp)
}
diff --git a/src/socks.rs b/src/socks.rs
index 02b893d..7847025 100644
--- a/src/socks.rs
+++ b/src/socks.rs
@@ -5,7 +5,6 @@ use crate::{
OutgoingDirection, TcpProxy,
},
};
-use smoltcp::wire::IpProtocol;
use socks5_impl::protocol::{self, handshake, password_method, Address, AuthMethod, StreamOperation, UserKey, Version};
use std::{collections::VecDeque, convert::TryFrom, net::SocketAddr};
@@ -336,14 +335,7 @@ pub(crate) struct SocksProxyManager {
}
impl ConnectionManager for SocksProxyManager {
- fn handles_connection(&self, info: &ConnectionInfo) -> bool {
- info.protocol == IpProtocol::Tcp
- }
-
fn new_tcp_proxy(&self, info: &ConnectionInfo, udp_associate: bool) -> Result> {
- if info.protocol != IpProtocol::Tcp {
- return Err("Invalid protocol".into());
- }
use socks5_impl::protocol::Command::{Connect, UdpAssociate};
let command = if udp_associate { UdpAssociate } else { Connect };
Ok(Box::new(SocksProxyImpl::new(
diff --git a/src/tun2proxy.rs b/src/tun2proxy.rs
index 4bdf1a8..83b57c3 100644
--- a/src/tun2proxy.rs
+++ b/src/tun2proxy.rs
@@ -192,7 +192,6 @@ pub(crate) trait UdpProxy {
}
pub(crate) trait ConnectionManager {
- fn handles_connection(&self, info: &ConnectionInfo) -> bool;
fn new_tcp_proxy(&self, info: &ConnectionInfo, udp_associate: bool) -> Result>;
fn close_connection(&self, info: &ConnectionInfo);
fn get_server_addr(&self) -> SocketAddr;
@@ -207,7 +206,7 @@ pub struct TunToProxy<'a> {
poll: Poll,
iface: Interface,
connection_map: HashMap,
- connection_managers: Vec>,
+ connection_manager: Option>,
next_token: usize,
sockets: SocketSet<'a>,
device: VirtualTunDevice,
@@ -255,7 +254,7 @@ impl<'a> TunToProxy<'a> {
iface,
connection_map: HashMap::default(),
next_token: usize::from(EXIT_TOKEN) + 1,
- connection_managers: Vec::default(),
+ connection_manager: None,
sockets: SocketSet::new([]),
device,
options,
@@ -272,8 +271,8 @@ impl<'a> TunToProxy<'a> {
token
}
- pub(crate) fn add_connection_manager(&mut self, manager: Rc) {
- self.connection_managers.push(manager);
+ pub(crate) fn set_connection_manager(&mut self, manager: Option>) {
+ self.connection_manager = manager;
}
/// Read data from virtual device (remote server) and inject it into tun interface.
@@ -320,13 +319,8 @@ impl<'a> TunToProxy<'a> {
Ok(())
}
- fn get_connection_manager(&self, info: &ConnectionInfo) -> Option> {
- for manager in self.connection_managers.iter() {
- if manager.handles_connection(info) {
- return Some(manager.clone());
- }
- }
- None
+ fn get_connection_manager(&self) -> Option> {
+ self.connection_manager.clone()
}
/// Scan connection state machine and check if any connection should be closed.
@@ -451,29 +445,17 @@ impl<'a> TunToProxy<'a> {
}
}
};
- if connection_info.protocol == IpProtocol::Tcp {
- let server_addr = self
- .get_connection_manager(&connection_info)
- .ok_or("get_connection_manager")?
- .get_server_addr();
- if first_packet {
- let mut done = false;
- for manager in self.connection_managers.iter_mut() {
- let tcp_proxy_handler = manager.new_tcp_proxy(&connection_info, false);
- if tcp_proxy_handler.is_err() {
- continue;
- }
- let tcp_proxy_handler = tcp_proxy_handler?;
- let state = self.create_new_tcp_connection_state(server_addr, dst, tcp_proxy_handler)?;
- self.connection_map.insert(connection_info.clone(), state);
- log::info!("Connect done {} ({})", connection_info, dst);
- done = true;
- break;
- }
- if !done {
- log::debug!("No connection manager for {} ({})", connection_info, dst);
- }
+ let manager = self.get_connection_manager().ok_or("get connection manager")?;
+ let server_addr = manager.get_server_addr();
+
+ if connection_info.protocol == IpProtocol::Tcp {
+ if first_packet {
+ let tcp_proxy_handler = manager.new_tcp_proxy(&connection_info, false)?;
+ let state = self.create_new_tcp_connection_state(server_addr, dst, tcp_proxy_handler)?;
+ self.connection_map.insert(connection_info.clone(), state);
+
+ log::info!("Connect done {} ({})", connection_info, dst);
} else if !self.connection_map.contains_key(&connection_info) {
log::debug!("Not found {} ({})", connection_info, dst);
return Ok(());
@@ -514,12 +496,6 @@ impl<'a> TunToProxy<'a> {
}
} else {
// Another UDP packet
- let manager = self.get_connection_manager(&connection_info);
- if manager.is_none() {
- return Ok(());
- }
- let manager = manager.ok_or("")?;
- let server_addr = manager.get_server_addr();
let tcp_proxy_handler = manager.new_tcp_proxy(&connection_info, true)?;
let state = self.create_new_tcp_connection_state(server_addr, dst, tcp_proxy_handler)?;
self.connection_map.insert(connection_info.clone(), state);
@@ -664,19 +640,19 @@ impl<'a> TunToProxy<'a> {
}
fn mio_socket_event(&mut self, event: &Event) -> Result<(), Error> {
- let e = "connection not found";
let conn_info = match self.find_info_by_token(event.token()) {
Some(conn_info) => conn_info.clone(),
None => {
// We may have closed the connection in an earlier iteration over the poll events,
// e.g. because an event through the tunnel interface indicated that the connection
// should be closed.
- log::trace!("{e}");
+ log::trace!("Connection info not found");
return Ok(());
}
};
- let server = self.get_connection_manager(&conn_info).ok_or(e)?.get_server_addr();
+ let e = "connection manager not found";
+ let server = self.get_connection_manager().ok_or(e)?.get_server_addr();
let mut block = || -> Result<(), Error> {
if event.is_readable() || event.is_read_closed() {
From da87fa8d5a606649f467d05635cff1497e2985e8 Mon Sep 17 00:00:00 2001
From: ssrlive <30760636+ssrlive@users.noreply.github.com>
Date: Fri, 11 Aug 2023 19:18:18 +0800
Subject: [PATCH 026/363] UDP associate
---
src/http.rs | 4 ++++
src/socks.rs | 8 +++++++-
src/tun2proxy.rs | 28 +++++++++++++++++++++++-----
3 files changed, 34 insertions(+), 6 deletions(-)
diff --git a/src/http.rs b/src/http.rs
index 880e3da..cdbeca2 100644
--- a/src/http.rs
+++ b/src/http.rs
@@ -382,6 +382,10 @@ impl TcpProxy for HttpConnection {
fn reset_connection(&self) -> bool {
self.state == HttpState::Reset
}
+
+ fn get_udp_associate(&self) -> Option {
+ None
+ }
}
pub(crate) struct HttpManager {
diff --git a/src/socks.rs b/src/socks.rs
index 7847025..a1af57c 100644
--- a/src/socks.rs
+++ b/src/socks.rs
@@ -224,7 +224,9 @@ impl SocksProxyImpl {
return Err(format!("SOCKS connection failed: {}", response.reply).into());
}
if self.command == protocol::Command::UdpAssociate {
- self.udp_associate = Some(SocketAddr::try_from(response.address)?);
+ self.udp_associate = Some(SocketAddr::try_from(&response.address)?);
+ assert!(self.data_buf.is_empty());
+ log::debug!("UDP associate: {}", response.address);
}
self.server_outbuf.append(&mut self.data_buf);
@@ -326,6 +328,10 @@ impl TcpProxy for SocksProxyImpl {
fn reset_connection(&self) -> bool {
false
}
+
+ fn get_udp_associate(&self) -> Option {
+ self.udp_associate
+ }
}
pub(crate) struct SocksProxyManager {
diff --git a/src/tun2proxy.rs b/src/tun2proxy.rs
index 83b57c3..687392c 100644
--- a/src/tun2proxy.rs
+++ b/src/tun2proxy.rs
@@ -7,7 +7,7 @@ use smoltcp::{
time::Instant,
wire::{IpCidr, IpProtocol, Ipv4Packet, Ipv6Packet, TcpPacket, UdpPacket, UDP_HEADER_LEN},
};
-use socks5_impl::protocol::{Address, UserKey};
+use socks5_impl::protocol::{Address, StreamOperation, UdpHeader, UserKey};
use std::{
collections::{HashMap, HashSet},
convert::{From, TryFrom},
@@ -184,6 +184,7 @@ pub(crate) trait TcpProxy {
fn connection_established(&self) -> bool;
fn have_data(&mut self, dir: Direction) -> bool;
fn reset_connection(&self) -> bool;
+ fn get_udp_associate(&self) -> Option;
}
pub(crate) trait UdpProxy {
@@ -496,11 +497,28 @@ impl<'a> TunToProxy<'a> {
}
} else {
// Another UDP packet
- let tcp_proxy_handler = manager.new_tcp_proxy(&connection_info, true)?;
- let state = self.create_new_tcp_connection_state(server_addr, dst, tcp_proxy_handler)?;
- self.connection_map.insert(connection_info.clone(), state);
+ if !self.connection_map.contains_key(&connection_info) {
+ log::trace!("New UDP connection {} ({})", connection_info, dst);
+ let tcp_proxy_handler = manager.new_tcp_proxy(&connection_info, true)?;
+ let state = self.create_new_tcp_connection_state(server_addr, dst, tcp_proxy_handler)?;
+ self.connection_map.insert(connection_info.clone(), state);
+ }
- // TODO: Handle UDP packets
+ self.expect_smoltcp_send()?;
+ self.tunsocket_read_and_forward(&connection_info)?;
+ self.write_to_server(&connection_info)?;
+
+ let mut s5_udp_data = Vec::::new();
+ UdpHeader::new(0, connection_info.dst.clone()).write_to_stream(&mut s5_udp_data)?;
+ s5_udp_data.extend_from_slice(payload);
+
+ let state = self.connection_map.get(&connection_info).ok_or("udp associate state")?;
+ if let Some(udp_associate) = state.tcp_proxy_handler.get_udp_associate() {
+ log::debug!("UDP associate address: {}", udp_associate);
+ // Send packets via UDP associate...
+ } else {
+ // UDP associate tunnel not ready yet, we must cache the packet...
+ }
}
} else {
log::warn!("Unsupported protocol: {} ({})", connection_info, dst);
From 30420059cc737884a4aa34b9a7bd1fa05ba5a698 Mon Sep 17 00:00:00 2001
From: ssrlive <30760636+ssrlive@users.noreply.github.com>
Date: Fri, 11 Aug 2023 22:54:20 +0800
Subject: [PATCH 027/363] send_udp_packet
---
src/socks.rs | 16 ++++++----------
src/tun2proxy.rs | 25 ++++++++++++++-----------
2 files changed, 20 insertions(+), 21 deletions(-)
diff --git a/src/socks.rs b/src/socks.rs
index a1af57c..3e4a7a9 100644
--- a/src/socks.rs
+++ b/src/socks.rs
@@ -158,11 +158,11 @@ impl SocksProxyImpl {
return Err("SOCKS5 server requires an unsupported authentication method.".into());
}
- if auth_method == AuthMethod::UserPass {
- self.state = SocksState::SendAuthData;
+ self.state = if auth_method == AuthMethod::UserPass {
+ SocksState::SendAuthData
} else {
- self.state = SocksState::SendRequest;
- }
+ SocksState::SendRequest
+ };
self.state_change()
}
@@ -344,12 +344,8 @@ impl ConnectionManager for SocksProxyManager {
fn new_tcp_proxy(&self, info: &ConnectionInfo, udp_associate: bool) -> Result> {
use socks5_impl::protocol::Command::{Connect, UdpAssociate};
let command = if udp_associate { UdpAssociate } else { Connect };
- Ok(Box::new(SocksProxyImpl::new(
- info,
- self.credentials.clone(),
- self.version,
- command,
- )?))
+ let credentials = self.credentials.clone();
+ Ok(Box::new(SocksProxyImpl::new(info, credentials, self.version, command)?))
}
fn close_connection(&self, _: &ConnectionInfo) {}
diff --git a/src/tun2proxy.rs b/src/tun2proxy.rs
index 687392c..22e538f 100644
--- a/src/tun2proxy.rs
+++ b/src/tun2proxy.rs
@@ -484,17 +484,7 @@ impl<'a> TunToProxy<'a> {
let payload = &frame[payload_offset..payload_offset + payload_size];
if let (Some(virtual_dns), true) = (&mut self.options.virtual_dns, port == 53) {
let response = virtual_dns.receive_query(payload)?;
- {
- let rx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 4096]);
- let tx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 4096]);
- let mut socket = udp::Socket::new(rx_buffer, tx_buffer);
- socket.bind(dst)?;
- let meta = UdpMetadata::from(connection_info.src);
- socket.send_slice(response.as_slice(), meta)?;
- let handle = self.sockets.add(socket);
- self.expect_smoltcp_send()?;
- self.sockets.remove(handle);
- }
+ self.send_udp_packet(dst, connection_info.src, response.as_slice())?;
} else {
// Another UDP packet
if !self.connection_map.contains_key(&connection_info) {
@@ -516,6 +506,7 @@ impl<'a> TunToProxy<'a> {
if let Some(udp_associate) = state.tcp_proxy_handler.get_udp_associate() {
log::debug!("UDP associate address: {}", udp_associate);
// Send packets via UDP associate...
+ // self.send_udp_packet(connection_info.src, udp_associate, &s5_udp_data)?;
} else {
// UDP associate tunnel not ready yet, we must cache the packet...
}
@@ -562,6 +553,18 @@ impl<'a> TunToProxy<'a> {
Ok(state)
}
+ fn send_udp_packet(&mut self, src: SocketAddr, dst: SocketAddr, data: &[u8]) -> Result<()> {
+ let rx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 4096]);
+ let tx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 4096]);
+ let mut socket = udp::Socket::new(rx_buffer, tx_buffer);
+ socket.bind(src)?;
+ socket.send_slice(data, UdpMetadata::from(dst))?;
+ let handle = self.sockets.add(socket);
+ self.expect_smoltcp_send()?;
+ self.sockets.remove(handle);
+ Ok(())
+ }
+
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);
From 119c9fef994423e63b3c86b199f80fe8d9cb950e Mon Sep 17 00:00:00 2001
From: ssrlive <30760636+ssrlive@users.noreply.github.com>
Date: Wed, 16 Aug 2023 12:18:42 +0800
Subject: [PATCH 028/363] deal with Unsupported protocol
---
src/tun2proxy.rs | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/src/tun2proxy.rs b/src/tun2proxy.rs
index 22e538f..ea1078d 100644
--- a/src/tun2proxy.rs
+++ b/src/tun2proxy.rs
@@ -433,7 +433,12 @@ impl<'a> TunToProxy<'a> {
// A raw packet was received on the tunnel interface.
fn receive_tun(&mut self, frame: &mut [u8]) -> Result<(), Error> {
let mut handler = || -> Result<(), Error> {
- let (info, first_packet, payload_offset, payload_size) = connection_tuple(frame)?;
+ let result = connection_tuple(frame);
+ if let Err(error) = result {
+ log::info!("{}, ignored", error);
+ return Ok(());
+ }
+ let (info, _first_packet, payload_offset, payload_size) = result?;
let dst = SocketAddr::try_from(&info.dst)?;
let connection_info = match &mut self.options.virtual_dns {
None => info.clone(),
@@ -451,7 +456,7 @@ impl<'a> TunToProxy<'a> {
let server_addr = manager.get_server_addr();
if connection_info.protocol == IpProtocol::Tcp {
- if first_packet {
+ if _first_packet {
let tcp_proxy_handler = manager.new_tcp_proxy(&connection_info, false)?;
let state = self.create_new_tcp_connection_state(server_addr, dst, tcp_proxy_handler)?;
self.connection_map.insert(connection_info.clone(), state);
From 334514cfc1cce511f5557c1037cdf6d5fa5b283c Mon Sep 17 00:00:00 2001
From: ssrlive <30760636+ssrlive@users.noreply.github.com>
Date: Fri, 18 Aug 2023 09:40:16 +0800
Subject: [PATCH 029/363] clearup_expired_udp_associate
---
src/tun2proxy.rs | 47 ++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 44 insertions(+), 3 deletions(-)
diff --git a/src/tun2proxy.rs b/src/tun2proxy.rs
index ea1078d..bf16cb9 100644
--- a/src/tun2proxy.rs
+++ b/src/tun2proxy.rs
@@ -166,6 +166,8 @@ fn connection_tuple(frame: &[u8]) -> Result<(ConnectionInfo, bool, usize, usize)
const SERVER_WRITE_CLOSED: u8 = 1;
const CLIENT_WRITE_CLOSED: u8 = 2;
+const UDP_ASSO_TIMEOUT: u64 = 5; // seconds
+
struct TcpConnectState {
smoltcp_handle: Option,
mio_stream: TcpStream,
@@ -174,6 +176,7 @@ struct TcpConnectState {
close_state: u8,
wait_read: bool,
wait_write: bool,
+ expiry: Option<::std::time::Instant>,
}
pub(crate) trait TcpProxy {
@@ -458,7 +461,7 @@ impl<'a> TunToProxy<'a> {
if connection_info.protocol == IpProtocol::Tcp {
if _first_packet {
let tcp_proxy_handler = manager.new_tcp_proxy(&connection_info, false)?;
- let state = self.create_new_tcp_connection_state(server_addr, dst, tcp_proxy_handler)?;
+ let state = self.create_new_tcp_connection_state(server_addr, dst, tcp_proxy_handler, false)?;
self.connection_map.insert(connection_info.clone(), state);
log::info!("Connect done {} ({})", connection_info, dst);
@@ -495,7 +498,7 @@ impl<'a> TunToProxy<'a> {
if !self.connection_map.contains_key(&connection_info) {
log::trace!("New UDP connection {} ({})", connection_info, dst);
let tcp_proxy_handler = manager.new_tcp_proxy(&connection_info, true)?;
- let state = self.create_new_tcp_connection_state(server_addr, dst, tcp_proxy_handler)?;
+ let state = self.create_new_tcp_connection_state(server_addr, dst, tcp_proxy_handler, true)?;
self.connection_map.insert(connection_info.clone(), state);
}
@@ -507,7 +510,13 @@ impl<'a> TunToProxy<'a> {
UdpHeader::new(0, connection_info.dst.clone()).write_to_stream(&mut s5_udp_data)?;
s5_udp_data.extend_from_slice(payload);
- let state = self.connection_map.get(&connection_info).ok_or("udp associate state")?;
+ let state = self
+ .connection_map
+ .get_mut(&connection_info)
+ .ok_or("udp associate state")?;
+ assert!(state.expiry.is_some());
+ state.expiry = Some(Self::udp_associate_timeout());
+
if let Some(udp_associate) = state.tcp_proxy_handler.get_udp_associate() {
log::debug!("UDP associate address: {}", udp_associate);
// Send packets via UDP associate...
@@ -532,6 +541,7 @@ impl<'a> TunToProxy<'a> {
server_addr: SocketAddr,
dst: SocketAddr,
tcp_proxy_handler: Box,
+ udp_associate: bool,
) -> Result {
let mut socket = tcp::Socket::new(
tcp::SocketBuffer::new(vec![0; 1024 * 128]),
@@ -546,6 +556,11 @@ impl<'a> TunToProxy<'a> {
let i = Interest::READABLE;
self.poll.registry().register(&mut client, token, i)?;
+ let expiry = if udp_associate {
+ Some(Self::udp_associate_timeout())
+ } else {
+ None
+ };
let state = TcpConnectState {
smoltcp_handle: Some(handle),
mio_stream: client,
@@ -554,10 +569,35 @@ impl<'a> TunToProxy<'a> {
close_state: 0,
wait_read: true,
wait_write: false,
+ expiry,
};
Ok(state)
}
+ fn udp_associate_timeout() -> ::std::time::Instant {
+ ::std::time::Instant::now() + ::std::time::Duration::from_secs(UDP_ASSO_TIMEOUT)
+ }
+
+ fn udp_associate_timeout_expired(&self, info: &ConnectionInfo) -> bool {
+ if let Some(state) = self.connection_map.get(info) {
+ if let Some(expiry) = state.expiry {
+ return expiry < ::std::time::Instant::now();
+ }
+ }
+ false
+ }
+
+ fn clearup_expired_udp_associate(&mut self) -> Result<()> {
+ let keys = self.connection_map.keys().map(|key| key.clone()).collect::>();
+ for key in keys {
+ if self.udp_associate_timeout_expired(&key) {
+ log::debug!("UDP associate timeout: {}", key);
+ self.remove_connection(&key)?;
+ }
+ }
+ Ok(())
+ }
+
fn send_udp_packet(&mut self, src: SocketAddr, dst: SocketAddr, data: &[u8]) -> Result<()> {
let rx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 4096]);
let tx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 4096]);
@@ -778,6 +818,7 @@ impl<'a> TunToProxy<'a> {
}
}
self.send_to_smoltcp()?;
+ self.clearup_expired_udp_associate()?;
}
}
From b019ace2e15a1164765510906258067c84c5cc97 Mon Sep 17 00:00:00 2001
From: ssrlive <30760636+ssrlive@users.noreply.github.com>
Date: Fri, 18 Aug 2023 09:43:18 +0800
Subject: [PATCH 030/363] minor changes
---
src/tun2proxy.rs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/tun2proxy.rs b/src/tun2proxy.rs
index bf16cb9..df0f87d 100644
--- a/src/tun2proxy.rs
+++ b/src/tun2proxy.rs
@@ -588,7 +588,7 @@ impl<'a> TunToProxy<'a> {
}
fn clearup_expired_udp_associate(&mut self) -> Result<()> {
- let keys = self.connection_map.keys().map(|key| key.clone()).collect::>();
+ let keys = self.connection_map.keys().cloned().collect::>();
for key in keys {
if self.udp_associate_timeout_expired(&key) {
log::debug!("UDP associate timeout: {}", key);
From 01157915b3895c4b45a2fb6fdea342c1faf71479 Mon Sep 17 00:00:00 2001
From: ssrlive <30760636+ssrlive@users.noreply.github.com>
Date: Sat, 19 Aug 2023 21:33:43 +0800
Subject: [PATCH 031/363] UDP proxy completed
---
src/socks.rs | 6 +--
src/tun2proxy.rs | 138 ++++++++++++++++++++++++++++++++++++-----------
2 files changed, 111 insertions(+), 33 deletions(-)
diff --git a/src/socks.rs b/src/socks.rs
index 3e4a7a9..4dbc091 100644
--- a/src/socks.rs
+++ b/src/socks.rs
@@ -142,7 +142,7 @@ impl SocksProxyImpl {
let response = handshake::Response::retrieve_from_stream(&mut self.server_inbuf.clone());
if let Err(e) = &response {
if e.kind() == std::io::ErrorKind::UnexpectedEof {
- log::trace!("receive_server_hello_socks5 needs more data \"{}\"...", e);
+ // log::trace!("receive_server_hello_socks5 needs more data \"{}\"...", e);
return Ok(());
} else {
return Err(e.to_string().into());
@@ -212,7 +212,7 @@ impl SocksProxyImpl {
let response = protocol::Response::retrieve_from_stream(&mut self.server_inbuf.clone());
if let Err(e) = &response {
if e.kind() == std::io::ErrorKind::UnexpectedEof {
- log::trace!("receive_connection_status needs more data \"{}\"...", e);
+ // log::trace!("receive_connection_status needs more data \"{}\"...", e);
return Ok(());
} else {
return Err(e.to_string().into());
@@ -226,7 +226,7 @@ impl SocksProxyImpl {
if self.command == protocol::Command::UdpAssociate {
self.udp_associate = Some(SocketAddr::try_from(&response.address)?);
assert!(self.data_buf.is_empty());
- log::debug!("UDP associate: {}", response.address);
+ // log::debug!("UDP associate: {}", response.address);
}
self.server_outbuf.append(&mut self.data_buf);
diff --git a/src/tun2proxy.rs b/src/tun2proxy.rs
index df0f87d..a537a3b 100644
--- a/src/tun2proxy.rs
+++ b/src/tun2proxy.rs
@@ -1,5 +1,5 @@
use crate::{error::Error, error::Result, virtdevice::VirtualTunDevice, NetworkInterface, Options};
-use mio::{event::Event, net::TcpStream, unix::SourceFd, Events, Interest, Poll, Token};
+use mio::{event::Event, net::TcpStream, net::UdpSocket, unix::SourceFd, Events, Interest, Poll, Token};
use smoltcp::{
iface::{Config, Interface, SocketHandle, SocketSet},
phy::{Device, Medium, RxToken, TunTapInterface, TxToken},
@@ -166,7 +166,7 @@ fn connection_tuple(frame: &[u8]) -> Result<(ConnectionInfo, bool, usize, usize)
const SERVER_WRITE_CLOSED: u8 = 1;
const CLIENT_WRITE_CLOSED: u8 = 2;
-const UDP_ASSO_TIMEOUT: u64 = 5; // seconds
+const UDP_ASSO_TIMEOUT: u64 = 10; // seconds
struct TcpConnectState {
smoltcp_handle: Option,
@@ -177,6 +177,10 @@ struct TcpConnectState {
wait_read: bool,
wait_write: bool,
expiry: Option<::std::time::Instant>,
+ udp_socket: Option,
+ udp_token: Option,
+ udp_origin_dst: Option,
+ udp_data_cache: Option>,
}
pub(crate) trait TcpProxy {
@@ -303,6 +307,17 @@ impl<'a> TunToProxy<'a> {
.find_map(|(info, state)| if state.token == token { Some(info) } else { None })
}
+ fn find_info_by_udp_token(&self, token: Token) -> Option<&ConnectionInfo> {
+ self.connection_map.iter().find_map(|(info, state)| {
+ if let Some(udp_token) = state.udp_token {
+ if udp_token == token {
+ return Some(info);
+ }
+ }
+ None
+ })
+ }
+
/// Destroy connection state machine
fn remove_connection(&mut self, info: &ConnectionInfo) -> Result<(), Error> {
if let Some(mut state) = self.connection_map.remove(info) {
@@ -316,7 +331,16 @@ impl<'a> TunToProxy<'a> {
// FIXME: Does this line should be moved up to the beginning of this function?
self.expect_smoltcp_send()?;
- _ = self.poll.registry().deregister(&mut state.mio_stream);
+ if let Err(e) = self.poll.registry().deregister(&mut state.mio_stream) {
+ // FIXME: The function `deregister` will frequently fail for unknown reasons.
+ log::debug!("{}", e);
+ }
+
+ if let Some(mut udp_socket) = state.udp_socket {
+ if let Err(e) = self.poll.registry().deregister(&mut udp_socket) {
+ log::debug!("{}", e);
+ }
+ }
log::info!("Close {}", info);
}
@@ -442,14 +466,14 @@ impl<'a> TunToProxy<'a> {
return Ok(());
}
let (info, _first_packet, payload_offset, payload_size) = result?;
- let dst = SocketAddr::try_from(&info.dst)?;
+ let origin_dst = SocketAddr::try_from(&info.dst)?;
let connection_info = match &mut self.options.virtual_dns {
- None => info.clone(),
+ None => info,
Some(virtual_dns) => {
- let dst_ip = dst.ip();
+ let dst_ip = origin_dst.ip();
virtual_dns.touch_ip(&dst_ip);
match virtual_dns.resolve_ip(&dst_ip) {
- None => info.clone(),
+ None => info,
Some(name) => info.to_named(name.clone()),
}
}
@@ -461,15 +485,16 @@ impl<'a> TunToProxy<'a> {
if connection_info.protocol == IpProtocol::Tcp {
if _first_packet {
let tcp_proxy_handler = manager.new_tcp_proxy(&connection_info, false)?;
- let state = self.create_new_tcp_connection_state(server_addr, dst, tcp_proxy_handler, false)?;
+ #[rustfmt::skip]
+ let state = self.create_new_tcp_connection_state(server_addr, origin_dst, tcp_proxy_handler, false)?;
self.connection_map.insert(connection_info.clone(), state);
- log::info!("Connect done {} ({})", connection_info, dst);
+ log::info!("Connect done {} ({})", connection_info, origin_dst);
} else if !self.connection_map.contains_key(&connection_info) {
- log::debug!("Not found {} ({})", connection_info, dst);
+ // log::debug!("Drop middle session {} ({})", connection_info, origin_dst);
return Ok(());
} else {
- log::trace!("Subsequent packet {} ({})", connection_info, dst);
+ // log::trace!("Subsequent packet {} ({})", connection_info, origin_dst);
}
// Inject the packet to advance the remote proxy server smoltcp socket state
@@ -487,46 +512,51 @@ impl<'a> TunToProxy<'a> {
// Therefore, we now expect it to write data to the server.
self.write_to_server(&connection_info)?;
} else if connection_info.protocol == IpProtocol::Udp {
- log::trace!("{} ({})", connection_info, dst);
let port = connection_info.dst.port();
let payload = &frame[payload_offset..payload_offset + payload_size];
if let (Some(virtual_dns), true) = (&mut self.options.virtual_dns, port == 53) {
+ log::info!("DNS query via virtual DNS {} ({})", connection_info, origin_dst);
let response = virtual_dns.receive_query(payload)?;
- self.send_udp_packet(dst, connection_info.src, response.as_slice())?;
+ self.send_udp_packet_to_client(origin_dst, connection_info.src, response.as_slice())?;
} else {
// Another UDP packet
if !self.connection_map.contains_key(&connection_info) {
- log::trace!("New UDP connection {} ({})", connection_info, dst);
+ log::info!("UDP associate session {} ({})", connection_info, origin_dst);
let tcp_proxy_handler = manager.new_tcp_proxy(&connection_info, true)?;
- let state = self.create_new_tcp_connection_state(server_addr, dst, tcp_proxy_handler, true)?;
+ #[rustfmt::skip]
+ let mut state = self.create_new_tcp_connection_state(server_addr, origin_dst, tcp_proxy_handler, true)?;
+ state.udp_origin_dst = Some(origin_dst);
self.connection_map.insert(connection_info.clone(), state);
+
+ self.expect_smoltcp_send()?;
+ self.tunsocket_read_and_forward(&connection_info)?;
+ self.write_to_server(&connection_info)?;
+ } else {
+ // log::trace!("Subsequent udp packet {} ({})", connection_info, origin_dst);
}
- self.expect_smoltcp_send()?;
- self.tunsocket_read_and_forward(&connection_info)?;
- self.write_to_server(&connection_info)?;
+ let err = "udp associate state not find";
+ let state = self.connection_map.get_mut(&connection_info).ok_or(err)?;
+ assert!(state.expiry.is_some());
+ state.expiry = Some(Self::udp_associate_timeout());
+ // Add SOCKS5 UDP header to the incoming data
let mut s5_udp_data = Vec::::new();
UdpHeader::new(0, connection_info.dst.clone()).write_to_stream(&mut s5_udp_data)?;
s5_udp_data.extend_from_slice(payload);
- let state = self
- .connection_map
- .get_mut(&connection_info)
- .ok_or("udp associate state")?;
- assert!(state.expiry.is_some());
- state.expiry = Some(Self::udp_associate_timeout());
-
if let Some(udp_associate) = state.tcp_proxy_handler.get_udp_associate() {
- log::debug!("UDP associate address: {}", udp_associate);
- // Send packets via UDP associate...
- // self.send_udp_packet(connection_info.src, udp_associate, &s5_udp_data)?;
+ // 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)?;
+ }
} else {
// UDP associate tunnel not ready yet, we must cache the packet...
+ state.udp_data_cache = Some(s5_udp_data);
}
}
} else {
- log::warn!("Unsupported protocol: {} ({})", connection_info, dst);
+ log::warn!("Unsupported protocol: {} ({})", connection_info, origin_dst);
}
Ok::<(), Error>(())
};
@@ -561,6 +591,16 @@ impl<'a> TunToProxy<'a> {
} else {
None
};
+
+ let (udp_socket, udp_token) = if udp_associate {
+ let addr = (Ipv4Addr::UNSPECIFIED, 0).into();
+ let mut socket = UdpSocket::bind(addr)?;
+ let token = self.new_token();
+ self.poll.registry().register(&mut socket, token, Interest::READABLE)?;
+ (Some(socket), Some(token))
+ } else {
+ (None, None)
+ };
let state = TcpConnectState {
smoltcp_handle: Some(handle),
mio_stream: client,
@@ -570,6 +610,10 @@ impl<'a> TunToProxy<'a> {
wait_read: true,
wait_write: false,
expiry,
+ udp_socket,
+ udp_token,
+ udp_origin_dst: None,
+ udp_data_cache: None,
};
Ok(state)
}
@@ -598,7 +642,7 @@ impl<'a> TunToProxy<'a> {
Ok(())
}
- fn send_udp_packet(&mut self, src: SocketAddr, dst: SocketAddr, data: &[u8]) -> Result<()> {
+ fn send_udp_packet_to_client(&mut self, src: SocketAddr, dst: SocketAddr, data: &[u8]) -> Result<()> {
let rx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 4096]);
let tx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 4096]);
let mut socket = udp::Socket::new(rx_buffer, tx_buffer);
@@ -706,6 +750,27 @@ impl<'a> TunToProxy<'a> {
}
fn mio_socket_event(&mut self, event: &Event) -> Result<(), Error> {
+ if let Some(info) = self.find_info_by_udp_token(event.token()) {
+ let info = info.clone();
+ let err = "udp connection state not found";
+ let state = self.connection_map.get_mut(&info).ok_or(err)?;
+ state.expiry = Some(Self::udp_associate_timeout());
+ if let Some(udp_socket) = state.udp_socket.as_ref() {
+ let mut buf = [0; 1 << 16];
+ // Receive UDP packet from remote SOCKS5 server
+ let (packet_size, _svr_addr) = udp_socket.recv_from(&mut buf)?;
+
+ let buf = buf[..packet_size].to_vec();
+ let header = UdpHeader::retrieve_from_stream(&mut &buf[..])?;
+
+ // Write to client
+ let src = state.udp_origin_dst.ok_or("udp address")?;
+ self.send_udp_packet_to_client(src, info.src, &buf[header.len()..])?;
+ }
+
+ return Ok(());
+ }
+
let conn_info = match self.find_info_by_token(event.token()) {
Some(conn_info) => conn_info.clone(),
None => {
@@ -723,6 +788,7 @@ impl<'a> TunToProxy<'a> {
let mut block = || -> Result<(), Error> {
if event.is_readable() || event.is_read_closed() {
{
+ let e = "connection state not found";
let state = self.connection_map.get_mut(&conn_info).ok_or(e)?;
// TODO: Move this reading process to its own function.
@@ -783,6 +849,18 @@ impl<'a> TunToProxy<'a> {
// The connection handler could have produced data that is to be written to the
// server.
self.write_to_server(&conn_info)?;
+
+ // Try to send the first UDP packet to remote SOCKS5 server for UDP associate session
+ if let Some(state) = self.connection_map.get_mut(&conn_info) {
+ if let Some(udp_socket) = state.udp_socket.as_ref() {
+ if let Some(addr) = state.tcp_proxy_handler.get_udp_associate() {
+ // Take ownership of udp_data_cache
+ if let Some(buf) = state.udp_data_cache.take() {
+ udp_socket.send_to(&buf, addr)?;
+ }
+ }
+ }
+ }
}
if event.is_writable() {
From 60b9683facd10a6f7309420252b23d7a600b8bd0 Mon Sep 17 00:00:00 2001
From: ssrlive <30760636+ssrlive@users.noreply.github.com>
Date: Sun, 20 Aug 2023 12:13:28 +0800
Subject: [PATCH 032/363] dns query from remote server
---
src/dns.rs | 19 ++++++++++++++++++-
src/tun2proxy.rs | 14 +++++++++++---
2 files changed, 29 insertions(+), 4 deletions(-)
diff --git a/src/dns.rs b/src/dns.rs
index 2ae1528..8aefd03 100644
--- a/src/dns.rs
+++ b/src/dns.rs
@@ -1,6 +1,9 @@
#![allow(dead_code)]
-use std::{net::IpAddr, str::FromStr};
+use std::{
+ net::{IpAddr, Ipv4Addr, SocketAddr},
+ str::FromStr,
+};
use trust_dns_proto::{
op::{Message, ResponseCode},
rr::{record_type::RecordType, Name, RData, Record},
@@ -90,3 +93,17 @@ pub fn parse_data_to_dns_message(data: &[u8], used_by_tcp: bool) -> Result bool {
+ fn is_benchmarking(addr: &Ipv4Addr) -> bool {
+ addr.octets()[0] == 198 && (addr.octets()[1] & 0xfe) == 18
+ }
+ fn addr_v4_is_private(addr: &Ipv4Addr) -> bool {
+ is_benchmarking(addr) || addr.is_private() || addr.is_loopback() || addr.is_link_local()
+ }
+ match addr {
+ SocketAddr::V4(addr) => addr_v4_is_private(addr.ip()),
+ SocketAddr::V6(_) => false,
+ }
+}
diff --git a/src/tun2proxy.rs b/src/tun2proxy.rs
index a537a3b..f4c0854 100644
--- a/src/tun2proxy.rs
+++ b/src/tun2proxy.rs
@@ -1,4 +1,4 @@
-use crate::{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};
use smoltcp::{
iface::{Config, Interface, SocketHandle, SocketSet},
@@ -468,7 +468,15 @@ impl<'a> TunToProxy<'a> {
let (info, _first_packet, payload_offset, payload_size) = result?;
let origin_dst = SocketAddr::try_from(&info.dst)?;
let connection_info = match &mut self.options.virtual_dns {
- None => info,
+ None => {
+ let mut info = info;
+ let port = origin_dst.port();
+ if port == 53 && info.protocol == IpProtocol::Udp && dns::addr_is_private(&origin_dst) {
+ let dns_addr: SocketAddr = "8.8.8.8:53".parse()?; // TODO: Configurable
+ info.dst = Address::from(dns_addr);
+ }
+ info
+ }
Some(virtual_dns) => {
let dst_ip = origin_dst.ip();
virtual_dns.touch_ip(&dst_ip);
@@ -798,7 +806,7 @@ impl<'a> TunToProxy<'a> {
Ok(read_result) => read_result,
Err(error) => {
if error.kind() != std::io::ErrorKind::WouldBlock {
- log::error!("Read from proxy: {}", error);
+ log::error!("{} Read from proxy: {}", conn_info.dst, error);
}
vecbuf.len()
}
From 6439cc7b4314f119f6633fcc3eb02d44b2878440 Mon Sep 17 00:00:00 2001
From: ssrlive <30760636+ssrlive@users.noreply.github.com>
Date: Sun, 20 Aug 2023 13:29:36 +0800
Subject: [PATCH 033/363] dns::remove_ipv6_entries
---
src/dns.rs | 6 ++++++
src/tun2proxy.rs | 10 +++++++++-
2 files changed, 15 insertions(+), 1 deletion(-)
diff --git a/src/dns.rs b/src/dns.rs
index 8aefd03..33be70c 100644
--- a/src/dns.rs
+++ b/src/dns.rs
@@ -50,6 +50,12 @@ pub fn build_dns_response(mut request: Message, domain: &str, ip: IpAddr, ttl: u
Ok(request)
}
+pub fn remove_ipv6_entries(message: &mut Message) {
+ message
+ .answers_mut()
+ .retain(|answer| !matches!(answer.data(), Some(RData::AAAA(_))));
+}
+
pub fn extract_ipaddr_from_dns_message(message: &Message) -> Result {
if message.response_code() != ResponseCode::NoError {
return Err(format!("{:?}", message.response_code()));
diff --git a/src/tun2proxy.rs b/src/tun2proxy.rs
index f4c0854..d68abdb 100644
--- a/src/tun2proxy.rs
+++ b/src/tun2proxy.rs
@@ -771,9 +771,17 @@ impl<'a> TunToProxy<'a> {
let buf = buf[..packet_size].to_vec();
let header = UdpHeader::retrieve_from_stream(&mut &buf[..])?;
+ let buf = if info.dst.port() == 53 {
+ let mut message = dns::parse_data_to_dns_message(&buf[header.len()..], false)?;
+ dns::remove_ipv6_entries(&mut message); // TODO: Configurable
+ message.to_vec()?
+ } else {
+ buf[header.len()..].to_vec()
+ };
+
// Write to client
let src = state.udp_origin_dst.ok_or("udp address")?;
- self.send_udp_packet_to_client(src, info.src, &buf[header.len()..])?;
+ self.send_udp_packet_to_client(src, info.src, &buf)?;
}
return Ok(());
From d5b76c18cc05bb14e481b383bcc48a60c58f042a Mon Sep 17 00:00:00 2001
From: "B. Blechschmidt"
Date: Sun, 20 Aug 2023 17:27:22 +0200
Subject: [PATCH 034/363] Fix UDP associate address
---
src/socks.rs | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/src/socks.rs b/src/socks.rs
index 4dbc091..9d61f47 100644
--- a/src/socks.rs
+++ b/src/socks.rs
@@ -203,7 +203,13 @@ impl SocksProxyImpl {
}
fn send_request_socks5(&mut self) -> Result<(), Error> {
- protocol::Request::new(self.command, self.info.dst.clone()).write_to_stream(&mut self.server_outbuf)?;
+ use socks5_impl::protocol::Command::UdpAssociate;
+ let addr = if self.command == UdpAssociate {
+ Address::unspecified()
+ } else {
+ self.info.dst.clone()
+ };
+ protocol::Request::new(self.command, addr).write_to_stream(&mut self.server_outbuf)?;
self.state = SocksState::ReceiveResponse;
self.state_change()
}
From 5301cf8d3787e14edff3454a63491a8f0ff49f4d Mon Sep 17 00:00:00 2001
From: "B. Blechschmidt"
Date: Sun, 20 Aug 2023 18:20:47 +0200
Subject: [PATCH 035/363] Add dual stack DNS lookup test
---
tests/manual-tests/test-ds-dns-lookup.py | 13 +++++++++++++
1 file changed, 13 insertions(+)
create mode 100644 tests/manual-tests/test-ds-dns-lookup.py
diff --git a/tests/manual-tests/test-ds-dns-lookup.py b/tests/manual-tests/test-ds-dns-lookup.py
new file mode 100644
index 0000000..1354be9
--- /dev/null
+++ b/tests/manual-tests/test-ds-dns-lookup.py
@@ -0,0 +1,13 @@
+import dns.message
+import socket
+
+s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+s.bind(('0.0.0.0', 0))
+
+s.sendto(dns.message.make_query('example.org', 'A').to_wire(), ('8.8.8.8', 53))
+s.sendto(dns.message.make_query('example.org', 'AAAA').to_wire(), ('8.8.8.8', 53))
+
+data, _ = s.recvfrom(0xffff)
+print(dns.message.from_wire(data))
+data, _ = s.recvfrom(0xffff)
+print(dns.message.from_wire(data))
From b0e275ec0805e8b6a3b99cbdcadd1bde36ccd069 Mon Sep 17 00:00:00 2001
From: "B. Blechschmidt"
Date: Sun, 20 Aug 2023 18:51:15 +0200
Subject: [PATCH 036/363] Use LinkedList as UDP packet cache
---
src/tun2proxy.rs | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/src/tun2proxy.rs b/src/tun2proxy.rs
index d68abdb..a289cdd 100644
--- a/src/tun2proxy.rs
+++ b/src/tun2proxy.rs
@@ -17,6 +17,7 @@ use std::{
rc::Rc,
str::FromStr,
};
+use std::collections::LinkedList;
#[derive(Hash, Clone, Eq, PartialEq, PartialOrd, Ord, Debug)]
pub(crate) struct ConnectionInfo {
@@ -180,7 +181,7 @@ struct TcpConnectState {
udp_socket: Option,
udp_token: Option,
udp_origin_dst: Option,
- udp_data_cache: Option>,
+ udp_data_cache: LinkedList>,
}
pub(crate) trait TcpProxy {
@@ -540,6 +541,7 @@ impl<'a> TunToProxy<'a> {
self.tunsocket_read_and_forward(&connection_info)?;
self.write_to_server(&connection_info)?;
} else {
+ log::info!("Subsequent udp packet {} ({})", connection_info, origin_dst);
// log::trace!("Subsequent udp packet {} ({})", connection_info, origin_dst);
}
@@ -559,8 +561,9 @@ impl<'a> TunToProxy<'a> {
socket.send_to(&s5_udp_data, udp_associate)?;
}
} else {
+ log::info!("Cache udp packet {} ({})", connection_info, origin_dst);
// UDP associate tunnel not ready yet, we must cache the packet...
- state.udp_data_cache = Some(s5_udp_data);
+ state.udp_data_cache.push_back(s5_udp_data);
}
}
} else {
@@ -621,7 +624,7 @@ impl<'a> TunToProxy<'a> {
udp_socket,
udp_token,
udp_origin_dst: None,
- udp_data_cache: None,
+ udp_data_cache: LinkedList::new(),
};
Ok(state)
}
@@ -871,7 +874,7 @@ impl<'a> TunToProxy<'a> {
if let Some(udp_socket) = state.udp_socket.as_ref() {
if let Some(addr) = state.tcp_proxy_handler.get_udp_associate() {
// Take ownership of udp_data_cache
- if let Some(buf) = state.udp_data_cache.take() {
+ while let Some(buf) = state.udp_data_cache.pop_front(){
udp_socket.send_to(&buf, addr)?;
}
}
From aa059e0dd5773cedc87fdd847f72c9457ef785c5 Mon Sep 17 00:00:00 2001
From: "B. Blechschmidt"
Date: Sun, 20 Aug 2023 18:54:02 +0200
Subject: [PATCH 037/363] Format correctly
---
src/tun2proxy.rs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/tun2proxy.rs b/src/tun2proxy.rs
index a289cdd..fc5119f 100644
--- a/src/tun2proxy.rs
+++ b/src/tun2proxy.rs
@@ -8,6 +8,7 @@ use smoltcp::{
wire::{IpCidr, IpProtocol, Ipv4Packet, Ipv6Packet, TcpPacket, UdpPacket, UDP_HEADER_LEN},
};
use socks5_impl::protocol::{Address, StreamOperation, UdpHeader, UserKey};
+use std::collections::LinkedList;
use std::{
collections::{HashMap, HashSet},
convert::{From, TryFrom},
@@ -17,7 +18,6 @@ use std::{
rc::Rc,
str::FromStr,
};
-use std::collections::LinkedList;
#[derive(Hash, Clone, Eq, PartialEq, PartialOrd, Ord, Debug)]
pub(crate) struct ConnectionInfo {
@@ -874,7 +874,7 @@ impl<'a> TunToProxy<'a> {
if let Some(udp_socket) = state.udp_socket.as_ref() {
if let Some(addr) = state.tcp_proxy_handler.get_udp_associate() {
// Take ownership of udp_data_cache
- 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)?;
}
}
From b244286e4d80e6fbb24d82ac7beba3d8c19a2c5c Mon Sep 17 00:00:00 2001
From: "B. Blechschmidt"
Date: Sun, 20 Aug 2023 19:36:59 +0200
Subject: [PATCH 038/363] Fix handling of multiple packets per event
---
src/tun2proxy.rs | 37 +++++++++++++++++++++----------------
1 file changed, 21 insertions(+), 16 deletions(-)
diff --git a/src/tun2proxy.rs b/src/tun2proxy.rs
index fc5119f..a8b3782 100644
--- a/src/tun2proxy.rs
+++ b/src/tun2proxy.rs
@@ -541,8 +541,7 @@ impl<'a> TunToProxy<'a> {
self.tunsocket_read_and_forward(&connection_info)?;
self.write_to_server(&connection_info)?;
} else {
- log::info!("Subsequent udp packet {} ({})", connection_info, origin_dst);
- // log::trace!("Subsequent udp packet {} ({})", connection_info, origin_dst);
+ log::trace!("Subsequent udp packet {} ({})", connection_info, origin_dst);
}
let err = "udp associate state not find";
@@ -561,8 +560,8 @@ impl<'a> TunToProxy<'a> {
socket.send_to(&s5_udp_data, udp_associate)?;
}
} else {
- log::info!("Cache udp packet {} ({})", connection_info, origin_dst);
// UDP associate tunnel not ready yet, we must cache the packet...
+ log::trace!("Cache udp packet {} ({})", connection_info, origin_dst);
state.udp_data_cache.push_back(s5_udp_data);
}
}
@@ -766,25 +765,31 @@ impl<'a> TunToProxy<'a> {
let err = "udp connection state not found";
let state = self.connection_map.get_mut(&info).ok_or(err)?;
state.expiry = Some(Self::udp_associate_timeout());
+ let src = state.udp_origin_dst.ok_or("udp address")?;
+ let mut to_send: LinkedList> = LinkedList::new();
if let Some(udp_socket) = state.udp_socket.as_ref() {
let mut buf = [0; 1 << 16];
// Receive UDP packet from remote SOCKS5 server
- let (packet_size, _svr_addr) = udp_socket.recv_from(&mut buf)?;
+ while let Ok((packet_size, _svr_addr)) = udp_socket.recv_from(&mut buf) {
+ let buf = buf[..packet_size].to_vec();
+ let header = UdpHeader::retrieve_from_stream(&mut &buf[..])?;
- let buf = buf[..packet_size].to_vec();
- let header = UdpHeader::retrieve_from_stream(&mut &buf[..])?;
+ let buf = if info.dst.port() == 53 {
+ let mut message = dns::parse_data_to_dns_message(&buf[header.len()..], false)?;
+ dns::remove_ipv6_entries(&mut message); // TODO: Configurable
+ message.to_vec()?
+ } else {
+ buf[header.len()..].to_vec()
+ };
- let buf = if info.dst.port() == 53 {
- let mut message = dns::parse_data_to_dns_message(&buf[header.len()..], false)?;
- dns::remove_ipv6_entries(&mut message); // TODO: Configurable
- message.to_vec()?
- } else {
- buf[header.len()..].to_vec()
- };
+ // Escape the borrow checker madness
+ to_send.push_back(buf);
+ }
+ }
- // Write to client
- let src = state.udp_origin_dst.ok_or("udp address")?;
- self.send_udp_packet_to_client(src, info.src, &buf)?;
+ // Write to client
+ while let Some(packet) = to_send.pop_front() {
+ self.send_udp_packet_to_client(src, info.src, &packet)?;
}
return Ok(());
From 3543472c38bf8cc419f2b06bc7aac9ab58c9b3dd Mon Sep 17 00:00:00 2001
From: "B. Blechschmidt"
Date: Sun, 20 Aug 2023 19:58:30 +0200
Subject: [PATCH 039/363] Update README with UDP info
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 1925a4e..0f76187 100644
--- a/README.md
+++ b/README.md
@@ -8,6 +8,7 @@ A tunnel interface for HTTP and SOCKS proxies on Linux based on [smoltcp](https:
- Minimal configuration setup for routing all traffic
- IPv4 and IPv6 support
- GFW evasion mechanism for certain use cases (see [issue #35](https://github.com/blechschmidt/tun2proxy/issues/35))
+- SOCKS5 UDP support
## Build
Clone the repository and `cd` into the project folder. Then run the following:
@@ -124,5 +125,4 @@ requests for IPv6 addresses.
## TODO
- Increase error robustness (reduce `unwrap` and `expect` usage)
-- UDP support for SOCKS
- Native support for proxying DNS over TCP or TLS
From 0f67dd698110e0f3b541a8a57936b560d56c1810 Mon Sep 17 00:00:00 2001
From: "B. Blechschmidt"
Date: Sun, 20 Aug 2023 20:01:02 +0200
Subject: [PATCH 040/363] Remove error robustness todo
Excessive expect and unwrap usage has been dealt with.
---
README.md | 1 -
1 file changed, 1 deletion(-)
diff --git a/README.md b/README.md
index 0f76187..4b89238 100644
--- a/README.md
+++ b/README.md
@@ -124,5 +124,4 @@ or through `ip -6 route del default`, which causes the `libc` resolver (and othe
requests for IPv6 addresses.
## TODO
-- Increase error robustness (reduce `unwrap` and `expect` usage)
- Native support for proxying DNS over TCP or TLS
From 3c09c2699ddca2759115aa4083996d4020db8985 Mon Sep 17 00:00:00 2001
From: ssrlive <30760636+ssrlive@users.noreply.github.com>
Date: Mon, 21 Aug 2023 16:08:48 +0800
Subject: [PATCH 041/363] refine code
---
src/socks.rs | 3 +--
src/tun2proxy.rs | 23 +++++++++++++----------
2 files changed, 14 insertions(+), 12 deletions(-)
diff --git a/src/socks.rs b/src/socks.rs
index 9d61f47..ee65501 100644
--- a/src/socks.rs
+++ b/src/socks.rs
@@ -203,8 +203,7 @@ impl SocksProxyImpl {
}
fn send_request_socks5(&mut self) -> Result<(), Error> {
- use socks5_impl::protocol::Command::UdpAssociate;
- let addr = if self.command == UdpAssociate {
+ let addr = if self.command == protocol::Command::UdpAssociate {
Address::unspecified()
} else {
self.info.dst.clone()
diff --git a/src/tun2proxy.rs b/src/tun2proxy.rs
index a8b3782..2ccc97e 100644
--- a/src/tun2proxy.rs
+++ b/src/tun2proxy.rs
@@ -168,6 +168,7 @@ const SERVER_WRITE_CLOSED: u8 = 1;
const CLIENT_WRITE_CLOSED: u8 = 2;
const UDP_ASSO_TIMEOUT: u64 = 10; // seconds
+const DNS_PORT: u16 = 53;
struct TcpConnectState {
smoltcp_handle: Option,
@@ -334,12 +335,12 @@ impl<'a> TunToProxy<'a> {
if let Err(e) = self.poll.registry().deregister(&mut state.mio_stream) {
// FIXME: The function `deregister` will frequently fail for unknown reasons.
- log::debug!("{}", e);
+ log::trace!("{}", e);
}
if let Some(mut udp_socket) = state.udp_socket {
if let Err(e) = self.poll.registry().deregister(&mut udp_socket) {
- log::debug!("{}", e);
+ log::trace!("{}", e);
}
}
@@ -440,7 +441,9 @@ impl<'a> TunToProxy<'a> {
fn update_mio_socket_interest(poll: &mut Poll, state: &mut TcpConnectState) -> Result<()> {
// Maybe we did not listen for any events before. Therefore, just swallow the error.
- _ = poll.registry().deregister(&mut state.mio_stream);
+ if let Err(err) = poll.registry().deregister(&mut state.mio_stream) {
+ log::trace!("{}", err);
+ }
// If we do not wait for read or write events, we do not need to register them.
if !state.wait_read && !state.wait_write {
@@ -472,7 +475,7 @@ impl<'a> TunToProxy<'a> {
None => {
let mut info = info;
let port = origin_dst.port();
- if port == 53 && 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
info.dst = Address::from(dns_addr);
}
@@ -523,7 +526,7 @@ impl<'a> TunToProxy<'a> {
} else if connection_info.protocol == IpProtocol::Udp {
let port = connection_info.dst.port();
let payload = &frame[payload_offset..payload_offset + payload_size];
- if let (Some(virtual_dns), true) = (&mut self.options.virtual_dns, port == 53) {
+ if let (Some(virtual_dns), true) = (&mut self.options.virtual_dns, port == DNS_PORT) {
log::info!("DNS query via virtual DNS {} ({})", connection_info, origin_dst);
let response = virtual_dns.receive_query(payload)?;
self.send_udp_packet_to_client(origin_dst, connection_info.src, response.as_slice())?;
@@ -560,7 +563,7 @@ impl<'a> TunToProxy<'a> {
socket.send_to(&s5_udp_data, udp_associate)?;
}
} else {
- // UDP associate tunnel not ready yet, we must cache the packet...
+ // UDP associate tunnel not ready yet, we must cache the packets...
log::trace!("Cache udp packet {} ({})", connection_info, origin_dst);
state.udp_data_cache.push_back(s5_udp_data);
}
@@ -645,7 +648,7 @@ impl<'a> TunToProxy<'a> {
let keys = self.connection_map.keys().cloned().collect::>();
for key in keys {
if self.udp_associate_timeout_expired(&key) {
- log::debug!("UDP associate timeout: {}", key);
+ log::trace!("UDP associate timeout: {}", key);
self.remove_connection(&key)?;
}
}
@@ -765,7 +768,6 @@ impl<'a> TunToProxy<'a> {
let err = "udp connection state not found";
let state = self.connection_map.get_mut(&info).ok_or(err)?;
state.expiry = Some(Self::udp_associate_timeout());
- let src = state.udp_origin_dst.ok_or("udp address")?;
let mut to_send: LinkedList> = LinkedList::new();
if let Some(udp_socket) = state.udp_socket.as_ref() {
let mut buf = [0; 1 << 16];
@@ -774,7 +776,7 @@ impl<'a> TunToProxy<'a> {
let buf = buf[..packet_size].to_vec();
let header = UdpHeader::retrieve_from_stream(&mut &buf[..])?;
- let buf = if info.dst.port() == 53 {
+ let buf = if info.dst.port() == DNS_PORT {
let mut message = dns::parse_data_to_dns_message(&buf[header.len()..], false)?;
dns::remove_ipv6_entries(&mut message); // TODO: Configurable
message.to_vec()?
@@ -788,6 +790,7 @@ impl<'a> TunToProxy<'a> {
}
// Write to client
+ let src = state.udp_origin_dst.ok_or("udp address")?;
while let Some(packet) = to_send.pop_front() {
self.send_udp_packet_to_client(src, info.src, &packet)?;
}
@@ -878,7 +881,7 @@ impl<'a> TunToProxy<'a> {
if let Some(state) = self.connection_map.get_mut(&conn_info) {
if let Some(udp_socket) = state.udp_socket.as_ref() {
if let Some(addr) = state.tcp_proxy_handler.get_udp_associate() {
- // Take ownership of udp_data_cache
+ // Consume udp_data_cache data
while let Some(buf) = state.udp_data_cache.pop_front() {
udp_socket.send_to(&buf, addr)?;
}
From 17566451cf3523a3ae23a136b9dad917ad379613 Mon Sep 17 00:00:00 2001
From: ssrlive <30760636+ssrlive@users.noreply.github.com>
Date: Mon, 21 Aug 2023 17:01:07 +0800
Subject: [PATCH 042/363] remove trait UdpProxy
---
src/tun2proxy.rs | 5 -----
1 file changed, 5 deletions(-)
diff --git a/src/tun2proxy.rs b/src/tun2proxy.rs
index 2ccc97e..c405486 100644
--- a/src/tun2proxy.rs
+++ b/src/tun2proxy.rs
@@ -196,11 +196,6 @@ pub(crate) trait TcpProxy {
fn get_udp_associate(&self) -> Option;
}
-pub(crate) trait UdpProxy {
- fn send_frame(&mut self, destination: &Address, frame: &[u8]) -> Result<(), Error>;
- fn receive_frame(&mut self, source: &SocketAddr, frame: &[u8]) -> Result<(), Error>;
-}
-
pub(crate) trait ConnectionManager {
fn new_tcp_proxy(&self, info: &ConnectionInfo, udp_associate: bool) -> Result>;
fn close_connection(&self, info: &ConnectionInfo);
From 10ade804885c2516b869f7f534189cd9079c2a77 Mon Sep 17 00:00:00 2001
From: ssrlive <30760636+ssrlive@users.noreply.github.com>
Date: Mon, 21 Aug 2023 17:14:33 +0800
Subject: [PATCH 043/363] Bump 0.1.4
---
Cargo.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Cargo.toml b/Cargo.toml
index a5ec577..cff8d93 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -2,7 +2,7 @@
authors = ["B. Blechschmidt"]
edition = "2018"
name = "tun2proxy"
-version = "0.1.2"
+version = "0.1.4"
[lib]
crate-type = ["cdylib", "lib"]
From 89aeffe19529577c7aa8221209e1105412eb5144 Mon Sep 17 00:00:00 2001
From: ssrlive <30760636+ssrlive@users.noreply.github.com>
Date: Mon, 21 Aug 2023 19:58:13 +0800
Subject: [PATCH 044/363] dns over tcp
---
src/lib.rs | 8 ++++++++
src/main.rs | 8 ++++++++
src/tun2proxy.rs | 24 ++++++++++++++++++++++++
3 files changed, 40 insertions(+)
diff --git a/src/lib.rs b/src/lib.rs
index bdbf7a0..d21d99b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -98,6 +98,7 @@ impl std::fmt::Display for ProxyType {
pub struct Options {
virtual_dns: Option,
mtu: Option,
+ dns_over_tcp: bool,
}
impl Options {
@@ -107,6 +108,13 @@ impl Options {
pub fn with_virtual_dns(mut self) -> Self {
self.virtual_dns = Some(virtdns::VirtualDns::new());
+ self.dns_over_tcp = false;
+ self
+ }
+
+ pub fn with_dns_over_tcp(mut self) -> Self {
+ self.dns_over_tcp = true;
+ self.virtual_dns = None;
self
}
diff --git a/src/main.rs b/src/main.rs
index 1502599..6047dd9 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -40,6 +40,10 @@ struct Args {
/// Verbosity level
#[arg(short, long, value_name = "level", value_enum, default_value = "info")]
verbosity: ArgVerbosity,
+
+ /// DNS over TCP
+ #[arg(long)]
+ dns_over_tcp: bool,
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, clap::ValueEnum)]
@@ -79,6 +83,10 @@ fn main() -> ExitCode {
options = options.with_virtual_dns();
}
+ if args.dns_over_tcp {
+ options = options.with_dns_over_tcp();
+ }
+
let interface = match args.tun_fd {
None => NetworkInterface::Named(args.tun.clone()),
Some(fd) => {
diff --git a/src/tun2proxy.rs b/src/tun2proxy.rs
index c405486..67574c7 100644
--- a/src/tun2proxy.rs
+++ b/src/tun2proxy.rs
@@ -527,6 +527,30 @@ impl<'a> TunToProxy<'a> {
self.send_udp_packet_to_client(origin_dst, connection_info.src, response.as_slice())?;
} else {
// Another UDP packet
+ if self.options.dns_over_tcp && port == DNS_PORT {
+ if !self.connection_map.contains_key(&connection_info) {
+ log::info!("DNS over TCP {} ({})", connection_info, origin_dst);
+ let tcp_proxy_handler = manager.new_tcp_proxy(&connection_info, false)?;
+ #[rustfmt::skip]
+ let state = self.create_new_tcp_connection_state(server_addr, origin_dst, tcp_proxy_handler, false)?;
+ self.connection_map.insert(connection_info.clone(), state);
+ } else {
+ log::trace!("Subsequent dns over tcp packet {} ({})", connection_info, origin_dst);
+ }
+
+ let len = payload.len() as u16;
+ let mut buf = Vec::with_capacity(2 + len as usize);
+ buf.extend_from_slice(&len.to_be_bytes());
+ buf.extend_from_slice(payload);
+
+ // TODO: Build an IP packet and inject it into the device.
+ self.device.inject_packet(&buf);
+
+ self.expect_smoltcp_send()?;
+ self.tunsocket_read_and_forward(&connection_info)?;
+ self.write_to_server(&connection_info)?;
+ return Ok(());
+ }
if !self.connection_map.contains_key(&connection_info) {
log::info!("UDP associate session {} ({})", connection_info, origin_dst);
let tcp_proxy_handler = manager.new_tcp_proxy(&connection_info, true)?;
From d42d3a8287a8049f5bed1ee2095a0603d7509f97 Mon Sep 17 00:00:00 2001
From: ssrlive <30760636+ssrlive@users.noreply.github.com>
Date: Tue, 22 Aug 2023 10:44:46 +0800
Subject: [PATCH 045/363] extract dns logic to separate functions
---
src/tun2proxy.rs | 89 ++++++++++++++++++++++++++----------------------
1 file changed, 48 insertions(+), 41 deletions(-)
diff --git a/src/tun2proxy.rs b/src/tun2proxy.rs
index c405486..10d17a5 100644
--- a/src/tun2proxy.rs
+++ b/src/tun2proxy.rs
@@ -757,40 +757,57 @@ impl<'a> TunToProxy<'a> {
Ok(())
}
- fn mio_socket_event(&mut self, event: &Event) -> Result<(), Error> {
- if let Some(info) = self.find_info_by_udp_token(event.token()) {
- let info = info.clone();
- let err = "udp connection state not found";
- let state = self.connection_map.get_mut(&info).ok_or(err)?;
- state.expiry = Some(Self::udp_associate_timeout());
- let mut to_send: LinkedList> = LinkedList::new();
+ fn receive_udp_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)?;
+ state.expiry = Some(Self::udp_associate_timeout());
+ let mut to_send: LinkedList> = LinkedList::new();
+ if let Some(udp_socket) = state.udp_socket.as_ref() {
+ let mut buf = [0; 1 << 16];
+ // Receive UDP packet from remote SOCKS5 server
+ while let Ok((packet_size, _svr_addr)) = udp_socket.recv_from(&mut buf) {
+ let buf = buf[..packet_size].to_vec();
+ let header = UdpHeader::retrieve_from_stream(&mut &buf[..])?;
+
+ let buf = if info.dst.port() == DNS_PORT {
+ let mut message = dns::parse_data_to_dns_message(&buf[header.len()..], false)?;
+ dns::remove_ipv6_entries(&mut message); // TODO: Configurable
+ message.to_vec()?
+ } else {
+ buf[header.len()..].to_vec()
+ };
+
+ // Escape the borrow checker madness
+ to_send.push_back(buf);
+ }
+ }
+
+ // Write to client
+ let src = state.udp_origin_dst.ok_or("udp address")?;
+ while let Some(packet) = to_send.pop_front() {
+ self.send_udp_packet_to_client(src, info.src, &packet)?;
+ }
+ Ok(())
+ }
+
+ fn comsume_cached_udp_packets(&mut self, info: &ConnectionInfo) -> Result<()> {
+ // 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() {
- let mut buf = [0; 1 << 16];
- // Receive UDP packet from remote SOCKS5 server
- while let Ok((packet_size, _svr_addr)) = udp_socket.recv_from(&mut buf) {
- let buf = buf[..packet_size].to_vec();
- let header = UdpHeader::retrieve_from_stream(&mut &buf[..])?;
-
- let buf = if info.dst.port() == DNS_PORT {
- let mut message = dns::parse_data_to_dns_message(&buf[header.len()..], false)?;
- dns::remove_ipv6_entries(&mut message); // TODO: Configurable
- message.to_vec()?
- } else {
- buf[header.len()..].to_vec()
- };
-
- // Escape the borrow checker madness
- to_send.push_back(buf);
+ if let Some(addr) = state.tcp_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)?;
+ }
}
}
+ }
+ Ok(())
+ }
- // Write to client
- let src = state.udp_origin_dst.ok_or("udp address")?;
- while let Some(packet) = to_send.pop_front() {
- self.send_udp_packet_to_client(src, info.src, &packet)?;
- }
-
- return Ok(());
+ fn mio_socket_event(&mut self, event: &Event) -> Result<(), Error> {
+ if let Some(info) = self.find_info_by_udp_token(event.token()) {
+ return self.receive_udp_packet_and_write_to_client(&info.clone());
}
let conn_info = match self.find_info_by_token(event.token()) {
@@ -872,17 +889,7 @@ impl<'a> TunToProxy<'a> {
// server.
self.write_to_server(&conn_info)?;
- // Try to send the first UDP packet to remote SOCKS5 server for UDP associate session
- if let Some(state) = self.connection_map.get_mut(&conn_info) {
- if let Some(udp_socket) = state.udp_socket.as_ref() {
- if let Some(addr) = state.tcp_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)?;
- }
- }
- }
- }
+ self.comsume_cached_udp_packets(&conn_info)?;
}
if event.is_writable() {
From 0f3903f45500b9ed34a76a4ea123d517453e35e6 Mon Sep 17 00:00:00 2001
From: ssrlive <30760636+ssrlive@users.noreply.github.com>
Date: Tue, 22 Aug 2023 11:19:58 +0800
Subject: [PATCH 046/363] deal_with_incoming_udp_packets
---
src/tun2proxy.rs | 81 +++++++++++++++++++++++++++---------------------
1 file changed, 46 insertions(+), 35 deletions(-)
diff --git a/src/tun2proxy.rs b/src/tun2proxy.rs
index 10d17a5..8888b2c 100644
--- a/src/tun2proxy.rs
+++ b/src/tun2proxy.rs
@@ -456,6 +456,51 @@ impl<'a> TunToProxy<'a> {
Ok(())
}
+ fn deal_with_incoming_udp_packets(
+ &mut self,
+ manager: &Rc,
+ info: &ConnectionInfo,
+ origin_dst: SocketAddr,
+ payload: &[u8],
+ ) -> 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 server_addr = manager.get_server_addr();
+ let mut state = self.create_new_tcp_connection_state(server_addr, origin_dst, tcp_proxy_handler, true)?;
+ state.udp_origin_dst = Some(origin_dst);
+ self.connection_map.insert(info.clone(), state);
+
+ self.expect_smoltcp_send()?;
+ self.tunsocket_read_and_forward(info)?;
+ self.write_to_server(info)?;
+ } else {
+ log::trace!("Subsequent udp packet {} ({})", info, origin_dst);
+ }
+
+ let err = "udp associate state not find";
+ let state = self.connection_map.get_mut(info).ok_or(err)?;
+ assert!(state.expiry.is_some());
+ state.expiry = Some(Self::udp_associate_timeout());
+
+ // Add SOCKS5 UDP header to the incoming data
+ let mut s5_udp_data = Vec::::new();
+ 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() {
+ // 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)?;
+ }
+ } else {
+ // UDP associate tunnel not ready yet, we must cache the packets...
+ log::trace!("Cache udp packet {} ({})", info, origin_dst);
+ state.udp_data_cache.push_back(s5_udp_data);
+ }
+ Ok(())
+ }
+
// A raw packet was received on the tunnel interface.
fn receive_tun(&mut self, frame: &mut [u8]) -> Result<(), Error> {
let mut handler = || -> Result<(), Error> {
@@ -527,41 +572,7 @@ impl<'a> TunToProxy<'a> {
self.send_udp_packet_to_client(origin_dst, connection_info.src, response.as_slice())?;
} else {
// Another UDP packet
- if !self.connection_map.contains_key(&connection_info) {
- log::info!("UDP associate session {} ({})", connection_info, origin_dst);
- let tcp_proxy_handler = manager.new_tcp_proxy(&connection_info, true)?;
- #[rustfmt::skip]
- let mut state = self.create_new_tcp_connection_state(server_addr, origin_dst, tcp_proxy_handler, true)?;
- state.udp_origin_dst = Some(origin_dst);
- self.connection_map.insert(connection_info.clone(), state);
-
- self.expect_smoltcp_send()?;
- self.tunsocket_read_and_forward(&connection_info)?;
- self.write_to_server(&connection_info)?;
- } else {
- log::trace!("Subsequent udp packet {} ({})", connection_info, origin_dst);
- }
-
- let err = "udp associate state not find";
- let state = self.connection_map.get_mut(&connection_info).ok_or(err)?;
- assert!(state.expiry.is_some());
- state.expiry = Some(Self::udp_associate_timeout());
-
- // Add SOCKS5 UDP header to the incoming data
- let mut s5_udp_data = Vec::::new();
- UdpHeader::new(0, connection_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() {
- // 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)?;
- }
- } else {
- // UDP associate tunnel not ready yet, we must cache the packets...
- log::trace!("Cache udp packet {} ({})", connection_info, origin_dst);
- state.udp_data_cache.push_back(s5_udp_data);
- }
+ self.deal_with_incoming_udp_packets(&manager, &connection_info, origin_dst, payload)?;
}
} else {
log::warn!("Unsupported protocol: {} ({})", connection_info, origin_dst);
From 40f8870033401a480d9afba5378013efc7a70441 Mon Sep 17 00:00:00 2001
From: ssrlive <30760636+ssrlive@users.noreply.github.com>
Date: Tue, 22 Aug 2023 12:14:14 +0800
Subject: [PATCH 047/363] preprocess_origin_connection_info
---
src/tun2proxy.rs | 44 +++++++++++++++++++++++++-------------------
1 file changed, 25 insertions(+), 19 deletions(-)
diff --git a/src/tun2proxy.rs b/src/tun2proxy.rs
index 8888b2c..848267e 100644
--- a/src/tun2proxy.rs
+++ b/src/tun2proxy.rs
@@ -456,6 +456,30 @@ impl<'a> TunToProxy<'a> {
Ok(())
}
+ fn preprocess_origin_connection_info(&mut self, info: ConnectionInfo) -> Result {
+ let origin_dst = SocketAddr::try_from(&info.dst)?;
+ let connection_info = match &mut self.options.virtual_dns {
+ None => {
+ 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
+ info.dst = Address::from(dns_addr);
+ }
+ info
+ }
+ Some(virtual_dns) => {
+ let dst_ip = origin_dst.ip();
+ virtual_dns.touch_ip(&dst_ip);
+ match virtual_dns.resolve_ip(&dst_ip) {
+ None => info,
+ Some(name) => info.to_named(name.clone()),
+ }
+ }
+ };
+ Ok(connection_info)
+ }
+
fn deal_with_incoming_udp_packets(
&mut self,
manager: &Rc,
@@ -511,25 +535,7 @@ impl<'a> TunToProxy<'a> {
}
let (info, _first_packet, payload_offset, payload_size) = result?;
let origin_dst = SocketAddr::try_from(&info.dst)?;
- let connection_info = match &mut self.options.virtual_dns {
- None => {
- 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
- info.dst = Address::from(dns_addr);
- }
- info
- }
- Some(virtual_dns) => {
- let dst_ip = origin_dst.ip();
- virtual_dns.touch_ip(&dst_ip);
- match virtual_dns.resolve_ip(&dst_ip) {
- None => info,
- Some(name) => info.to_named(name.clone()),
- }
- }
- };
+ let connection_info = self.preprocess_origin_connection_info(info)?;
let manager = self.get_connection_manager().ok_or("get connection manager")?;
let server_addr = manager.get_server_addr();
From fb86172ecc9795aa2fcdaa449836c7ef97f6348d Mon Sep 17 00:00:00 2001
From: ssrlive <30760636+ssrlive@users.noreply.github.com>
Date: Tue, 22 Aug 2023 12:59:31 +0800
Subject: [PATCH 048/363] refine code
---
src/tun2proxy.rs | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/src/tun2proxy.rs b/src/tun2proxy.rs
index 848267e..27e8635 100644
--- a/src/tun2proxy.rs
+++ b/src/tun2proxy.rs
@@ -480,7 +480,7 @@ impl<'a> TunToProxy<'a> {
Ok(connection_info)
}
- fn deal_with_incoming_udp_packets(
+ fn process_incoming_udp_packets(
&mut self,
manager: &Rc,
info: &ConnectionInfo,
@@ -538,13 +538,12 @@ impl<'a> TunToProxy<'a> {
let connection_info = self.preprocess_origin_connection_info(info)?;
let manager = self.get_connection_manager().ok_or("get connection manager")?;
- let server_addr = manager.get_server_addr();
if connection_info.protocol == IpProtocol::Tcp {
if _first_packet {
let tcp_proxy_handler = manager.new_tcp_proxy(&connection_info, false)?;
- #[rustfmt::skip]
- let state = self.create_new_tcp_connection_state(server_addr, origin_dst, tcp_proxy_handler, false)?;
+ let server = manager.get_server_addr();
+ let state = self.create_new_tcp_connection_state(server, origin_dst, tcp_proxy_handler, false)?;
self.connection_map.insert(connection_info.clone(), state);
log::info!("Connect done {} ({})", connection_info, origin_dst);
@@ -578,7 +577,7 @@ impl<'a> TunToProxy<'a> {
self.send_udp_packet_to_client(origin_dst, connection_info.src, response.as_slice())?;
} else {
// Another UDP packet
- self.deal_with_incoming_udp_packets(&manager, &connection_info, origin_dst, payload)?;
+ self.process_incoming_udp_packets(&manager, &connection_info, origin_dst, payload)?;
}
} else {
log::warn!("Unsupported protocol: {} ({})", connection_info, origin_dst);
From b2505dcfd7268e057e8c2bf313976c4a440f4a4b Mon Sep 17 00:00:00 2001
From: ssrlive <30760636+ssrlive@users.noreply.github.com>
Date: Tue, 22 Aug 2023 17:20:35 +0800
Subject: [PATCH 049/363] udp_acco_expiry
---
src/tun2proxy.rs | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/src/tun2proxy.rs b/src/tun2proxy.rs
index 27e8635..d71944d 100644
--- a/src/tun2proxy.rs
+++ b/src/tun2proxy.rs
@@ -178,7 +178,7 @@ struct TcpConnectState {
close_state: u8,
wait_read: bool,
wait_write: bool,
- expiry: Option<::std::time::Instant>,
+ udp_acco_expiry: Option<::std::time::Instant>,
udp_socket: Option,
udp_token: Option,
udp_origin_dst: Option,
@@ -504,8 +504,8 @@ impl<'a> TunToProxy<'a> {
let err = "udp associate state not find";
let state = self.connection_map.get_mut(info).ok_or(err)?;
- assert!(state.expiry.is_some());
- state.expiry = Some(Self::udp_associate_timeout());
+ assert!(state.udp_acco_expiry.is_some());
+ state.udp_acco_expiry = Some(Self::udp_associate_timeout());
// Add SOCKS5 UDP header to the incoming data
let mut s5_udp_data = Vec::::new();
@@ -633,7 +633,7 @@ impl<'a> TunToProxy<'a> {
close_state: 0,
wait_read: true,
wait_write: false,
- expiry,
+ udp_acco_expiry: expiry,
udp_socket,
udp_token,
udp_origin_dst: None,
@@ -648,7 +648,7 @@ impl<'a> TunToProxy<'a> {
fn udp_associate_timeout_expired(&self, info: &ConnectionInfo) -> bool {
if let Some(state) = self.connection_map.get(info) {
- if let Some(expiry) = state.expiry {
+ if let Some(expiry) = state.udp_acco_expiry {
return expiry < ::std::time::Instant::now();
}
}
@@ -776,7 +776,8 @@ impl<'a> TunToProxy<'a> {
fn receive_udp_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)?;
- state.expiry = Some(Self::udp_associate_timeout());
+ assert!(state.udp_acco_expiry.is_some());
+ state.udp_acco_expiry = Some(Self::udp_associate_timeout());
let mut to_send: LinkedList> = LinkedList::new();
if let Some(udp_socket) = state.udp_socket.as_ref() {
let mut buf = [0; 1 << 16];
From df7ecfd6a92fe12359ddef7a6bca1b0704ec9e0d Mon Sep 17 00:00:00 2001
From: ssrlive <30760636+ssrlive@users.noreply.github.com>
Date: Tue, 22 Aug 2023 17:57:59 +0800
Subject: [PATCH 050/363] minor changes
---
src/tun2proxy.rs | 18 +++++++++++++-----
1 file changed, 13 insertions(+), 5 deletions(-)
diff --git a/src/tun2proxy.rs b/src/tun2proxy.rs
index 511ca42..e9776c8 100644
--- a/src/tun2proxy.rs
+++ b/src/tun2proxy.rs
@@ -496,6 +496,10 @@ impl<'a> TunToProxy<'a> {
let server_addr = manager.get_server_addr();
let state = self.create_new_tcp_connection_state(server_addr, origin_dst, tcp_proxy_handler, false)?;
self.connection_map.insert(info.clone(), state);
+
+ self.expect_smoltcp_send()?;
+ self.tunsocket_read_and_forward(info)?;
+ self.write_to_server(info)?;
} else {
log::trace!("DNS over TCP subsequent packet {} ({})", info, origin_dst);
}
@@ -506,12 +510,16 @@ impl<'a> TunToProxy<'a> {
buf.extend_from_slice(&len.to_be_bytes());
buf.extend_from_slice(payload);
- // FIXME: Build an IP packet with TCP and inject it into the device.
- self.device.inject_packet(&buf);
+ let err = "udp over tcp state not find";
+ let state = self.connection_map.get_mut(info).ok_or(err)?;
+ if state.tcp_proxy_handler.connection_established() {
+ _ = state.mio_stream.write(&buf)?;
+ } else {
+ // FIXME: Build an IP packet with TCP and inject it into the device,
+ // or cache them and send them when the connection is established?
+ self.device.inject_packet(&buf);
+ }
- self.expect_smoltcp_send()?;
- self.tunsocket_read_and_forward(info)?;
- self.write_to_server(info)?;
return Ok(());
}
From 1f5586b880b36ca003ee80cd3d47eb570b185901 Mon Sep 17 00:00:00 2001
From: ssrlive <30760636+ssrlive@users.noreply.github.com>
Date: Tue, 22 Aug 2023 18:21:38 +0800
Subject: [PATCH 051/363] udp_over_tcp_data_cache
---
src/tun2proxy.rs | 18 ++++++++++++------
1 file changed, 12 insertions(+), 6 deletions(-)
diff --git a/src/tun2proxy.rs b/src/tun2proxy.rs
index e9776c8..de2167d 100644
--- a/src/tun2proxy.rs
+++ b/src/tun2proxy.rs
@@ -183,6 +183,8 @@ struct TcpConnectState {
udp_token: Option,
udp_origin_dst: Option,
udp_data_cache: LinkedList>,
+ udp_over_tcp_expiry: Option<::std::time::Instant>,
+ udp_over_tcp_data_cache: LinkedList