mirror of
https://github.com/tun2proxy/tun2proxy.git
synced 2025-06-21 08:30:53 +00:00
Initial commit
This commit is contained in:
commit
6607df83cd
9 changed files with 1737 additions and 0 deletions
177
src/http.rs
Normal file
177
src/http.rs
Normal file
|
@ -0,0 +1,177 @@
|
|||
use crate::tun2proxy::{Connection, TcpProxy, IncomingDirection, OutgoingDirection, OutgoingDataEvent, IncomingDataEvent, ConnectionManager};
|
||||
use std::collections::VecDeque;
|
||||
use std::net::SocketAddr;
|
||||
|
||||
#[derive(Eq, PartialEq, Debug)]
|
||||
#[allow(dead_code)]
|
||||
enum HttpState {
|
||||
SendRequest,
|
||||
ExpectResponse,
|
||||
Established
|
||||
}
|
||||
|
||||
pub struct HttpConnection {
|
||||
state: HttpState,
|
||||
client_inbuf: VecDeque<u8>,
|
||||
server_inbuf: VecDeque<u8>,
|
||||
client_outbuf: VecDeque<u8>,
|
||||
server_outbuf: VecDeque<u8>,
|
||||
data_buf: VecDeque<u8>,
|
||||
crlf_state: u8
|
||||
}
|
||||
|
||||
impl HttpConnection {
|
||||
fn new(connection: &Connection) -> Self {
|
||||
let mut result = Self {
|
||||
state: HttpState::ExpectResponse,
|
||||
client_inbuf: Default::default(),
|
||||
server_inbuf: Default::default(),
|
||||
client_outbuf: Default::default(),
|
||||
server_outbuf: Default::default(),
|
||||
data_buf: Default::default(),
|
||||
crlf_state: Default::default()
|
||||
};
|
||||
|
||||
|
||||
result.server_outbuf.extend(b"CONNECT ".iter());
|
||||
result.destination_to_server_outbuf(connection);
|
||||
result.server_outbuf.extend(b" HTTP/1.1\r\nHost: ".iter());
|
||||
result.destination_to_server_outbuf(connection);
|
||||
result.server_outbuf.extend(b"\r\n\r\n".iter());
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn destination_to_server_outbuf(&mut self, connection: &Connection) {
|
||||
let ipv6 = connection.dst.is_ipv6();
|
||||
if ipv6 {
|
||||
self.server_outbuf.extend(b"[".iter());
|
||||
}
|
||||
self.server_outbuf.extend(connection.dst.ip().to_string().as_bytes());
|
||||
if ipv6 {
|
||||
self.server_outbuf.extend(b"]".iter());
|
||||
}
|
||||
self.server_outbuf.extend(b":".iter());
|
||||
self.server_outbuf.extend(connection.dst.port().to_string().as_bytes());
|
||||
}
|
||||
|
||||
fn state_change(&mut self) {
|
||||
match self.state {
|
||||
HttpState::ExpectResponse => {
|
||||
let mut counter = 0usize;
|
||||
for b_ref in self.server_inbuf.iter() {
|
||||
let b = *b_ref;
|
||||
if b == b'\n' {
|
||||
self.crlf_state += 1;
|
||||
} else if b != b'\r' {
|
||||
self.crlf_state = 0;
|
||||
}
|
||||
counter += 1;
|
||||
|
||||
if self.crlf_state == 2 {
|
||||
self.server_inbuf.drain(0..counter);
|
||||
|
||||
self.server_outbuf.append(&mut self.data_buf);
|
||||
self.data_buf.clear();
|
||||
|
||||
self.client_outbuf.extend(self.server_inbuf.iter());
|
||||
self.server_outbuf.extend(self.client_inbuf.iter());
|
||||
self.server_inbuf.clear();
|
||||
self.client_inbuf.clear();
|
||||
|
||||
self.state = HttpState::Established;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
self.server_inbuf.drain(0..counter);
|
||||
}
|
||||
HttpState::Established => {
|
||||
self.client_outbuf.extend(self.server_inbuf.iter());
|
||||
self.server_outbuf.extend(self.client_inbuf.iter());
|
||||
self.server_inbuf.clear();
|
||||
self.client_inbuf.clear();
|
||||
}
|
||||
_ => {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl TcpProxy for HttpConnection {
|
||||
fn push_data(&mut self, event: IncomingDataEvent<'_>) {
|
||||
let direction = event.direction;
|
||||
let buffer = event.buffer;
|
||||
match direction {
|
||||
IncomingDirection::FromServer => {
|
||||
self.server_inbuf.extend(buffer.iter());
|
||||
},
|
||||
IncomingDirection::FromClient => {
|
||||
if self.state == HttpState::Established {
|
||||
self.client_inbuf.extend(buffer.iter());
|
||||
} else {
|
||||
self.data_buf.extend(buffer.iter());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.state_change();
|
||||
|
||||
}
|
||||
|
||||
fn consume_data(&mut self, dir: OutgoingDirection, size: usize) {
|
||||
let buffer = if dir == OutgoingDirection::ToServer
|
||||
{
|
||||
&mut self.server_outbuf
|
||||
} else {
|
||||
&mut self.client_outbuf
|
||||
};
|
||||
buffer.drain(0..size);
|
||||
}
|
||||
|
||||
fn peek_data(&mut self, dir: OutgoingDirection) -> OutgoingDataEvent {
|
||||
let buffer = if dir == OutgoingDirection::ToServer {
|
||||
&mut self.server_outbuf
|
||||
} else {
|
||||
&mut self.client_outbuf
|
||||
};
|
||||
let event = OutgoingDataEvent {
|
||||
direction: dir,
|
||||
buffer: buffer.make_contiguous()
|
||||
};
|
||||
return event;
|
||||
}
|
||||
}
|
||||
|
||||
pub struct HttpManager {
|
||||
server: std::net::SocketAddr,
|
||||
}
|
||||
|
||||
impl ConnectionManager for HttpManager {
|
||||
fn handles_connection(&self, connection: &Connection) -> bool {
|
||||
connection.proto == smoltcp::wire::IpProtocol::Tcp.into()
|
||||
}
|
||||
|
||||
fn new_connection(&mut self, connection: &Connection) -> Option<std::boxed::Box<dyn TcpProxy>> {
|
||||
if connection.proto != smoltcp::wire::IpProtocol::Tcp.into() {
|
||||
return None;
|
||||
}
|
||||
Some(std::boxed::Box::new(HttpConnection::new(&connection)))
|
||||
}
|
||||
|
||||
fn close_connection(&mut self, _: &Connection) {}
|
||||
|
||||
fn get_server(&self) -> SocketAddr {
|
||||
self.server
|
||||
}
|
||||
}
|
||||
|
||||
impl HttpManager {
|
||||
pub fn new(server: SocketAddr) -> Self {
|
||||
Self {
|
||||
server,
|
||||
}
|
||||
}
|
||||
}
|
66
src/main.rs
Normal file
66
src/main.rs
Normal file
|
@ -0,0 +1,66 @@
|
|||
#![feature(deque_make_contiguous)]
|
||||
mod virtdevice;
|
||||
mod socks5;
|
||||
mod http;
|
||||
mod tun2proxy;
|
||||
|
||||
use socks5::*;
|
||||
use crate::http::HttpManager;
|
||||
use crate::tun2proxy::TunToProxy;
|
||||
use std::net::SocketAddr;
|
||||
use std::str::FromStr;
|
||||
|
||||
fn main() {
|
||||
let matches = clap::App::new(env!("CARGO_PKG_NAME"))
|
||||
.version(env!("CARGO_PKG_VERSION"))
|
||||
.about("Tunnel interface to proxy.")
|
||||
.arg(clap::Arg::with_name("tun")
|
||||
.short("t")
|
||||
.long("tun")
|
||||
.value_name("TUN")
|
||||
.help("Name of the tun interface")
|
||||
.required(true)
|
||||
.takes_value(true))
|
||||
.arg(clap::Arg::with_name("socks5_server")
|
||||
.help("SOCKS5 server to use")
|
||||
.short("s")
|
||||
.long("socks5")
|
||||
.value_name("IP:PORT"))
|
||||
.arg(clap::Arg::with_name("http_server")
|
||||
.help("HTTP server to use")
|
||||
.short("h")
|
||||
.long("http")
|
||||
.value_name("IP:PORT"))
|
||||
.get_matches();
|
||||
|
||||
if matches.value_of("socks5_server").is_some()
|
||||
&& matches.value_of("http_server").is_some()
|
||||
|| matches.value_of("socks5_server").is_none()
|
||||
&& matches.value_of("http_server").is_none() {
|
||||
eprintln!("You need to specify exactly one server.");
|
||||
return;
|
||||
}
|
||||
|
||||
let tun_name = matches.value_of("tun").unwrap();
|
||||
let mut ttp = TunToProxy::new(tun_name);
|
||||
if let Some(addr) = matches.value_of("socks5_server") {
|
||||
if let Ok(server) = SocketAddr::from_str(addr)
|
||||
{
|
||||
ttp.add_connection_manager(Box::new(Socks5Manager::new(server)));
|
||||
} else {
|
||||
eprintln!("Invalid server address.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(addr) = matches.value_of("http_server") {
|
||||
if let Ok(server) = SocketAddr::from_str(addr)
|
||||
{
|
||||
ttp.add_connection_manager(Box::new(HttpManager::new(server)));
|
||||
} else {
|
||||
eprintln!("Invalid server address.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
ttp.run();
|
||||
}
|
225
src/socks5.rs
Normal file
225
src/socks5.rs
Normal file
|
@ -0,0 +1,225 @@
|
|||
use crate::tun2proxy::{Connection, OutgoingDirection, OutgoingDataEvent, IncomingDirection, IncomingDataEvent, ConnectionManager, TcpProxy};
|
||||
use std::collections::VecDeque;
|
||||
use std::net::{IpAddr, SocketAddr};
|
||||
|
||||
#[derive(Eq, PartialEq, Debug)]
|
||||
#[allow(dead_code)]
|
||||
enum SocksState {
|
||||
ClientHello,
|
||||
ServerHello,
|
||||
SendRequest,
|
||||
ReceiveResponse,
|
||||
Established
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(u8)]
|
||||
#[derive(Copy, Clone)]
|
||||
enum SocksAddressType {
|
||||
Ipv4 = 1,
|
||||
DomainName = 3,
|
||||
Ipv6 = 4
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(u8)]
|
||||
enum SocksAuthentication {
|
||||
None = 0,
|
||||
Password = 2
|
||||
}
|
||||
|
||||
pub struct SocksConnection {
|
||||
connection: Connection,
|
||||
state: SocksState,
|
||||
client_inbuf: VecDeque<u8>,
|
||||
server_inbuf: VecDeque<u8>,
|
||||
client_outbuf: VecDeque<u8>,
|
||||
server_outbuf: VecDeque<u8>,
|
||||
data_buf: VecDeque<u8>,
|
||||
}
|
||||
|
||||
impl SocksConnection {
|
||||
pub fn new(connection: &Connection) -> Self {
|
||||
let mut result = Self {
|
||||
connection: *connection,
|
||||
state: SocksState::ServerHello,
|
||||
client_inbuf: Default::default(),
|
||||
server_inbuf: Default::default(),
|
||||
client_outbuf: Default::default(),
|
||||
server_outbuf: Default::default(),
|
||||
data_buf: Default::default(),
|
||||
};
|
||||
result.server_outbuf.extend(&[5u8, 1, 0]);
|
||||
result.state = SocksState::ServerHello;
|
||||
result
|
||||
}
|
||||
|
||||
fn forward_data(&mut self) {
|
||||
self.client_outbuf.extend(self.server_inbuf.iter());
|
||||
self.server_outbuf.extend(self.client_inbuf.iter());
|
||||
self.server_inbuf.clear();
|
||||
self.client_inbuf.clear();
|
||||
}
|
||||
|
||||
pub fn state_change(&mut self) {
|
||||
let dst_ip = self.connection.dst.ip();
|
||||
|
||||
|
||||
match self.state {
|
||||
SocksState::ServerHello if self.server_inbuf.len() == 2 => {
|
||||
assert!(self.server_inbuf[0] == 5 && self.server_inbuf[1] == 0);
|
||||
self.server_inbuf.drain(0..2);
|
||||
|
||||
let cmd = if dst_ip.is_ipv4() { 1 } else { 4 };
|
||||
self.server_outbuf.extend(&[5u8, 1, 0, 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()),
|
||||
};
|
||||
self.server_outbuf.extend(&[
|
||||
(self.connection.dst.port() >> 8) as u8,
|
||||
(self.connection.dst.port() & 0xff) as u8
|
||||
]);
|
||||
|
||||
self.state = SocksState::ReceiveResponse;
|
||||
}
|
||||
|
||||
SocksState::ServerHello if self.server_inbuf.len() > 2 => {
|
||||
panic!("Socks protocol error!")
|
||||
}
|
||||
|
||||
SocksState::ReceiveResponse if self.server_inbuf.len() >= 4 => {
|
||||
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 atyp != SocksAddressType::Ipv4 as u8
|
||||
&& atyp != SocksAddressType::Ipv6 as u8
|
||||
&& atyp != SocksAddressType::DomainName as u8 {
|
||||
panic!("Invalid address type");
|
||||
}
|
||||
|
||||
if atyp == SocksAddressType::DomainName as u8 && self.server_inbuf.len() < 5 {
|
||||
return;
|
||||
}
|
||||
|
||||
if atyp == SocksAddressType::DomainName as u8
|
||||
&& self.server_inbuf.len() < 7 + (self.server_inbuf[4] as usize) {
|
||||
return;
|
||||
}
|
||||
|
||||
let message_length = if atyp == SocksAddressType::Ipv4 as u8 {
|
||||
10
|
||||
} else if atyp == SocksAddressType::Ipv6 as u8 {
|
||||
22
|
||||
} else {
|
||||
7 + (self.server_inbuf[4] as usize)
|
||||
};
|
||||
|
||||
self.server_inbuf.drain(0..message_length);
|
||||
self.server_outbuf.append(&mut self.data_buf);
|
||||
self.data_buf.clear();
|
||||
|
||||
self.forward_data();
|
||||
self.state = SocksState::Established;
|
||||
}
|
||||
|
||||
SocksState::Established => {
|
||||
self.forward_data();
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TcpProxy for SocksConnection {
|
||||
fn push_data(&mut self, event: IncomingDataEvent<'_>) {
|
||||
let direction = event.direction;
|
||||
let buffer = event.buffer;
|
||||
match direction {
|
||||
IncomingDirection::FromServer => {
|
||||
self.server_inbuf.extend(buffer.iter());
|
||||
},
|
||||
IncomingDirection::FromClient => {
|
||||
if self.state == SocksState::Established {
|
||||
self.client_inbuf.extend(buffer.iter());
|
||||
} else {
|
||||
self.data_buf.extend(buffer.iter());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.state_change();
|
||||
|
||||
}
|
||||
|
||||
fn consume_data(&mut self, dir: OutgoingDirection, size: usize) {
|
||||
let buffer = if dir == OutgoingDirection::ToServer
|
||||
{
|
||||
&mut self.server_outbuf
|
||||
} else {
|
||||
&mut self.client_outbuf
|
||||
};
|
||||
buffer.drain(0..size);
|
||||
}
|
||||
|
||||
fn peek_data(&mut self, dir: OutgoingDirection) -> OutgoingDataEvent {
|
||||
let buffer = if dir == OutgoingDirection::ToServer {
|
||||
&mut self.server_outbuf
|
||||
} else {
|
||||
&mut self.client_outbuf
|
||||
};
|
||||
let event = OutgoingDataEvent {
|
||||
direction: dir,
|
||||
buffer: buffer.make_contiguous()
|
||||
};
|
||||
return event;
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Socks5Manager {
|
||||
server: std::net::SocketAddr,
|
||||
authentication: SocksAuthentication,
|
||||
username: Vec<u8>,
|
||||
password: Vec<u8>,
|
||||
}
|
||||
|
||||
impl ConnectionManager for Socks5Manager {
|
||||
fn handles_connection(&self, connection: &Connection) -> bool {
|
||||
connection.proto == smoltcp::wire::IpProtocol::Tcp.into()
|
||||
}
|
||||
|
||||
fn new_connection(&mut self, connection: &Connection) -> Option<std::boxed::Box<dyn TcpProxy>> {
|
||||
if connection.proto != smoltcp::wire::IpProtocol::Tcp.into() {
|
||||
return None;
|
||||
}
|
||||
Some(std::boxed::Box::new(SocksConnection::new(&connection)))
|
||||
}
|
||||
|
||||
fn close_connection(&mut self, _: &Connection) {}
|
||||
|
||||
fn get_server(&self) -> SocketAddr {
|
||||
self.server
|
||||
}
|
||||
}
|
||||
|
||||
impl Socks5Manager {
|
||||
pub fn new(server: SocketAddr) -> Self {
|
||||
Self {
|
||||
server,
|
||||
authentication: SocksAuthentication::None,
|
||||
username: Default::default(),
|
||||
password: Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn set_credentials(&mut self, username: &[u8], password: &[u8]) {
|
||||
assert!(username.len() <= 255 && password.len() <= 255);
|
||||
self.authentication = SocksAuthentication::Password;
|
||||
self.username = Vec::from(username);
|
||||
self.password = Vec::from(password);
|
||||
}
|
||||
}
|
450
src/tun2proxy.rs
Normal file
450
src/tun2proxy.rs
Normal file
|
@ -0,0 +1,450 @@
|
|||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::convert::From;
|
||||
use std::io::{Read, Write};
|
||||
use std::net::{IpAddr, SocketAddr, Shutdown};
|
||||
use std::os::unix::io::AsRawFd;
|
||||
|
||||
use mio::{Events, Interest, Poll, Token};
|
||||
use mio::event::Event;
|
||||
use mio::net::{TcpSocket as MioTcp, TcpStream};
|
||||
use mio::unix::SourceFd;
|
||||
use smoltcp::iface::{Interface, InterfaceBuilder, Routes};
|
||||
use smoltcp::phy::{Device, Medium, RxToken, TunTapInterface, TxToken};
|
||||
use smoltcp::socket::{SocketHandle, SocketSet, TcpSocket, TcpSocketBuffer};
|
||||
use smoltcp::time::Instant;
|
||||
use smoltcp::wire::{IpAddress, IpCidr, Ipv4Address, Ipv4Packet, TcpPacket, UdpPacket, Ipv6Packet};
|
||||
use crate::virtdevice::VirtualTunDevice;
|
||||
|
||||
|
||||
#[derive(Hash, Clone, Copy)]
|
||||
pub struct Connection {
|
||||
pub src: std::net::SocketAddr,
|
||||
pub dst: std::net::SocketAddr,
|
||||
pub proto: u8
|
||||
}
|
||||
|
||||
impl Eq for Connection {}
|
||||
|
||||
impl PartialEq<Self> for Connection {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
return other.src == self.src && other.dst == self.dst && other.proto == self.proto;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Debug)]
|
||||
pub(crate) enum IncomingDirection {
|
||||
FromServer,
|
||||
FromClient
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Debug)]
|
||||
pub(crate) enum OutgoingDirection {
|
||||
ToServer,
|
||||
ToClient
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) enum ConnectionEvent<'a> {
|
||||
NewConnection(&'a Connection),
|
||||
ConnectionClosed(&'a Connection)
|
||||
}
|
||||
|
||||
pub(crate) struct DataEvent<'a, T> {
|
||||
pub(crate) direction: T,
|
||||
pub(crate) buffer: &'a [u8]
|
||||
}
|
||||
|
||||
pub(crate) type IncomingDataEvent<'a> = DataEvent<'a, IncomingDirection>;
|
||||
pub(crate) type OutgoingDataEvent<'a> = DataEvent<'a, OutgoingDirection>;
|
||||
|
||||
fn get_transport_info(proto: u8, transport_offset: usize, packet: &[u8]) -> Option<((u16, u16), bool, usize, usize)> {
|
||||
if proto == smoltcp::wire::IpProtocol::Udp.into() {
|
||||
match UdpPacket::new_checked(packet) {
|
||||
Ok(result) => {
|
||||
Some(((result.src_port(), result.dst_port()), false, transport_offset + 8, packet.len() - 8))
|
||||
},
|
||||
Err(_) => None
|
||||
}
|
||||
} else if proto == smoltcp::wire::IpProtocol::Tcp.into() {
|
||||
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
|
||||
}
|
||||
}
|
||||
else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn connection_tuple(frame: &[u8]) -> Option<(Connection, bool, usize, usize)> {
|
||||
match Ipv4Packet::new_checked(frame) {
|
||||
Ok(packet) => {
|
||||
let proto:u8 = packet.protocol().into();
|
||||
|
||||
let mut a: [u8; 4] = Default::default();
|
||||
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);
|
||||
|
||||
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),
|
||||
proto
|
||||
};
|
||||
return Some((connection, first_packet, payload_offset, payload_size));
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
|
||||
}
|
||||
_ => { }
|
||||
}
|
||||
|
||||
match Ipv6Packet::new_checked(frame) {
|
||||
Ok(packet) => {
|
||||
// TODO: Support extension headers.
|
||||
let proto:u8 = packet.next_header().into();
|
||||
|
||||
let mut a: [u8; 16] = Default::default();
|
||||
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);
|
||||
|
||||
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),
|
||||
proto
|
||||
};
|
||||
Some((connection, first_packet, payload_offset, payload_size))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
||||
}
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
struct ConnectionState {
|
||||
smoltcp_handle: SocketHandle,
|
||||
mio_stream: TcpStream,
|
||||
token: Token
|
||||
}
|
||||
|
||||
pub(crate) trait TcpProxy {
|
||||
fn push_data(&mut self, event: IncomingDataEvent<'_>);
|
||||
fn consume_data(&mut self, dir: OutgoingDirection, size: usize);
|
||||
fn peek_data(&mut self, dir: OutgoingDirection) -> OutgoingDataEvent;
|
||||
}
|
||||
|
||||
pub(crate) trait ConnectionManager {
|
||||
fn handles_connection(&self, connection: &Connection) -> bool;
|
||||
fn new_connection(&mut self, connection: &Connection) -> Option<std::boxed::Box<dyn TcpProxy>>;
|
||||
fn close_connection(&mut self, connection: &Connection);
|
||||
fn get_server(&self) -> SocketAddr;
|
||||
}
|
||||
|
||||
pub(crate) struct TunToProxy<'a> {
|
||||
tun: TunTapInterface,
|
||||
poll: Poll,
|
||||
tun_token: Token,
|
||||
udp_token: Token,
|
||||
iface: Interface<'a, VirtualTunDevice>,
|
||||
connections: HashMap<Connection, ConnectionState>,
|
||||
managers: HashMap<Connection, std::boxed::Box<dyn TcpProxy>>,
|
||||
connection_managers: Vec<std::boxed::Box<dyn ConnectionManager>>,
|
||||
next_token: usize,
|
||||
token_to_connection: HashMap<Token, Connection>,
|
||||
socketset: SocketSet<'a>
|
||||
}
|
||||
|
||||
impl<'a> TunToProxy<'a> {
|
||||
|
||||
pub(crate) fn new(interface: &str) -> Self {
|
||||
let tun_token = Token(0);
|
||||
let tun = TunTapInterface::new(interface, Medium::Ip).unwrap();
|
||||
let poll = Poll::new().unwrap();
|
||||
poll.registry().register(&mut SourceFd(&tun.as_raw_fd()), tun_token, Interest::READABLE).unwrap();
|
||||
|
||||
let virt = VirtualTunDevice::new(tun.capabilities());
|
||||
let builder = InterfaceBuilder::new(virt);
|
||||
let ip_addrs = [
|
||||
IpCidr::new(IpAddress::v4(0, 0, 0, 1), 0),
|
||||
];
|
||||
|
||||
let mut routes = Routes::new(BTreeMap::new());
|
||||
routes.add_default_ipv4_route(Ipv4Address::new(0, 0, 0, 1)).unwrap();
|
||||
|
||||
|
||||
let iface = builder.any_ip(true)
|
||||
.ip_addrs(ip_addrs).routes(routes).finalize();
|
||||
|
||||
|
||||
Self {
|
||||
tun,
|
||||
poll,
|
||||
tun_token,
|
||||
udp_token: Token(1),
|
||||
iface,
|
||||
connections: Default::default(),
|
||||
next_token: 2,
|
||||
token_to_connection: Default::default(),
|
||||
socketset: SocketSet::new([]),
|
||||
managers: Default::default(),
|
||||
connection_managers: Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn add_connection_manager(&mut self, manager: Box<dyn ConnectionManager>) {
|
||||
self.connection_managers.push(manager);
|
||||
}
|
||||
|
||||
fn expect_smoltcp_send(&mut self) {
|
||||
self.iface.poll(&mut self.socketset, Instant::now()).unwrap();
|
||||
|
||||
while let Some(vec) = self.iface.device_mut().exfiltrate_packet() {
|
||||
let slice = vec.as_slice();
|
||||
|
||||
// TODO: Actual write. Replace.
|
||||
self.tun.transmit().unwrap().consume(Instant::now(), slice.len(), |buf| {
|
||||
buf[..].clone_from_slice(slice);
|
||||
Ok(())
|
||||
}).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_connection(&mut self, connection: &Connection) {
|
||||
self.managers.remove(connection);
|
||||
let mut connection_state = self.connections.remove(connection).unwrap();
|
||||
self.token_to_connection.remove(&connection_state.token);
|
||||
self.poll.registry().deregister(&mut connection_state.mio_stream).unwrap();
|
||||
println!("[{:?}] CLOSE {} -> {} (TCP)", chrono::offset::Local::now(), connection.src, connection.dst);
|
||||
}
|
||||
|
||||
fn get_connection_manager(&self, connection: &Connection) -> Option<&Box<dyn ConnectionManager>>{
|
||||
for manager in self.connection_managers.iter() {
|
||||
if manager.handles_connection(connection) {
|
||||
return Some(manager);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn tunsocket_read_and_forward(&mut self, connection: &Connection) {
|
||||
if let Some(handler) = self.managers.get_mut(&connection) {
|
||||
let closed = {
|
||||
let conn_info = self.connections.get_mut(&connection).unwrap();
|
||||
let mut socket = self.socketset.get::<TcpSocket>(conn_info.smoltcp_handle);
|
||||
while socket.can_recv() {
|
||||
socket.recv(|data| {
|
||||
let event = IncomingDataEvent {
|
||||
direction: IncomingDirection::FromClient,
|
||||
buffer: data,
|
||||
|
||||
};
|
||||
handler.push_data(event);
|
||||
|
||||
(data.len(), ())
|
||||
}).unwrap();
|
||||
}
|
||||
|
||||
socket.state() == smoltcp::socket::TcpState::CloseWait
|
||||
};
|
||||
|
||||
if closed {
|
||||
let connection_state = self.connections.get_mut(&connection).unwrap();
|
||||
connection_state.mio_stream.shutdown(Shutdown::Both).unwrap();
|
||||
self.remove_connection(&connection);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn receive_tun(&mut self, frame: &mut [u8]) {
|
||||
if let Some((connection, first_packet, _payload_offset, _payload_size)) = connection_tuple(frame) {
|
||||
|
||||
if connection.proto == smoltcp::wire::IpProtocol::Tcp.into() {
|
||||
let cm = self.get_connection_manager(&connection);
|
||||
if !cm.is_some() {
|
||||
return;
|
||||
}
|
||||
let server = cm.unwrap().get_server();
|
||||
if first_packet {
|
||||
let mut socket = TcpSocket::new(TcpSocketBuffer::new(vec![0; 4096]), TcpSocketBuffer::new(vec![0; 4096]));
|
||||
socket.set_ack_delay(None);
|
||||
socket.listen(connection.dst).unwrap();
|
||||
let handle = self.socketset.add(socket);
|
||||
|
||||
let socket = MioTcp::new_v4().unwrap();
|
||||
let client = socket.connect(server).unwrap();
|
||||
|
||||
let token = Token(self.next_token);
|
||||
self.next_token += 1;
|
||||
|
||||
let mut conn = ConnectionState {
|
||||
smoltcp_handle: handle,
|
||||
mio_stream: client,
|
||||
token
|
||||
};
|
||||
|
||||
self.token_to_connection.insert(token, connection);
|
||||
self.poll.registry().register(&mut conn.mio_stream, token, Interest::READABLE | Interest::WRITABLE).unwrap();
|
||||
|
||||
self.connections.insert(connection, conn);
|
||||
|
||||
for manager in self.connection_managers.iter_mut() {
|
||||
if let Some(handler) = manager.new_connection(&connection) {
|
||||
self.managers.insert(connection, handler);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
println!("[{:?}] CONNECT {} -> {} (TCP)", chrono::offset::Local::now(), connection.src, connection.dst);
|
||||
} else if !self.connections.contains_key(&connection) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Inject the packet to advance the smoltcp socket state
|
||||
self.iface.device_mut().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);
|
||||
|
||||
// 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);
|
||||
}
|
||||
else if connection.proto == smoltcp::wire::IpProtocol::Udp.into() {
|
||||
/* // UDP is not yet supported.
|
||||
if payload_offset > frame.len() || payload_offset + payload_offset > frame.len() {
|
||||
return;
|
||||
}
|
||||
let payload = &frame[payload_offset..payload_offset+payload_size]; */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn write_to_server(&mut self, connection: &Connection) {
|
||||
if let Some(handler) = self.managers.get_mut(&connection) {
|
||||
let event = handler.peek_data(OutgoingDirection::ToServer);
|
||||
if event.buffer.len() == 0 {
|
||||
return;
|
||||
}
|
||||
let connection_state = self.connections.get_mut(&connection).unwrap();
|
||||
let result = connection_state.mio_stream.write(event.buffer);
|
||||
match result {
|
||||
Ok(consumed) => {
|
||||
handler.consume_data(OutgoingDirection::ToServer, consumed);
|
||||
}
|
||||
Err(error) if error.kind() != std::io::ErrorKind::WouldBlock => {
|
||||
panic!("Error: {:?}", error);
|
||||
}
|
||||
_ => {
|
||||
// println!("{:?}", result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn write_to_client(&mut self, connection: &Connection) {
|
||||
if let Some(handler) = self.managers.get_mut(&connection) {
|
||||
let event = handler.peek_data(OutgoingDirection::ToClient);
|
||||
let socket = &mut self.socketset.get::<TcpSocket>(self.connections.get(&connection).unwrap().smoltcp_handle);
|
||||
if socket.may_send() {
|
||||
let consumed = socket.send_slice(event.buffer).unwrap();
|
||||
handler.consume_data(OutgoingDirection::ToClient, consumed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn tun_event(&mut self, event: &Event) {
|
||||
if event.is_readable() {
|
||||
while let Some((rx_token, _)) = self.tun.receive() {
|
||||
if let Err(err) = rx_token.consume(Instant::now(), |frame| {
|
||||
self.receive_tun(frame);
|
||||
Ok(())
|
||||
}) {
|
||||
panic!("Error: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn mio_socket_event(&mut self, event: &Event) {
|
||||
let connection = *self.token_to_connection.get(&event.token()).unwrap();
|
||||
|
||||
if event.is_read_closed() {
|
||||
{
|
||||
let mut socket = self.socketset.get::<TcpSocket>(self.connections.get(&connection).unwrap().smoltcp_handle);
|
||||
socket.close();
|
||||
}
|
||||
self.expect_smoltcp_send();
|
||||
self.remove_connection(&connection.clone());
|
||||
return;
|
||||
}
|
||||
|
||||
if event.is_readable() {
|
||||
{
|
||||
let conn = self.managers.get_mut(&connection).unwrap();
|
||||
let state = self.connections.get_mut(&connection).unwrap();
|
||||
|
||||
let mut buf = [0u8; 4096];
|
||||
let read = state.mio_stream.read(&mut buf).unwrap();
|
||||
|
||||
let event = IncomingDataEvent {
|
||||
direction: IncomingDirection::FromServer,
|
||||
buffer: &buf[0..read],
|
||||
|
||||
};
|
||||
conn.push_data(event);
|
||||
}
|
||||
|
||||
// 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.expect_smoltcp_send();
|
||||
self.write_to_client(&connection);
|
||||
self.expect_smoltcp_send();
|
||||
}
|
||||
if event.is_writable() {
|
||||
self.write_to_server(&connection);
|
||||
}
|
||||
}
|
||||
|
||||
fn udp_event(&mut self, _event: &Event) {
|
||||
|
||||
}
|
||||
|
||||
pub(crate) fn run(&mut self) {
|
||||
|
||||
let mut events = Events::with_capacity(1024);
|
||||
|
||||
loop {
|
||||
self.poll.poll(&mut events, None).unwrap();
|
||||
for event in events.iter() {
|
||||
if event.token() == self.tun_token {
|
||||
self.tun_event(event);
|
||||
} else if event.token() == self.udp_token {
|
||||
self.udp_event(event);
|
||||
} else {
|
||||
self.mio_socket_event(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
80
src/virtdevice.rs
Normal file
80
src/virtdevice.rs
Normal file
|
@ -0,0 +1,80 @@
|
|||
use smoltcp::phy;
|
||||
use smoltcp::phy::{Device, DeviceCapabilities};
|
||||
use smoltcp::time::Instant;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct VirtualTunDevice {
|
||||
capabilities: DeviceCapabilities,
|
||||
inbuf: Vec<Vec<u8>>,
|
||||
outbuf: Vec<Vec<u8>>
|
||||
}
|
||||
|
||||
|
||||
impl VirtualTunDevice {
|
||||
pub fn inject_packet(self: &mut Self, buffer: &[u8]) {
|
||||
let vec = Vec::from(buffer);
|
||||
self.inbuf.push(vec);
|
||||
}
|
||||
|
||||
pub fn exfiltrate_packet(self: &mut Self, ) -> Option<Vec<u8>> {
|
||||
self.outbuf.pop()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VirtRxToken {
|
||||
buffer: Vec<u8>,
|
||||
}
|
||||
|
||||
impl phy::RxToken for VirtRxToken {
|
||||
fn consume<R, F>(mut self, _timestamp: Instant, f: F) -> smoltcp::Result<R>
|
||||
where
|
||||
F: FnOnce(&mut [u8]) -> smoltcp::Result<R>,
|
||||
{
|
||||
f(&mut self.buffer[..])
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VirtTxToken<'a>(&'a mut VirtualTunDevice);
|
||||
|
||||
impl<'a> phy::TxToken for VirtTxToken<'a> {
|
||||
fn consume<R, F>(self, _timestamp: Instant, len: usize, f: F) -> smoltcp::Result<R>
|
||||
where
|
||||
F: FnOnce(&mut [u8]) -> smoltcp::Result<R>,
|
||||
{
|
||||
let mut buffer = vec![0; len];
|
||||
let result = f(&mut buffer);
|
||||
self.0.outbuf.push(Vec::from(buffer));
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Device<'a> for VirtualTunDevice {
|
||||
type RxToken = VirtRxToken;
|
||||
type TxToken = VirtTxToken<'a>;
|
||||
|
||||
fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> {
|
||||
if let Some(buffer) = self.inbuf.pop() {
|
||||
let rx = Self::RxToken { buffer };
|
||||
let tx = VirtTxToken(self);
|
||||
return Some((rx, tx));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn transmit(&'a mut self) -> Option<Self::TxToken> {
|
||||
return Some(VirtTxToken(self));
|
||||
}
|
||||
|
||||
fn capabilities(&self) -> DeviceCapabilities {
|
||||
self.capabilities.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> VirtualTunDevice {
|
||||
pub fn new(capabilities: DeviceCapabilities) -> Self {
|
||||
Self {
|
||||
capabilities,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue