Skip to content

Commit

Permalink
Bump version 0.2.15
Browse files Browse the repository at this point in the history
  • Loading branch information
ssrlive committed Mar 24, 2024
1 parent 9fb39cd commit 6d2f862
Show file tree
Hide file tree
Showing 6 changed files with 206 additions and 35 deletions.
8 changes: 4 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "overtls"
version = "0.2.14"
version = "0.2.15"
edition = "2021"
license = "MIT"
description = "A simple proxy tunnel, minimalist tool for bypassing the GFW."
Expand All @@ -12,7 +12,7 @@ crate-type = ["staticlib", "cdylib", "lib"]
[dependencies]
async-shared-timeout = "0.2"
base64 = "0.22"
bytes = "1.5"
bytes = "1.6"
chrono = "0.4"
clap = { version = "4.5", features = ["derive"] }
ctrlc2 = { version = "3.5", features = ["tokio", "termination"] }
Expand All @@ -26,7 +26,7 @@ http = "1.1"
httparse = "1.8"
log = { version = "0.4", features = ["std"] }
moka = { version = "0.12", default-features = false, features = ["future"] }
reqwest = { version = "0.11", default-features = false, features = [
reqwest = { version = "0.12", default-features = false, features = [
"rustls-tls",
"json",
] }
Expand All @@ -37,7 +37,7 @@ serde_json = "1.0"
socks5-impl = "0.5"
thiserror = "1.0"
tokio = { version = "1.36", features = ["full"] }
tokio-rustls = "0.25"
tokio-rustls = { version = "0.26", default-features = false }
tokio-tungstenite = { version = "0.21", features = ["rustls-tls-webpki-roots"] }
tokio-util = "0.7"
trust-dns-proto = "0.23"
Expand Down
12 changes: 10 additions & 2 deletions cbindgen.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
[export]
include = ["over_tls_client_run", "over_tls_client_stop", "overtls_set_log_callback"]
exclude = ["Java_com_github_shadowsocks_bg_OverTlsWrapper_runClient", "Java_com_github_shadowsocks_bg_OverTlsWrapper_stopClient"]
include = [
"over_tls_client_run",
"over_tls_client_run_with_ssr_url",
"over_tls_client_stop",
"overtls_set_log_callback",
]
exclude = [
"Java_com_github_shadowsocks_bg_OverTlsWrapper_runClient",
"Java_com_github_shadowsocks_bg_OverTlsWrapper_stopClient",
]
101 changes: 77 additions & 24 deletions src/api.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#![cfg(not(target_os = "android"))]

use crate::{
config::Config,
error::{Error, Result},
ArgVerbosity,
};
Expand Down Expand Up @@ -28,6 +29,9 @@ static EXITING_FLAG: std::sync::Mutex<Option<crate::CancellationToken>> = std::s
/// # Safety
///
/// Run the overtls client with config file.
/// The callback function will be called when the client is listening on a port.
/// It should be thread-safe and will be called with the port number and should be called only once.
///
#[no_mangle]
pub unsafe extern "C" fn over_tls_client_run(
config_path: *const c_char,
Expand All @@ -39,45 +43,94 @@ pub unsafe extern "C" fn over_tls_client_run(
if let Err(err) = log::set_boxed_logger(Box::<crate::dump_logger::DumpLogger>::default()) {
log::info!("failed to set logger, error={:?}", err);
}
_over_tls_client_run(config_path, callback, ctx)
let config_path = std::ffi::CStr::from_ptr(config_path).to_str();
if let Err(err) = config_path {
log::error!("invalid config path, error={:?}", err);
return -1;
}
let config_path = config_path.unwrap();

let config = Config::from_config_file(config_path);
if let Err(err) = config {
log::error!("failed to load config, error={:?}", err);
return -2;
}
let mut config = config.unwrap();

if let Err(err) = config.check_correctness(false) {
log::error!("invalid config, error={:?}", err);
return -3;
}
if let Err(err) = _over_tls_client_run(config, callback, ctx) {
log::error!("failed to run client, error={:?}", err);
return -4;
}
0
}

unsafe fn _over_tls_client_run(
config_path: *const c_char,
/// # Safety
///
/// Run the overtls client with SSR URL.
/// The callback function will be called when the client is listening on a port.
/// It should be thread-safe and will be called with the port number and should be called only once.
///
#[no_mangle]
pub unsafe extern "C" fn over_tls_client_run_with_ssr_url(
url: *const c_char,
verbosity: ArgVerbosity,
callback: Option<unsafe extern "C" fn(c_int, *mut c_void)>,
ctx: *mut c_void,
) -> c_int {
log::set_max_level(verbosity.into());
if let Err(err) = log::set_boxed_logger(Box::<crate::dump_logger::DumpLogger>::default()) {
log::info!("failed to set logger, error={:?}", err);
}
let url = std::ffi::CStr::from_ptr(url).to_str();
if let Err(err) = url {
log::error!("invalid config path, error={:?}", err);
return -1;
}
let url = url.unwrap();

let config = Config::from_ssr_url(url);
if let Err(err) = config {
log::error!("failed to load config, error={:?}", err);
return -2;
}
let mut config = config.unwrap();

if let Err(err) = config.check_correctness(false) {
log::error!("invalid config, error={:?}", err);
return -3;
}
if let Err(err) = _over_tls_client_run(config, callback, ctx) {
log::error!("failed to run client, error={:?}", err);
return -4;
}
0
}

fn _over_tls_client_run(config: Config, callback: Option<unsafe extern "C" fn(c_int, *mut c_void)>, ctx: *mut c_void) -> Result<()> {
let shutdown_token = crate::CancellationToken::new();
if let Ok(mut lock) = EXITING_FLAG.lock() {
if lock.is_some() {
log::error!("tun2proxy already started");
return -1;
return Err("tun2proxy already started".into());
}
*lock = Some(shutdown_token.clone());
}

let ccb = CCallback(callback, ctx);

let block = || -> Result<()> {
let config_path = std::ffi::CStr::from_ptr(config_path).to_str()?;

let cb = |addr: SocketAddr| unsafe {
ccb.call(addr.port() as _);
};

let mut config = crate::config::Config::from_config_file(config_path)?;
config.check_correctness(false)?;
let rt = tokio::runtime::Builder::new_multi_thread().enable_all().build()?;
rt.block_on(async {
crate::client::run_client(&config, shutdown_token, Some(cb)).await?;
Ok::<(), Error>(())
})
let cb = |addr: SocketAddr| unsafe {
ccb.call(addr.port() as _);
};
if let Err(error) = block() {
log::error!("failed to run client, error={:?}", error);
return -1;
}
0

let rt = tokio::runtime::Builder::new_multi_thread().enable_all().build()?;
rt.block_on(async {
crate::client::run_client(&config, shutdown_token, Some(cb)).await?;
Ok::<(), Error>(())
})?;
Ok(())
}

/// # Safety
Expand Down
107 changes: 107 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,88 @@ impl Config {
Ok(config)
}

/// load from `ssr://...` style url
pub fn from_ssr_url(url: &str) -> Result<Self> {
let engine = crate::Base64Engine::UrlSafeNoPad;
let url = url.trim_start_matches("ssr://");
let url = crate::base64_decode(url, engine)?;
let url = String::from_utf8(url)?;
// split string by `/?`
let mut parts = url.split("/?");

// split first part by `:` and collect to vector
let mut parts0 = parts.next().ok_or("url is invalid")?.split(':').collect::<Vec<&str>>();
// check if parts length is less than 6
if parts0.len() < 6 {
return Err("url is invalid".into());
}
let host = parts0.remove(0);
let port = parts0.remove(0);
let protocol = parts0.remove(0);
let method = parts0.remove(0); // none is default
let obfs = parts0.remove(0);
let password = String::from_utf8(crate::base64_decode(parts0.remove(0), engine)?)?;

if method != "none" {
return Err("method is not none".into());
}
if obfs != "plain" {
return Err("obfs is not plain".into());
}
if protocol != "origin" {
return Err("protocol is not origin".into());
}
let port = port.parse::<u16>()?;

// split second part by `&` and collect to vector
let parts1 = parts.next().ok_or("url is invalid")?.split('&').collect::<Vec<&str>>();
// for each element in parts1, split by `=` and collect to a hashmap
let mut map = std::collections::HashMap::new();
for part in parts1 {
let mut kv = part.split('=');
let k = kv.next().ok_or("url is invalid")?;
let v = kv.next().ok_or("url is invalid")?;
map.insert(k, v);
}

let ot_enable = map.get("ot_enable").map_or("0".to_string(), |r| r.to_string());
if ot_enable != "1" {
return Err("ot_enable is not 1".into());
}
let remarks = map.get("remarks").and_then(|r| match crate::base64_decode(r, engine) {
Ok(decoded) => match String::from_utf8(decoded) {
Ok(string) => Some(string),
Err(_) => None,
},
Err(_) => None,
});
let ot_domain = map.get("ot_domain").and_then(|r| match crate::base64_decode(r, engine) {
Ok(decoded) => match String::from_utf8(decoded) {
Ok(string) => Some(string),
Err(_) => None,
},
Err(_) => None,
});
let ot_path = map.get("ot_path").ok_or("ot_path is not set")?;
let ot_path = String::from_utf8(crate::base64_decode(ot_path, engine)?)?;

let client = Client {
server_host: host.to_string(),
server_port: port,
server_domain: ot_domain,
..Client::default()
};

let mut config = Config::new();
config.password = Some(password);
config.method = Some(method.to_string());
config.remarks = remarks;
config.tunnel_path = TunnelPath::Single(ot_path);
config.client = Some(client);

Ok(config)
}

pub fn generate_ssr_qrcode(&self) -> Result<String> {
let client = self.client.as_ref().ok_or(Error::from("client is not set"))?;
let engine = crate::Base64Engine::UrlSafeNoPad;
Expand All @@ -319,3 +401,28 @@ impl Config {
Ok(format!("ssr://{}", crate::base64_encode(url.as_bytes(), engine)))
}
}

#[test]
fn test_config() {
let mut config = Config::new();
config.tunnel_path = TunnelPath::Single("/tunnel/".to_string());
config.remarks = Some("remarks".to_string());
config.method = Some("none".to_string());
config.password = Some("password".to_string());

let mut client = Client::default();
client.server_host = "baidu.com".to_string();
client.server_port = 443;
client.listen_host = "127.0.0.1".to_string();
client.listen_port = 0;
// client.server_domain = Some("baidu.com".to_string());
config.client = Some(client);

config.check_correctness(false).unwrap();

let qrcode = config.generate_ssr_qrcode().unwrap();
println!("{:?}", qrcode);

let config = Config::from_ssr_url(&qrcode).unwrap();
println!("{:?}", config);
}
11 changes: 7 additions & 4 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,6 @@ pub enum Error {
#[error("reqwest::Error {0}")]
Reqwest(#[from] reqwest::Error),

#[error("rustls::error::Error {0}")]
Rustls(#[from] rustls::Error),

#[error("rustls::pki_types::InvalidDnsNameError {0}")]
InvalidDnsName(#[from] rustls::pki_types::InvalidDnsNameError),

Expand All @@ -52,13 +49,19 @@ pub enum Error {
#[error("jni::errors::Error {0}")]
Jni(#[from] jni::errors::Error),

#[cfg(target_family = "unix")]
#[cfg(unix)]
#[error("daemonize::Error {0}")]
Daemonize(#[from] daemonize::Error),

#[error("std::str::Utf8Error {0}")]
Utf8(#[from] std::str::Utf8Error),

#[error("FromUtf8Error {0}")]
FromUtf8(#[from] std::string::FromUtf8Error),

#[error("ParseIntError {0}")]
FromParseIntError(#[from] std::num::ParseIntError),

#[error("socks5_impl::Error {0}")]
Socks5(#[from] socks5_impl::Error),

Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub(crate) mod udprelay;
pub(crate) mod webapi;
pub(crate) mod weirduri;

pub use api::{over_tls_client_run, over_tls_client_stop};
pub use api::{over_tls_client_run, over_tls_client_run_with_ssr_url, over_tls_client_stop};
use base64_wrapper::{base64_decode, base64_encode, Base64Engine};
use bytes::BytesMut;
pub use cmdopt::{ArgVerbosity, CmdOpt, Role};
Expand Down

0 comments on commit 6d2f862

Please sign in to comment.