Skip to content

Commit

Permalink
Add forward traffic to another HTTP proxy for the server (only for Lo…
Browse files Browse the repository at this point in the history
…calProtocol::Tcp) (#326)

* Add forward traffic to another HTTP proxy for the server (only for LocalProtocol::Tcp)

* Update to the newest code:
- Add `connect_with_http_proxy` to `TunnelConnector`

* Remove unnecessary error checks

* Resolve conflict again
  • Loading branch information
CorrM authored Jul 31, 2024
1 parent 58c34cc commit ce04666
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 21 deletions.
63 changes: 62 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,29 @@ struct Server {
/// The ca will be automatically reloaded if it changes
#[arg(long, value_name = "FILE_PATH", verbatim_doc_comment)]
tls_client_ca_certs: Option<PathBuf>,

/// If set, will use this http proxy to connect to the client
#[arg(
short = 'p',
long,
value_name = "USER:PASS@HOST:PORT",
verbatim_doc_comment,
env = "HTTP_PROXY"
)]
http_proxy: Option<String>,

/// If set, will use this login to connect to the http proxy. Override the one from --http-proxy
#[arg(long, value_name = "LOGIN", verbatim_doc_comment, env = "WSTUNNEL_HTTP_PROXY_LOGIN")]
http_proxy_login: Option<String>,

/// If set, will use this password to connect to the http proxy. Override the one from --http-proxy
#[arg(
long,
value_name = "PASSWORD",
verbatim_doc_comment,
env = "WSTUNNEL_HTTP_PROXY_PASSWORD"
)]
http_proxy_password: Option<String>,
}

