mirror of
https://github.com/tun2proxy/tun2proxy.git
synced 2025-06-03 13:20:32 +00:00
swith socks5-impl
This commit is contained in:
parent
ab9f8011f0
commit
c61b6c74cd
11 changed files with 163 additions and 301 deletions
|
@ -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"
|
||||
|
|
|
@ -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 <address>` if you
|
||||
actually tunneled. In such a case, the tool will tell you to specify the address through `--bypass-ip <address>` 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 <URL> Proxy URL in the form proto://[username[:password]@]host:port
|
||||
-d, --dns <method> DNS handling [default: virtual] [possible values: virtual, none]
|
||||
-s, --setup <method> Routing and system setup [possible values: auto]
|
||||
--setup-ip <IP> Public proxy IP used in routing setup
|
||||
--bypass-ip <IP> Public proxy IP used in routing setup which should bypassing the tunnel
|
||||
-h, --help Print help
|
||||
-V, --version Print version
|
||||
```
|
||||
|
|
|
@ -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},
|
||||
|
|
47
src/http.rs
47
src/http.rs
|
@ -1,19 +1,22 @@
|
|||
use crate::error::Error;
|
||||
use crate::tun2proxy::{
|
||||
Connection, ConnectionManager, Destination, Direction, IncomingDataEvent, IncomingDirection,
|
||||
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<RefCell<Option<DigestState>>>,
|
||||
before: bool,
|
||||
credentials: Option<Credentials>,
|
||||
destination: Destination,
|
||||
credentials: Option<UserKey>,
|
||||
destination: Address,
|
||||
}
|
||||
|
||||
static PROXY_AUTHENTICATE: &str = "Proxy-Authenticate";
|
||||
|
@ -66,11 +69,11 @@ impl HttpConnection {
|
|||
) -> Result<Self, Error> {
|
||||
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>,
|
||||
credentials: Option<UserKey>,
|
||||
digest_state: Rc<RefCell<Option<DigestState>>>,
|
||||
}
|
||||
|
||||
|
@ -416,13 +419,13 @@ impl ConnectionManager for HttpManager {
|
|||
self.server
|
||||
}
|
||||
|
||||
fn get_credentials(&self) -> &Option<Credentials> {
|
||||
fn get_credentials(&self) -> &Option<UserKey> {
|
||||
&self.credentials
|
||||
}
|
||||
}
|
||||
|
||||
impl HttpManager {
|
||||
pub fn new(server: SocketAddr, credentials: Option<Credentials>) -> Rc<Self> {
|
||||
pub fn new(server: SocketAddr, credentials: Option<UserKey>) -> Rc<Self> {
|
||||
Rc::new(Self {
|
||||
server,
|
||||
credentials,
|
||||
|
|
29
src/lib.rs
29
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<Credentials>,
|
||||
pub credentials: Option<UserKey>,
|
||||
}
|
||||
|
||||
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,
|
||||
|
|
24
src/main.rs
24
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<ArgSetup>,
|
||||
|
||||
/// 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<IpAddr>,
|
||||
bypass_ip: Option<IpAddr>,
|
||||
}
|
||||
|
||||
#[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
|
||||
}
|
||||
|
|
25
src/setup.rs
25
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
|
||||
)
|
||||
}
|
||||
|
|
124
src/socks.rs
124
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<u8> for SocksAddressType {
|
||||
type Error = Error;
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
1 => Ok(SocksAddressType::Ipv4),
|
||||
3 => Ok(SocksAddressType::DomainName),
|
||||
4 => Ok(SocksAddressType::Ipv6),
|
||||
_ => Err(format!("Unknown address type: {}", value).into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SocksAddressType> 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<u8>,
|
||||
data_buf: VecDeque<u8>,
|
||||
version: SocksVersion,
|
||||
credentials: Option<Credentials>,
|
||||
credentials: Option<UserKey>,
|
||||
}
|
||||
|
||||
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::<u8>::new();
|
||||
let mut name_vec = Vec::<u8>::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>,
|
||||
credentials: Option<UserKey>,
|
||||
version: SocksVersion,
|
||||
}
|
||||
|
||||
|
@ -462,7 +400,7 @@ impl ConnectionManager for SocksManager {
|
|||
self.server
|
||||
}
|
||||
|
||||
fn get_credentials(&self) -> &Option<Credentials> {
|
||||
fn get_credentials(&self) -> &Option<UserKey> {
|
||||
&self.credentials
|
||||
}
|
||||
}
|
||||
|
@ -471,7 +409,7 @@ impl SocksManager {
|
|||
pub fn new(
|
||||
server: SocketAddr,
|
||||
version: SocksVersion,
|
||||
credentials: Option<Credentials>,
|
||||
credentials: Option<UserKey>,
|
||||
) -> Rc<Self> {
|
||||
Rc::new(Self {
|
||||
server,
|
||||
|
|
152
src/tun2proxy.rs
152
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<Destination> for SocketAddr {
|
||||
type Error = Error;
|
||||
fn try_from(value: Destination) -> Result<Self, Self::Error> {
|
||||
let ip = match value.host {
|
||||
DestinationHost::Address(addr) => addr,
|
||||
DestinationHost::Hostname(e) => {
|
||||
return Err(e.into());
|
||||
}
|
||||
};
|
||||
Ok(SocketAddr::new(ip, value.port))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SocketAddr> 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<Option<Box<dyn TcpProxy>>, Error>;
|
||||
fn close_connection(&self, connection: &Connection);
|
||||
fn get_server(&self) -> SocketAddr;
|
||||
fn get_credentials(&self) -> &Option<Credentials>;
|
||||
fn get_credentials(&self) -> &Option<UserKey>;
|
||||
}
|
||||
|
||||
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(())
|
||||
})
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Vec<u8>> {
|
||||
|
@ -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());
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue