Skip to content

Commit

Permalink
multi path (#48)
Browse files Browse the repository at this point in the history
  • Loading branch information
ssrlive authored Mar 12, 2024
1 parent ed7e03f commit 615671a
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 10 deletions.
3 changes: 3 additions & 0 deletions readme-cn.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ overtls -r client -c config.json
注意 `tunnel_path` 配置項,請務必改成你自己獨有的複雜字符串,否則 `GFW` 立馬拿你祭旗。
> `tuunel_path` 選項現在可以是字符串或字符串數組,如 `["/secret-tunnel-path/", "/another-secret-tunnel-path/"]`。
> Overtls 客戶端將選擇第一個使用。在服務端,它將用整個字符串數組来检查傳入請求.
> 爲方便測試,提供了 `disable_tls` 選項以具備停用 `TLS` 的能力;就是說,若該項存在且爲 `true` 時,本軟件將 `明文(plain text)` 傳輸流量;出於安全考慮,正式場合請勿使用。
本示例展示的是最少條目的配置文件,完整的配置文件可以參考 [config.json](config.json)。
4 changes: 4 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,10 @@ The `certfile` and `keyfile` are optional, and the software will become `https`
Note the `tunnel_path` configuration, please make sure to change it to your own unique complex string, otherwise `GFW` will block you immediately.
> The `tunnel_path` option now can be a string or an array of strings, like `["/secret-tunnel-path/", "/another-secret-tunnel-path/"]`.
> Overtls client side will select the first one to use.
> In the server side, it will check the incoming request with the entire array of strings.
> For testing purposes, the `disable_tls` option is provided to have the ability to disable `TLS`; that is, if this option exists and is true, the software will transmit traffic in `plain text`; for security reasons, please do not use it on official occasions.
This example shows the configuration file of the least entry, the complete configuration file can refer to [config.json](config.json).
3 changes: 2 additions & 1 deletion src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,8 @@ pub(crate) async fn create_ws_stream<S: AsyncRead + AsyncWrite + Unpin>(
mut stream: S,
) -> Result<WebSocketStream<S>> {
let client = config.client.as_ref().ok_or("client not exist")?;
let tunnel_path = config.tunnel_path.trim_matches('/');
let err = "tunnel path not exist";
let tunnel_path = config.tunnel_path.extract().first().ok_or(err)?.trim_matches('/');

let b64_dst = dst_addr.as_ref().map(|dst_addr| addess_to_b64str(dst_addr, false));

Expand Down
78 changes: 73 additions & 5 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,80 @@ pub struct Config {
pub remarks: Option<String>,
pub method: Option<String>,
pub password: Option<String>,
pub tunnel_path: String,
pub tunnel_path: TunnelPath,
#[serde(skip)]
pub test_timeout_secs: u64,
#[serde(skip)]
pub is_server: bool,
}

#[derive(Clone, Serialize, Deserialize, Debug)]
#[serde(untagged)]
pub enum TunnelPath {
Single(String),
Multiple(Vec<String>),
}

impl std::fmt::Display for TunnelPath {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
TunnelPath::Single(s) => write!(f, "{}", s),
TunnelPath::Multiple(v) => {
let mut s = String::new();
for (i, item) in v.iter().enumerate() {
if i > 0 {
s.push(',');
}
s.push_str(item);
}
write!(f, "{}", s)
}
}
}
}

impl Default for TunnelPath {
fn default() -> Self {
TunnelPath::Single("/tunnel/".to_string())
}
}

impl TunnelPath {
pub fn is_empty(&self) -> bool {
match self {
TunnelPath::Single(s) => s.is_empty(),
TunnelPath::Multiple(v) => v.is_empty(),
}
}

pub fn standardize(&mut self) {
if self.is_empty() {
*self = TunnelPath::default();
}
match self {
TunnelPath::Single(s) => {
*s = format!("/{}/", s.trim().trim_matches('/'));
}
TunnelPath::Multiple(v) => {
v.iter_mut().for_each(|s| {
*s = s.trim().trim_matches('/').to_string();
if !s.is_empty() {
*s = format!("/{}/", s);
}
});
v.retain(|s| !s.is_empty());
}
}
}

pub fn extract(&self) -> Vec<&str> {
match self {
TunnelPath::Single(s) => vec![s],
TunnelPath::Multiple(v) => v.iter().map(|s| s.as_str()).collect(),
}
}
}

#[derive(Clone, Serialize, Deserialize, Debug, Default)]
pub struct Server {
pub disable_tls: Option<bool>,
Expand Down Expand Up @@ -70,7 +137,7 @@ impl Config {
remarks: None,
method: None,
password: None,
tunnel_path: "/tunnel/".to_string(),
tunnel_path: TunnelPath::default(),
server: None,
client: None,
test_timeout_secs: 5,
Expand Down Expand Up @@ -177,9 +244,9 @@ impl Config {
self.test_timeout_secs = 5;
}
if self.tunnel_path.is_empty() {
self.tunnel_path = "/tunnel/".to_string();
self.tunnel_path = TunnelPath::default();
} else {
self.tunnel_path = format!("/{}/", self.tunnel_path.trim().trim_matches('/'));
self.tunnel_path.standardize();
}

if let Some(server) = &mut self.server {
Expand Down Expand Up @@ -238,7 +305,8 @@ impl Config {
let remarks = crate::base64_encode(remarks.as_bytes(), engine);
let domain = client.server_domain.as_ref().map_or("".to_string(), |d| d.clone());
let domain = crate::base64_encode(domain.as_bytes(), engine);
let tunnel_path = crate::base64_encode(self.tunnel_path.as_bytes(), engine);
let err = "tunnel_path is not set";
let tunnel_path = crate::base64_encode(self.tunnel_path.extract().first().ok_or(err)?.as_bytes(), engine);
let host = &client.server_host;
let port = client.server_port;

Expand Down
10 changes: 6 additions & 4 deletions src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ async fn handle_incoming<S: AsyncRead + AsyncWrite + Unpin>(
return Err(Error::from("empty request"));
}

if !check_uri_path(&buf, &config.tunnel_path)? {
if !check_uri_path(&buf, &config.tunnel_path.extract())? {
return forward_traffic_wrapper(stream, &buf, &config).await;
}

Expand Down Expand Up @@ -158,14 +158,16 @@ where
Ok(())
}

fn check_uri_path(buf: &[u8], path: &str) -> Result<bool> {
fn check_uri_path(buf: &[u8], path: &[&str]) -> Result<bool> {
let mut headers = [httparse::EMPTY_HEADER; 512];
let mut req = httparse::Request::new(&mut headers);
req.parse(buf)?;

if let Some(p) = req.path {
if p == path {
return Ok(true);
for path in path {
if p == *path {
return Ok(true);
}
}
}
Ok(false)
Expand Down

0 comments on commit 615671a

Please sign in to comment.