#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
Expand Down Expand Up @@ -750,7 +773,24 @@ async fn main() -> anyhow::Result<()> {
TransportScheme::from_str(args.remote_addr.scheme()).expect("invalid scheme in server url");
let tls = match transport_scheme {
TransportScheme::Ws | TransportScheme::Http => None,
TransportScheme::Wss | TransportScheme::Https => Some(TlsClientConfig {
TransportScheme::Wss => Some(TlsClientConfig {
tls_connector: Arc::new(RwLock::new(
tls::tls_connector(
args.tls_verify_certificate,
transport_scheme.alpn_protocols(),
!args.tls_sni_disable,
tls_certificate,
tls_key,
)
.expect("Cannot create tls connector"),
)),
tls_sni_override: args.tls_sni_override,
tls_verify_certificate: args.tls_verify_certificate,
tls_sni_disabled: args.tls_sni_disable,
tls_certificate_path: args.tls_certificate.clone(),
tls_key_path: args.tls_private_key.clone(),
}),
TransportScheme::Https => Some(TlsClientConfig {
tls_connector: Arc::new(RwLock::new(
tls::tls_connector(
args.tls_verify_certificate,
Expand Down Expand Up @@ -1136,6 +1176,26 @@ async fn main() -> anyhow::Result<()> {
restriction_cfg
};

let http_proxy = if let Some(proxy) = args.http_proxy {
let mut proxy = if proxy.starts_with("http://") {
Url::parse(&proxy).expect("Invalid http proxy url")
} else {
Url::parse(&format!("http://{}", proxy)).expect("Invalid http proxy url")
};

if let Some(login) = args.http_proxy_login {
proxy.set_username(login.as_str()).expect("Cannot set http proxy login");
}
if let Some(password) = args.http_proxy_password {
proxy
.set_password(Some(password.as_str()))
.expect("Cannot set http proxy password");
}
Some(proxy)
} else {
None
};

let server_config = WsServerConfig {
socket_so_mark: args.socket_so_mark,
bind: args.remote_addr.socket_addrs(|| Some(8080)).unwrap()[0],
Expand All @@ -1151,6 +1211,7 @@ async fn main() -> anyhow::Result<()> {
)
.expect("Cannot create DNS resolver"),
restriction_config: args.restrict_config,
http_proxy,
};
let server = WsServer::new(server_config);

Expand Down
15 changes: 11 additions & 4 deletions src/tunnel/connectors/mod.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
mod sock5;
mod tcp;
mod udp;
use tokio::io::{AsyncRead, AsyncWrite};
use url::Url;

pub use sock5::Socks5TunnelConnector;
pub use tcp::TcpTunnelConnector;
pub use udp::UdpTunnelConnector;

use crate::tunnel::RemoteAddr;
use tokio::io::{AsyncRead, AsyncWrite};

mod sock5;
mod tcp;
mod udp;

pub trait TunnelConnector {
type Reader: AsyncRead + Send + 'static;
type Writer: AsyncWrite + Send + 'static;

async fn connect(&self, remote: &Option<RemoteAddr>) -> anyhow::Result<(Self::Reader, Self::Writer)>;
async fn connect_with_http_proxy(
&self,
proxy: &Url,
remote: &Option<RemoteAddr>,
) -> anyhow::Result<(Self::Reader, Self::Writer)>;
}
9 changes: 9 additions & 0 deletions src/tunnel/connectors/sock5.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use std::time::Duration;
use anyhow::anyhow;
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf};
use url::Url;

use crate::protocols::dns::DnsResolver;
use crate::protocols::udp;
Expand Down Expand Up @@ -61,6 +62,14 @@ impl TunnelConnector for Socks5TunnelConnector<'_> {
_ => Err(anyhow!("Invalid protocol for reverse socks5 {:?}", remote.protocol)),
}
}

async fn connect_with_http_proxy(
&self,
proxy: &Url,

Check warning on line 68 in src/tunnel/connectors/sock5.rs

View workflow job for this annotation

GitHub Actions / Build - MacOS x86_64

unused variable: `proxy`

Check warning on line 68 in src/tunnel/connectors/sock5.rs

View workflow job for this annotation

GitHub Actions / Build - Linux x86_64

unused variable: `proxy`

Check warning on line 68 in src/tunnel/connectors/sock5.rs

View workflow job for this annotation

GitHub Actions / Build - MacOS aarch64

unused variable: `proxy`

Check warning on line 68 in src/tunnel/connectors/sock5.rs

View workflow job for this annotation

GitHub Actions / Build - Freebsd x86

unused variable: `proxy`

Check warning on line 68 in src/tunnel/connectors/sock5.rs

View workflow job for this annotation

GitHub Actions / Build - Freebsd x86_64

unused variable: `proxy`

Check warning on line 68 in src/tunnel/connectors/sock5.rs

View workflow job for this annotation

GitHub Actions / Build - Linux aarch64

unused variable: `proxy`

Check warning on line 68 in src/tunnel/connectors/sock5.rs

View workflow job for this annotation

GitHub Actions / Build - Linux armv7hf

unused variable: `proxy`

Check warning on line 68 in src/tunnel/connectors/sock5.rs

View workflow job for this annotation

GitHub Actions / Build - Android armv7

unused variable: `proxy`

Check warning on line 68 in src/tunnel/connectors/sock5.rs

View workflow job for this annotation

GitHub Actions / Build - Linux x86

unused variable: `proxy`

Check warning on line 68 in src/tunnel/connectors/sock5.rs

View workflow job for this annotation

GitHub Actions / Build - Android aarch64

unused variable: `proxy`

Check warning on line 68 in src/tunnel/connectors/sock5.rs

View workflow job for this annotation

GitHub Actions / Build - Windows x86_64

unused variable: `proxy`

Check warning on line 68 in src/tunnel/connectors/sock5.rs

View workflow job for this annotation

GitHub Actions / Build - Windows x86

unused variable: `proxy`
remote: &Option<RemoteAddr>,

Check warning on line 69 in src/tunnel/connectors/sock5.rs

View workflow job for this annotation

GitHub Actions / Build - MacOS x86_64

unused variable: `remote`

Check warning on line 69 in src/tunnel/connectors/sock5.rs

View workflow job for this annotation

GitHub Actions / Build - Linux x86_64

unused variable: `remote`

Check warning on line 69 in src/tunnel/connectors/sock5.rs

View workflow job for this annotation

GitHub Actions / Build - MacOS aarch64

unused variable: `remote`

Check warning on line 69 in src/tunnel/connectors/sock5.rs

View workflow job for this annotation

GitHub Actions / Build - Freebsd x86

unused variable: `remote`

Check warning on line 69 in src/tunnel/connectors/sock5.rs

View workflow job for this annotation

GitHub Actions / Build - Freebsd x86_64

unused variable: `remote`

Check warning on line 69 in src/tunnel/connectors/sock5.rs

View workflow job for this annotation

GitHub Actions / Build - Linux aarch64

unused variable: `remote`

Check warning on line 69 in src/tunnel/connectors/sock5.rs

View workflow job for this annotation

GitHub Actions / Build - Linux armv7hf

unused variable: `remote`

Check warning on line 69 in src/tunnel/connectors/sock5.rs

View workflow job for this annotation

GitHub Actions / Build - Android armv7

unused variable: `remote`

Check warning on line 69 in src/tunnel/connectors/sock5.rs

View workflow job for this annotation

GitHub Actions / Build - Linux x86

unused variable: `remote`

Check warning on line 69 in src/tunnel/connectors/sock5.rs

View workflow job for this annotation

GitHub Actions / Build - Android aarch64

unused variable: `remote`

Check warning on line 69 in src/tunnel/connectors/sock5.rs

View workflow job for this annotation

GitHub Actions / Build - Windows x86_64

unused variable: `remote`

Check warning on line 69 in src/tunnel/connectors/sock5.rs

View workflow job for this annotation

GitHub Actions / Build - Windows x86

unused variable: `remote`
) -> anyhow::Result<(Self::Reader, Self::Writer)> {
Err(anyhow!("SOCKS5 tunneling is not supported with HTTP proxy"))
}
}

pub enum Socks5Reader {
Expand Down
30 changes: 27 additions & 3 deletions src/tunnel/connectors/tcp.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use std::time::Duration;

use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf};
use url::{Host, Url};

use crate::protocols;
use crate::protocols::dns::DnsResolver;
use crate::tunnel::connectors::TunnelConnector;
use crate::tunnel::RemoteAddr;
use std::time::Duration;
use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf};
use url::Host;

pub struct TcpTunnelConnector<'a> {
host: &'a Host,
Expand Down Expand Up @@ -45,4 +47,26 @@ impl TunnelConnector for TcpTunnelConnector<'_> {
let stream = protocols::tcp::connect(host, port, self.so_mark, self.connect_timeout, self.dns_resolver).await?;
Ok(stream.into_split())
}

async fn connect_with_http_proxy(
&self,
proxy: &Url,
remote: &Option<RemoteAddr>,
) -> anyhow::Result<(Self::Reader, Self::Writer)> {
let (host, port) = match remote {
Some(remote) => (&remote.host, remote.port),
None => (self.host, self.port),
};

let stream = protocols::tcp::connect_with_http_proxy(
proxy,
host,
port,
self.so_mark,
self.connect_timeout,
self.dns_resolver,
)
.await?;
Ok(stream.into_split())
}
}
15 changes: 13 additions & 2 deletions src/tunnel/connectors/udp.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
use std::time::Duration;

use anyhow::anyhow;
use url::{Host, Url};

use crate::protocols;
use crate::protocols::dns::DnsResolver;
use crate::protocols::udp::WsUdpSocket;
use crate::tunnel::connectors::TunnelConnector;
use crate::tunnel::RemoteAddr;
use std::time::Duration;
use url::Host;

pub struct UdpTunnelConnector<'a> {
host: &'a Host,
Expand Down Expand Up @@ -43,4 +46,12 @@ impl TunnelConnector for UdpTunnelConnector<'_> {

Ok((stream.clone(), stream))
}

async fn connect_with_http_proxy(
&self,
proxy: &Url,

Check warning on line 52 in src/tunnel/connectors/udp.rs

View workflow job for this annotation

GitHub Actions / Build - MacOS x86_64

unused variable: `proxy`

Check warning on line 52 in src/tunnel/connectors/udp.rs

View workflow job for this annotation

GitHub Actions / Build - Linux x86_64

unused variable: `proxy`

Check warning on line 52 in src/tunnel/connectors/udp.rs

View workflow job for this annotation

GitHub Actions / Build - MacOS aarch64

unused variable: `proxy`

Check warning on line 52 in src/tunnel/connectors/udp.rs

View workflow job for this annotation

GitHub Actions / Build - Freebsd x86

unused variable: `proxy`

Check warning on line 52 in src/tunnel/connectors/udp.rs

View workflow job for this annotation

GitHub Actions / Build - Freebsd x86_64

unused variable: `proxy`

Check warning on line 52 in src/tunnel/connectors/udp.rs

View workflow job for this annotation

GitHub Actions / Build - Linux aarch64

unused variable: `proxy`

Check warning on line 52 in src/tunnel/connectors/udp.rs

View workflow job for this annotation

GitHub Actions / Build - Linux armv7hf

unused variable: `proxy`

Check warning on line 52 in src/tunnel/connectors/udp.rs

View workflow job for this annotation

GitHub Actions / Build - Android armv7

unused variable: `proxy`

Check warning on line 52 in src/tunnel/connectors/udp.rs

View workflow job for this annotation

GitHub Actions / Build - Linux x86

unused variable: `proxy`

Check warning on line 52 in src/tunnel/connectors/udp.rs

View workflow job for this annotation

GitHub Actions / Build - Android aarch64

unused variable: `proxy`

Check warning on line 52 in src/tunnel/connectors/udp.rs

View workflow job for this annotation

GitHub Actions / Build - Windows x86_64

unused variable: `proxy`

Check warning on line 52 in src/tunnel/connectors/udp.rs

View workflow job for this annotation

GitHub Actions / Build - Windows x86

unused variable: `proxy`
remote: &Option<RemoteAddr>,

Check warning on line 53 in src/tunnel/connectors/udp.rs

View workflow job for this annotation

GitHub Actions / Build - MacOS x86_64

unused variable: `remote`

Check warning on line 53 in src/tunnel/connectors/udp.rs

View workflow job for this annotation

GitHub Actions / Build - Linux x86_64

unused variable: `remote`

Check warning on line 53 in src/tunnel/connectors/udp.rs

View workflow job for this annotation

GitHub Actions / Build - MacOS aarch64

unused variable: `remote`

Check warning on line 53 in src/tunnel/connectors/udp.rs

View workflow job for this annotation

GitHub Actions / Build - Freebsd x86

unused variable: `remote`

Check warning on line 53 in src/tunnel/connectors/udp.rs

View workflow job for this annotation

GitHub Actions / Build - Freebsd x86_64

unused variable: `remote`

Check warning on line 53 in src/tunnel/connectors/udp.rs

View workflow job for this annotation

GitHub Actions / Build - Linux aarch64

unused variable: `remote`

Check warning on line 53 in src/tunnel/connectors/udp.rs

View workflow job for this annotation

GitHub Actions / Build - Linux armv7hf

unused variable: `remote`

Check warning on line 53 in src/tunnel/connectors/udp.rs

View workflow job for this annotation

GitHub Actions / Build - Android armv7

unused variable: `remote`

Check warning on line 53 in src/tunnel/connectors/udp.rs

View workflow job for this annotation

GitHub Actions / Build - Linux x86

unused variable: `remote`

Check warning on line 53 in src/tunnel/connectors/udp.rs

View workflow job for this annotation

GitHub Actions / Build - Android aarch64

unused variable: `remote`

Check warning on line 53 in src/tunnel/connectors/udp.rs

View workflow job for this annotation

GitHub Actions / Build - Windows x86_64

unused variable: `remote`

Check warning on line 53 in src/tunnel/connectors/udp.rs

View workflow job for this annotation

GitHub Actions / Build - Windows x86

unused variable: `remote`
) -> anyhow::Result<(Self::Reader, Self::Writer)> {
Err(anyhow!("UDP tunneling is not supported with HTTP proxy"))
}
}
27 changes: 16 additions & 11 deletions src/tunnel/server/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ use tokio::sync::mpsc;
use tokio_rustls::rustls::pki_types::{CertificateDer, PrivateKeyDer};
use tokio_rustls::TlsAcceptor;
use tracing::{error, info, span, warn, Instrument, Level, Span};
use url::Host;
use url::{Host, Url};

