Add forward traffic to another HTTP proxy for the server (only for LocalProtocol::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
This commit is contained in:
parent
58c34ccc41
commit
ce04666b8b
6 changed files with 138 additions and 21 deletions
63
src/main.rs
63
src/main.rs
|
@ -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)]
|
||||
|
@ -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,
|
||||
|
@ -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],
|
||||
|
@ -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);
|
||||
|
||||
|
|
|
@ -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)>;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
@ -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,
|
||||
remote: &Option<RemoteAddr>,
|
||||
) -> anyhow::Result<(Self::Reader, Self::Writer)> {
|
||||
Err(anyhow!("SOCKS5 tunneling is not supported with HTTP proxy"))
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Socks5Reader {
|
||||
|
|
|
@ -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,
|
||||
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
@ -43,4 +46,12 @@ impl TunnelConnector for UdpTunnelConnector<'_> {
|
|||
|
||||
Ok((stream.clone(), stream))
|
||||
}
|
||||
|
||||
async fn connect_with_http_proxy(
|
||||
&self,
|
||||
proxy: &Url,
|
||||
remote: &Option<RemoteAddr>,
|
||||
) -> anyhow::Result<(Self::Reader, Self::Writer)> {
|
||||
Err(anyhow!("UDP tunneling is not supported with HTTP proxy"))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
@ -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)]
|
||||
|
@ -172,28 +173,32 @@ 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(
|
||||
|
|
Loading…
Reference in a new issue