swith socks5-impl

This commit is contained in:
ssrlive 2023-07-23 02:03:15 +08:00 committed by B. Blechschmidt
parent ab9f8011f0
commit c61b6c74cd
11 changed files with 163 additions and 301 deletions

View file

@ -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"

View file

@ -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
```

View file

@ -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},

View file

@ -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,

View file

@ -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,

View file

@ -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
}

View file

@ -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
)
}

View file

@ -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,

View file

@ -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());
}
}
}

View file

@ -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()
}
}
}

View file

@ -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());