Skip to content

Commit

Permalink
[#260] Support HTTPS local server with native-tls
Browse files Browse the repository at this point in the history
  • Loading branch information
zonyitoo committed May 31, 2020
1 parent ab41fbc commit 5a1d9ef
Show file tree
Hide file tree
Showing 12 changed files with 367 additions and 62 deletions.
88 changes: 44 additions & 44 deletions Cargo.lock

Large diffs are not rendered by default.

29 changes: 29 additions & 0 deletions src/bin/local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ const AVAILABLE_PROTOCOLS: &[&str] = &[
"socks4",
#[cfg(feature = "local-http")]
"http",
#[cfg(all(
feature = "local-http",
any(feature = "local-http-native-tls", feature = "local-http-rustls")
))]
"https",
#[cfg(feature = "local-tunnel")]
"tunnel",
#[cfg(feature = "local-redir")]
Expand Down Expand Up @@ -122,6 +127,14 @@ fn main() {
);
}

#[cfg(feature = "local-http-native-tls")]
{
app = clap_app!(@app (app)
(@arg TLS_IDENTITY_PATH: --("tls-identity") +takes_value required_if("PROTOCOL", "https") requires[TLS_IDENTITY_PASSWORD] "TLS identity file (PKCS #12) path for HTTPS server")
(@arg TLS_IDENTITY_PASSWORD: --("tls-identity-password") +takes_value required_if("PROTOCOL", "https") requires[TLS_IDENTITY_PATH] "TLS identity file's password for HTTPS server")
);
}

let matches = app.get_matches();
drop(available_ciphers);

Expand All @@ -134,6 +147,11 @@ fn main() {
Some("socks4") => ConfigType::Socks4Local,
#[cfg(feature = "local-http")]
Some("http") => ConfigType::HttpLocal,
#[cfg(all(
feature = "local-http",
any(feature = "local-http-native-tls", feature = "local-http-rustls")
))]
Some("https") => ConfigType::HttpsLocal,
#[cfg(feature = "local-tunnel")]
Some("tunnel") => ConfigType::TunnelLocal,
#[cfg(feature = "local-redir")]
Expand Down Expand Up @@ -269,6 +287,17 @@ fn main() {
}
}

#[cfg(feature = "local-http-native-tls")]
{
if let Some(ipath) = matches.value_of("TLS_IDENTITY_PATH") {
config.tls_identity_path = Some(ipath.into());
}

if let Some(ipwd) = matches.value_of("TLS_IDENTITY_PASSWORD") {
config.tls_identity_password = Some(ipwd.into());
}
}

// DONE READING options

if config.local_addr.is_none() {
Expand Down
28 changes: 28 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,15 @@ pub enum ConfigType {
#[cfg(feature = "local-http")]
HttpLocal,

/// Config for HTTPS local
///
/// Requires `local` configuration
#[cfg(all(
feature = "local-http",
any(feature = "local-http-native-tls", feature = "local-http-rustls")
))]
HttpsLocal,

/// Config for tunnel local
///
/// Requires `local` and `forward` configuration
Expand Down Expand Up @@ -633,6 +642,11 @@ impl ConfigType {
ConfigType::TunnelLocal => true,
#[cfg(feature = "local-http")]
ConfigType::HttpLocal => true,
#[cfg(all(
feature = "local-http",
any(feature = "local-http-native-tls", feature = "local-http-rustls")
))]
ConfigType::HttpsLocal => true,
#[cfg(feature = "local-redir")]
ConfigType::RedirLocal => true,
ConfigType::Server | ConfigType::Manager => false,
Expand All @@ -651,6 +665,11 @@ impl ConfigType {
ConfigType::TunnelLocal => false,
#[cfg(feature = "local-http")]
ConfigType::HttpLocal => false,
#[cfg(all(
feature = "local-http",
any(feature = "local-http-native-tls", feature = "local-http-rustls")
))]
ConfigType::HttpsLocal => false,
#[cfg(feature = "local-redir")]
ConfigType::RedirLocal => false,
ConfigType::Manager => false,
Expand Down Expand Up @@ -958,6 +977,11 @@ pub struct Config {
///
/// Set to `true` if you want to query IPv6 addresses before IPv4
pub ipv6_first: bool,
/// TLS cryptographic identify (X509)
#[cfg(feature = "local-http-native-tls")]
pub tls_identity_path: Option<PathBuf>,
#[cfg(feature = "local-http-native-tls")]
pub tls_identity_password: Option<String>,
}

/// Configuration parsing error kind
Expand Down Expand Up @@ -1048,6 +1072,10 @@ impl Config {
local_dns_addr: None,
remote_dns_addr: None,
ipv6_first: false,
#[cfg(feature = "local-http-native-tls")]
tls_identity_path: None,
#[cfg(feature = "local-http-native-tls")]
tls_identity_password: None,
}
}