#[derive(Debug)]
pub struct TlsServerConfig {
Expand All @@ -69,6 +69,7 @@ pub struct WsServerConfig {
pub tls: Option<TlsServerConfig>,
pub dns_resolver: DnsResolver,
pub restriction_config: Option<PathBuf>,
pub http_proxy: Option<Url>,
}

#[derive(Clone)]
Expand Down Expand Up @@ -172,37 +173,41 @@ impl WsServer {
) -> anyhow::Result<(RemoteAddr, Pin<Box<dyn AsyncRead + Send>>, Pin<Box<dyn AsyncWrite + Send>>)> {
match remote.protocol {
LocalProtocol::Udp { timeout, .. } => {
let (rx, tx) = UdpTunnelConnector::new(
let connector = UdpTunnelConnector::new(
&remote.host,
remote.port,
self.config.socket_so_mark,
timeout.unwrap_or(Duration::from_secs(10)),
&self.config.dns_resolver,
)
.connect(&None)
.await?;
);
let (rx, tx) = match &self.config.http_proxy {
None => connector.connect(&None).await?,
Some(_) => Err(anyhow!("UDP tunneling is not supported with HTTP proxy"))?,
};

Ok((remote, Box::pin(rx), Box::pin(tx)))
}
LocalProtocol::Tcp { proxy_protocol } => {
let (rx, mut tx) = TcpTunnelConnector::new(
let connector = TcpTunnelConnector::new(
&remote.host,
remote.port,
self.config.socket_so_mark,
Duration::from_secs(10),
&self.config.dns_resolver,
)
.connect(&None)
.await?;
);
let (rx, mut tx) = match &self.config.http_proxy {
None => connector.connect(&None).await?,
Some(proxy_url) => connector.connect_with_http_proxy(proxy_url, &None).await?,
};

if proxy_protocol {
let header = ppp::v2::Builder::with_addresses(
ppp::v2::Version::Two | ppp::v2::Command::Proxy,
ppp::v2::Protocol::Stream,
(client_address, tx.local_addr().unwrap()),
)
.build()
.unwrap();
.build()
.unwrap();
let _ = tx.write_all(&header).await;
}

Expand Down

0 comments on commit ce04666

Please sign in to comment.