Skip to content

Commit

Permalink
Forward Proxy and Bridge integration (#10)
Browse files Browse the repository at this point in the history
attempting to bridge the bin/lib gap
not sure all the generics are helping
use gsoc2023/pt-proxy from arti and get it to compile
building - yay
  • Loading branch information
jmwample authored Mar 7, 2024
1 parent 2a1926f commit b4dcf87
Show file tree
Hide file tree
Showing 29 changed files with 864 additions and 512 deletions.
62 changes: 28 additions & 34 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,22 +44,22 @@ jobs:
- name: Lint
run: cargo clippy --workspace --all-targets --verbose --all-features

bench:
name: Check that benchmarks compile
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@stable
- name: Build u32 bench
env:
RUSTFLAGS: '--cfg curve25519_dalek_bits="32"'
run: cargo build --benches
- name: Build u64 bench
env:
RUSTFLAGS: '--cfg curve25519_dalek_bits="64"'
run: cargo build --benches
- name: Build default (host native) bench
run: cargo build --benches
# bench:
# name: Check that benchmarks compile
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v3
# - uses: dtolnay/rust-toolchain@stable
# - name: Build u32 bench
# env:
# RUSTFLAGS: '--cfg curve25519_dalek_bits="32"'
# run: cargo build --benches
# - name: Build u64 bench
# env:
# RUSTFLAGS: '--cfg curve25519_dalek_bits="64"'
# run: cargo build --benches
# - name: Build default (host native) bench
# run: cargo build --benches

test-stable:
name: Test 32/64 bit stable
Expand All @@ -69,7 +69,11 @@ jobs:
include:
# 32-bit target
- target: i686-unknown-linux-gnu
deps: sudo apt update && sudo apt install gcc-multilib
deps: >
sudo dpkg --add-architecture i386;
sudo touch /etc/apt/sources.list.d/i386-cross-compile-sources.list;
echo "deb [arch=i386] http://ports.ubuntu.com/ focal universe\ndeb [arch=i386] http://ports.ubuntu.com/ focal-updates universe\n" | sudo tee -a /etc/apt/sources.list.d/i386-cross-compile-sources.list;
sudo apt update && sudo apt install gcc-multilib libsqlite3-dev:i386
# 64-bit target
- target: x86_64-unknown-linux-gnu
Expand All @@ -78,15 +82,9 @@ jobs:
- uses: dtolnay/rust-toolchain@stable
- run: rustup target add ${{ matrix.target }}
- run: ${{ matrix.deps }}
- run: cargo test --target ${{ matrix.target }} --workspace --all-targets --no-default-features
- run: cargo test --target ${{ matrix.target }} --workspace --all-targets
- run: cargo test --target ${{ matrix.target }} --workspace --all-targets --all-features
# - name: Test
# run: cargo test --verbose --workspace --all-features
# env:
# CARGO_INCREMENTAL: '0'
# RUSTFLAGS: '-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Cpanic=abort -Zpanic_abort_tests'
# RUSTDOCFLAGS: '-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Cpanic=abort -Zpanic_abort_tests'
# - run: cargo test --target ${{ matrix.target }} --workspace --all-targets
# - run: cargo test --target ${{ matrix.target }} --workspace --all-targets --no-default-features

test-nightly:
name: Test Nightly
Expand Down Expand Up @@ -114,22 +112,18 @@ jobs:
uses: dtolnay/rust-toolchain@nightly

# Build ptrs library
- name: Build library
- name: Build library and bins
run: cargo build

# Build ptrs proxy PoC
- name: Build library
run: cargo build --bin proxy --release

- name: Install cargo-llvm-cov
uses: taiki-e/install-action@cargo-llvm-cov
- name: Generate code coverage
run: cargo llvm-cov --all-features --workspace --codecov --output-path codecov.json
- name: Codecov
uses: codecov/codecov-action@v3
env:
CODECOV_TOKEN: ${{ secrets.PTRS_CODECOV_TOKEN }}
uses: codecov/[email protected]
with:
fail_ci_if_error: true
files: codecov.json
token: ${{ secrets.CODECOV_TOKEN }} # required
verbose: true
fail_ci_if_error: true
slug: jmwample/o7
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ members = [

resolver = "2"


10 changes: 7 additions & 3 deletions crates/obfs4/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ bench = false

[dependencies]
getrandom = "0.2.11"
rand = { version="0.8.5", fratures=["getrandom"]}
rand = { version="0.8.5", features=["getrandom"]}
rand_core = "0.6.4"

digest = { version = "0.10.7", features=["mac"]}
Expand Down Expand Up @@ -64,8 +64,12 @@ cipher = "0.4.4"
zeroize = "1.7.0"
thiserror = "1.0.56"

# [build-dependencies]
# cc = { version = "1.0.83", features = ["parallel"] }
## lyrebird bin only
fast-socks5 = "0.9.1"
tokio-stream = "0.1.14"
tor-linkspec = { version="0.11.1" }
tor-chanmgr = { version="0.14.1", features = ["pt-client"] }
tor-ptmgr = "0.11.0"

[dev-dependencies]
tracing-subscriber = "0.3.18"
Expand Down
167 changes: 93 additions & 74 deletions crates/obfs4/src/bin/fwd_proxy/config.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::handler::{EchoHandler, Handler};
use crate::handler::{EchoHandler, Handler, Socks5Handler};

use std::{convert::TryFrom, default::Default, net, str::FromStr};
use std::{net, sync::Arc};

use anyhow::anyhow;
use clap::{Args, CommandFactory, Parser, Subcommand};
Expand All @@ -21,7 +21,8 @@ pub const DEFAULT_LOG_LEVEL: Level = Level::INFO;

pub enum ProxyConfig {
Entrance(EntranceConfig),
Exit(ExitConfig),
Socks5Exit(ExitConfig),
EchoExit(ExitConfig),
}

impl ProxyConfig {
Expand All @@ -32,13 +33,15 @@ impl ProxyConfig {
) -> Result<(), anyhow::Error> {
match self {
ProxyConfig::Entrance(config) => config.run(close, wait).await,
ProxyConfig::Exit(config) => config.run(close, wait).await,
ProxyConfig::Socks5Exit(config) => {
Arc::new(config).run::<Socks5Handler>(close, wait).await
}
ProxyConfig::EchoExit(config) => Arc::new(config).run::<EchoHandler>(close, wait).await,
}
}
}

pub struct EntranceConfig {
pt: String,
pt_args: Vec<String>,

listen_address: net::SocketAddr,
Expand All @@ -59,8 +62,8 @@ impl EntranceConfig {
let t_name = "obfs4";

loop {
let client = ClientBuilder::from_params(self.pt_args.clone())?.build();
let (in_stream, socket_addr) = listener.accept().await?;
let client = ClientBuilder::from_params(self.pt_args.clone())?.build();
trace!("new tcp connection {socket_addr}");

let mut out_stream = TcpStream::connect(self.remote_address)
Expand Down Expand Up @@ -92,7 +95,6 @@ impl EntranceConfig {
impl Default for EntranceConfig {
fn default() -> Self {
Self {
pt: String::from("plain"),
pt_args: vec![],

listen_address: DEFAULT_LISTEN_ADDRESS.parse().unwrap(),
Expand All @@ -102,27 +104,25 @@ impl Default for EntranceConfig {
}
}

#[allow(unused)] // TODO: parse args and things
pub struct ExitConfig {
pt: String,
pt_args: Vec<String>,
level: Level,

handler: Handler,

// handler: Handler,
listen_address: net::SocketAddr,

level: Level,
}

impl ExitConfig {
pub async fn run(
self,
pub async fn run<H: Handler + Send + Sync + 'static>(
self: Arc<Self>,
close: CancellationToken,
_wait: Sender<()>,
) -> Result<(), anyhow::Error> {
let listener = TcpListener::bind(self.listen_address).await.unwrap();
info!("started server listening on {}", self.listen_address);

let server = Server::getrandom();
let server = Arc::new(Server::getrandom());
println!(
"{}\n{}",
server.client_params(),
Expand All @@ -131,95 +131,114 @@ impl ExitConfig {

let t_name = "obfs4";

let mut sessions = JoinSet::new();
let sessions = &mut JoinSet::new();

let close_c = close.clone();
tokio::spawn(async move {
loop {
tokio::select! {
_ = close_c.cancelled() => {
break
}
r = sessions.join_next() => {
match r {
Some(Err(e)) => {
warn!("handler error: \"{e}\", session closed");
}
_ => {}
}

loop {
tokio::select! {
_ = close_c.cancelled() => {
break
}
r = sessions.join_next() => {
if let Some(Err(e)) = r {
warn!("handler error: \"{e}\", session closed");
}
}
}
sessions.abort_all();
let start = std::time::Instant::now();
while !sessions.is_empty() && start.elapsed().as_millis() < 3000 {
_ = sessions.join_next().await;
}
});
r = listener.accept() => {
let (stream, socket_addr) = match r {
Err(e) => {
warn!("connection listener returned error {e}");
close_c.cancel();
continue
}
Ok(s) => s,
};

loop {
let (stream, socket_addr) = listener.accept().await?;
trace!("new tcp connection {socket_addr}");
debug!("new tcp connection {socket_addr}");

let close_c = close.clone();
let close_c = close.clone();

let stream = match server.wrap(stream).await {
Ok(s) => s,
Err(e) => {
error!("failed to wrap in_stream ->({socket_addr}): {:?}", e);
continue;
}
};
let proxy_stream = match server.wrap(stream).await {
Ok(s) => s,
Err(e) => {
error!("failed to wrap in_stream ->({socket_addr}): {:?}", e);
continue;
}
};

debug!("connection successfully revealed ->{t_name}-[{socket_addr}]");
let handler = self.handler;
sessions.spawn(async move {
handler.handle(stream, close_c).await;
});
debug!("connection successfully opened ->{t_name}-[{socket_addr}]");
sessions.spawn(H::handle(proxy_stream, close_c));
}
}
}

// let (up, dn) = handler.handle(stream, close_c);
sessions.abort_all();
let start = std::time::Instant::now();
while !sessions.is_empty() && start.elapsed().as_millis() < 3000 {
_ = sessions.join_next().await;
}
Ok(())
}
}

impl Default for ExitConfig {
fn default() -> Self {
Self {
pt: String::from("plain"),
pt_args: vec![],
listen_address: DEFAULT_SERVER_ADDRESS.parse().unwrap(),
level: DEFAULT_LOG_LEVEL,
handler: Handler::Echo(EchoHandler),
}
}
}

impl TryFrom<Cli> for ProxyConfig {
impl TryFrom<&Cli> for ProxyConfig {
type Error = anyhow::Error;

fn try_from(cli: Cli) -> Result<Self, Self::Error> {
match cli.command {
fn try_from(cli: &Cli) -> Result<Self, Self::Error> {
match &cli.command {
Some(Commands::Server(args)) => {
let mut config = ExitConfig::default();
if args.debug {
config.level = Level::DEBUG;
} else if args.trace {
config.level = Level::TRACE;
match &*args.backend {
"socks5" => {} // ExitConfig::<B, Socks5Handler>::new(),
"echo" => {} // ExitConfig::<B, EchoHandler>::new(),
_ => return Err(anyhow!("unknown backend")),
}
tracing_subscriber::fmt()
.with_max_level(config.level)
.init();
trace!("{:?}", args);

config.pt = "".to_string();
config.pt_args = vec![];
let level = if args.debug {
Level::DEBUG
} else if args.trace {
Level::TRACE
} else {
Level::ERROR
};

config.listen_address = args.listen_addr.parse()?;
tracing_subscriber::fmt().with_max_level(level).init();
trace!("{:?}", args);

// TODO: parse pt name and arguments.
let pt_args = vec![];

config.handler = Handler::from_str(&args.backend)
.map_err(|e| anyhow!("failed to parse backend: {:?}", e))?;
let listen_address = args.listen_addr.parse()?;

Ok(ProxyConfig::Exit(config))
match &*args.backend {
"socks5" => {
let config = ExitConfig {
pt_args,
listen_address,
level,
};
Ok(ProxyConfig::Socks5Exit(config))
}
"echo" => {
let config = ExitConfig {
pt_args,
listen_address,
level,
};
Ok(ProxyConfig::EchoExit(config))
}
_ => Err(anyhow!("unknown backend")),
}
}
Some(Commands::Client(args)) => {
let mut config = EntranceConfig::default();
Expand All @@ -236,7 +255,7 @@ impl TryFrom<Cli> for ProxyConfig {
config.remote_address = args.remote.parse()?;
config.listen_address = args.listen_addr.parse()?;

config.pt = "".to_string();
// TODO: parse pt name and arguments.
config.pt_args = vec![];

Ok(ProxyConfig::Entrance(config))
Expand Down
Loading

0 comments on commit b4dcf87

Please sign in to comment.