Expand Down
7 changes: 7 additions & 0 deletions src/relay/local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,13 @@ pub async fn run(mut config: Config) -> io::Result<()> {
#[cfg(feature = "local-http")]
ConfigType::HttpLocal => true,

// HTTPS must be TCP
#[cfg(all(
feature = "local-http",
any(feature = "local-http-native-tls", feature = "local-http-rustls")
))]
ConfigType::HttpsLocal => true,

// Redir mode controlled by this flag
#[cfg(feature = "local-redir")]
ConfigType::RedirLocal => mode.enable_tcp(),
Expand Down
71 changes: 53 additions & 18 deletions src/relay/tcprelay/http_local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ use pin_project::pin_project;
use tokio::io::{AsyncRead, AsyncWrite};

use crate::{
config::ConfigType,
context::SharedContext,
relay::{
loadbalancing::server::{
Expand Down Expand Up @@ -826,31 +827,65 @@ pub async fn run(context: SharedContext) -> io::Result<()> {
let bind_addr = local_addr.bind_addr(&context).await?;

let bypass_client = Client::builder().build::<_, Body>(DirectConnector::new(context.clone()));
let servers: PingBalancer<ServerScore> = PingBalancer::new(context, ServerType::Tcp).await;
let servers: PingBalancer<ServerScore> = PingBalancer::new(context.clone(), ServerType::Tcp).await;
let servers = Arc::new(servers);

let make_service = make_service_fn(|socket: &AddrStream| {
let client_addr = socket.remote_addr();
let servers = servers.clone();
let bypass_client = bypass_client.clone();
match context.config().config_type {
ConfigType::HttpLocal => {
let make_service = make_service_fn(|socket: &AddrStream| {
let client_addr = socket.remote_addr();
let servers = servers.clone();
let bypass_client = bypass_client.clone();

async move {
Ok::<_, Infallible>(service_fn(move |req: Request<Body>| {
let svr_score = servers.pick_server();
server_dispatch(req, svr_score, client_addr, bypass_client.clone())
}))
}
});

// HTTP Proxy protocol only defined in HTTP 1.x
let server = Server::bind(&bind_addr).http1_only(true).serve(make_service);
info!("shadowsocks HTTP listening on {}", server.local_addr());

async move {
Ok::<_, Infallible>(service_fn(move |req: Request<Body>| {
let svr_score = servers.pick_server();
server_dispatch(req, svr_score, client_addr, bypass_client.clone())
}))
if let Err(err) = server.await {
use std::io::Error;

error!("hyper server exited with error: {}", err);
return Err(Error::new(ErrorKind::Other, err));
}
}
});
#[cfg(any(feature = "local-http-native-tls", feature = "local-http-rustls"))]
ConfigType::HttpsLocal => {
use super::http_tls::{TlsAcceptor, TlsStream};

let make_service = make_service_fn(|socket: &TlsStream| {
let client_addr = socket.remote_addr();
let servers = servers.clone();
let bypass_client = bypass_client.clone();

async move {
Ok::<_, Infallible>(service_fn(move |req: Request<Body>| {
let svr_score = servers.pick_server();
server_dispatch(req, svr_score, client_addr, bypass_client.clone())
}))
}
});

let acceptor = TlsAcceptor::bind(context.config(), &bind_addr)?;
info!("shadowsocks HTTPS listening on {}", acceptor.local_addr());

// HTTP Proxy protocol only defined in HTTP 1.x
let server = Server::bind(&bind_addr).http1_only(true).serve(make_service);
info!("shadowsocks HTTP listening on {}", server.local_addr());
let server = Server::builder(acceptor).http1_only(true).serve(make_service);

if let Err(err) = server.await {
use std::io::Error;
if let Err(err) = server.await {
use std::io::Error;

error!("hyper server exited with error: {}", err);
return Err(Error::new(ErrorKind::Other, err));
error!("hyper server exited with error: {}", err);
return Err(Error::new(ErrorKind::Other, err));
}
}
_ => unreachable!("http_local::run shouldn't used with {:?}", context.config().config_type),
}

Ok(())
Expand Down
15 changes: 15 additions & 0 deletions src/relay/tcprelay/http_tls/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//! TLS support for HTTP local (HTTPS)
//!
//! Choosing TLS library by `local-http-rustls` and `local-http-native-tls`
#[cfg(feature = "local-http-native-tls")]
pub mod native_tls;

#[cfg(feature = "local-http-native-tls")]
pub use self::native_tls::{TlsAcceptor, TlsStream};

#[cfg(feature = "local-http-rustls")]
pub mod rustls;

#[cfg(feature = "local-http-rustls")]
pub use self::rustls::{TlsAcceptor, TlsStream};
Loading

0 comments on commit 5a1d9ef

Please sign in to comment.