mirror of https://github.com/ospab/ostp.git
fix: GUI, JNI SDK, and TUN handler audit fixes
ostp-gui: - GUI-01: Config parsing now strips JSONC comments via json_comments crate, matching CLI behavior. Previously failed on any commented config. - GUI-02: stop_tunnel now properly aborts the JoinHandle with a 2s timeout instead of silently dropping it. ostp-jni (Android SDK): - JNI-01: Replaced all .unwrap() calls in JNI functions with safe null_mut fallback. JNI functions must never panic. - JNI-02: Added missing exclusions, multiplex, debug fields to Kotlin SDK Config.toNativeJson(). Without these, serde deserialization on the native side could fail or use wrong defaults. - JNI-03: Replaced shutdown_background() with shutdown_timeout(3s) to allow proper task cleanup and port unbinding. - JNI-04: Updated Kotlin log string matchers to match professionalized messages (Connection established, TUN tunnel established, etc.) TUN handlers: - TUN-01: Windows TUN cleanup guard now resets DNS via netsh. Previously the custom DNS server remained configured after disconnect, causing complete DNS resolution failure. - Unified all remaining [ostp-client] log prefixes to [ostp] across wintun_handler.rs, linux_handler.rs, and proxy.rs.
This commit is contained in:
parent
8eb3fc72cb
commit
31f3fff187
|
|
@ -102,7 +102,7 @@ pub async fn run_linux_tunnel(
|
|||
let server_ip_str = server_ip.to_string();
|
||||
|
||||
if debug {
|
||||
println!("[ostp-client] Resolved server IP: {}", server_ip_str);
|
||||
println!("[ostp] Resolved server IP: {}", server_ip_str);
|
||||
}
|
||||
|
||||
// 3. Detect current default gateway and interface
|
||||
|
|
@ -132,7 +132,7 @@ pub async fn run_linux_tunnel(
|
|||
}
|
||||
|
||||
if debug {
|
||||
println!("[ostp-client] Default route: gateway={} interface={}", default_gw, default_if);
|
||||
println!("[ostp] Default route: gateway={} interface={}", default_gw, default_if);
|
||||
}
|
||||
|
||||
// 4. Setup commands (Using standard /1 routing trick for fail-proof overriding)
|
||||
|
|
@ -150,7 +150,7 @@ pub async fn run_linux_tunnel(
|
|||
);
|
||||
|
||||
if debug {
|
||||
println!("[ostp-client] Executing Linux network config: {}", setup_script);
|
||||
println!("[ostp] Executing Linux network config: {}", setup_script);
|
||||
}
|
||||
|
||||
let out = Command::new("sh")
|
||||
|
|
@ -158,7 +158,7 @@ pub async fn run_linux_tunnel(
|
|||
.output()?;
|
||||
|
||||
if !out.status.success() && debug {
|
||||
println!("[ostp-client] Warning: Setup routing returned: {}", String::from_utf8_lossy(&out.stderr));
|
||||
println!("[ostp] Warning: Setup routing returned: {}", String::from_utf8_lossy(&out.stderr));
|
||||
}
|
||||
|
||||
// 5. Prepare and launch tun2socks
|
||||
|
|
@ -167,7 +167,7 @@ pub async fn run_linux_tunnel(
|
|||
let proxy_url = format!("http://{}", config.local_proxy.bind_addr);
|
||||
|
||||
if debug {
|
||||
println!("[ostp-client] Spawning {} -device ostp_tun -proxy {}", tun2socks_exe.display(), proxy_url);
|
||||
println!("[ostp] Spawning {} -device ostp_tun -proxy {}", tun2socks_exe.display(), proxy_url);
|
||||
}
|
||||
|
||||
let mut child = Command::new(&tun2socks_exe)
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@ pub async fn run_local_socks5_proxy(
|
|||
.with_context(|| format!("failed to bind local HTTP/SOCKS5 proxy at {}", cfg.bind_addr))?;
|
||||
|
||||
if debug {
|
||||
eprintln!("[ostp-client] local HTTP/SOCKS5 proxy listening at {}", cfg.bind_addr);
|
||||
eprintln!("[ostp-client] Windows system proxy: set HTTP proxy to {}. tun2socks: SOCKS5 on same address.", cfg.bind_addr);
|
||||
eprintln!("[ostp] local HTTP/SOCKS5 proxy listening at {}", cfg.bind_addr);
|
||||
eprintln!("[ostp] Windows system proxy: set HTTP proxy to {}. tun2socks: SOCKS5 on same address.", cfg.bind_addr);
|
||||
}
|
||||
|
||||
let matcher = ExclusionMatcher::new(&exclusions);
|
||||
|
|
@ -75,7 +75,7 @@ pub async fn run_local_socks5_proxy(
|
|||
&& !msg.contains("unsupported SOCKS5 command")
|
||||
{
|
||||
if debug {
|
||||
eprintln!("[ostp-client] proxy client error: {err}");
|
||||
eprintln!("[ostp] proxy client error: {err}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -85,7 +85,7 @@ pub async fn run_local_socks5_proxy(
|
|||
if stream_id == 0 {
|
||||
if let ProxyToClientMsg::Close = msg {
|
||||
if debug {
|
||||
eprintln!("[ostp-client] Resetting all active proxy streams on reconnect");
|
||||
eprintln!("[ostp] Resetting all active proxy streams on reconnect");
|
||||
}
|
||||
for (_, tx) in active_streams.drain() {
|
||||
let _ = tx.send(ProxyToClientMsg::Close);
|
||||
|
|
@ -200,7 +200,7 @@ async fn handle_proxy_client(
|
|||
};
|
||||
|
||||
if debug {
|
||||
eprintln!("[ostp-client] proxy CONNECT stream_id={stream_id} target={target}");
|
||||
eprintln!("[ostp] proxy CONNECT stream_id={stream_id} target={target}");
|
||||
}
|
||||
if matcher.should_bypass(&target, connect_timeout).await {
|
||||
return direct_connect_socks5(client, stream_id, &target, close_tx, debug).await;
|
||||
|
|
@ -277,7 +277,7 @@ async fn handle_proxy_client(
|
|||
};
|
||||
|
||||
if debug {
|
||||
eprintln!("[ostp-client] proxy CONNECT stream_id={stream_id} target={target}");
|
||||
eprintln!("[ostp] proxy CONNECT stream_id={stream_id} target={target}");
|
||||
}
|
||||
if matcher.should_bypass(&target, connect_timeout).await {
|
||||
return direct_connect_http(
|
||||
|
|
@ -333,7 +333,7 @@ async fn handle_proxy_client(
|
|||
Ok(0) => {
|
||||
let _ = event_tx.send(ProxyEvent::Close { stream_id }).await;
|
||||
if debug {
|
||||
eprintln!("[ostp-client] proxy CLOSE stream_id={stream_id}");
|
||||
eprintln!("[ostp] proxy CLOSE stream_id={stream_id}");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -346,7 +346,7 @@ async fn handle_proxy_client(
|
|||
Err(_) => {
|
||||
let _ = event_tx.send(ProxyEvent::Close { stream_id }).await;
|
||||
if debug {
|
||||
eprintln!("[ostp-client] proxy CLOSE stream_id={stream_id}");
|
||||
eprintln!("[ostp] proxy CLOSE stream_id={stream_id}");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -513,7 +513,7 @@ async fn direct_connect_socks5(
|
|||
debug: bool,
|
||||
) -> Result<()> {
|
||||
if debug {
|
||||
eprintln!("[ostp-client] proxy BYPASS stream_id={stream_id} target={target}");
|
||||
eprintln!("[ostp] proxy BYPASS stream_id={stream_id} target={target}");
|
||||
}
|
||||
let mut remote = TcpStream::connect(target).await
|
||||
.with_context(|| format!("direct connect failed: {target}"))?;
|
||||
|
|
@ -534,7 +534,7 @@ async fn direct_connect_http(
|
|||
debug: bool,
|
||||
) -> Result<()> {
|
||||
if debug {
|
||||
eprintln!("[ostp-client] proxy BYPASS stream_id={stream_id} target={target}");
|
||||
eprintln!("[ostp] proxy BYPASS stream_id={stream_id} target={target}");
|
||||
}
|
||||
let mut remote = TcpStream::connect(target).await
|
||||
.with_context(|| format!("direct connect failed: {target}"))?;
|
||||
|
|
|
|||
|
|
@ -24,7 +24,8 @@ pub async fn run_wintun_tunnel(
|
|||
"$remote_ip = '{}'\n\
|
||||
Remove-NetRoute -DestinationPrefix \"$remote_ip/32\" -Confirm:$false -ErrorAction SilentlyContinue\n\
|
||||
Remove-NetRoute -DestinationPrefix \"1.1.1.1/32\" -Confirm:$false -ErrorAction SilentlyContinue\n\
|
||||
Remove-NetFirewallRule -DisplayName 'OSTP Tunnel*' -ErrorAction SilentlyContinue\n",
|
||||
Remove-NetFirewallRule -DisplayName 'OSTP Tunnel*' -ErrorAction SilentlyContinue\n\
|
||||
netsh interface ipv4 set dnsservers name=\"ostp_tun\" source=dhcp 2>$null\n",
|
||||
self.server_ip_str
|
||||
);
|
||||
let _ = Command::new("powershell")
|
||||
|
|
@ -64,12 +65,12 @@ pub async fn run_wintun_tunnel(
|
|||
let server_ip_str = server_ip.to_string();
|
||||
|
||||
if debug {
|
||||
println!("[ostp-client] Resolved server IP: {}", server_ip_str);
|
||||
println!("[ostp] Resolved server IP: {}", server_ip_str);
|
||||
}
|
||||
|
||||
// 3. Run PowerShell script to configure system routes
|
||||
if debug {
|
||||
println!("[ostp-client] Configuring system routes...");
|
||||
println!("[ostp] Configuring system routes...");
|
||||
}
|
||||
|
||||
let current_exe = std::env::current_exe()?.to_string_lossy().into_owned();
|
||||
|
|
@ -102,7 +103,7 @@ pub async fn run_wintun_tunnel(
|
|||
.output()?;
|
||||
|
||||
if !out.status.success() && debug {
|
||||
println!("[ostp-client] Warning: Setup routing returned: {}", String::from_utf8_lossy(&out.stderr));
|
||||
println!("[ostp] Warning: Setup routing returned: {}", String::from_utf8_lossy(&out.stderr));
|
||||
}
|
||||
|
||||
// 4. Prepare and launch tun2socks.exe in the background
|
||||
|
|
@ -111,7 +112,7 @@ pub async fn run_wintun_tunnel(
|
|||
let proxy_url = format!("http://{}", config.local_proxy.bind_addr);
|
||||
|
||||
if debug {
|
||||
println!("[ostp-client] Starting tun2socks (proxy={})", proxy_url);
|
||||
println!("[ostp] Starting tun2socks (proxy={})", proxy_url);
|
||||
}
|
||||
|
||||
// Spawning buffer to allow local proxy listener to finish binding to local address
|
||||
|
|
@ -139,7 +140,7 @@ pub async fn run_wintun_tunnel(
|
|||
tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
|
||||
|
||||
if debug {
|
||||
println!("[ostp-client] Applying network configuration...");
|
||||
println!("[ostp] Applying network configuration...");
|
||||
}
|
||||
|
||||
let mut net_setup = String::from("\
|
||||
|
|
@ -150,7 +151,7 @@ pub async fn run_wintun_tunnel(
|
|||
if let Some(ref dns) = config.dns_server {
|
||||
if !dns.is_empty() {
|
||||
if debug {
|
||||
println!("[ostp-client] DNS server: {}", dns);
|
||||
println!("[ostp] DNS server: {}", dns);
|
||||
}
|
||||
net_setup.push_str(&format!("netsh interface ipv4 set dnsservers name=\"ostp_tun\" static {} primary\n", dns));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,4 +26,5 @@ tokio = { version = "1", features = ["full"] }
|
|||
anyhow = "1"
|
||||
ostp-client = { path = "../../ostp-client" }
|
||||
portable-atomic = "1"
|
||||
json_comments = "0.2"
|
||||
|
||||
|
|
|
|||
|
|
@ -206,15 +206,18 @@ async fn get_config() -> Result<String, String> {
|
|||
"debug": false
|
||||
}"#.into());
|
||||
}
|
||||
std::fs::read_to_string(&path).map_err(|e| format!("Read error: {}", e))
|
||||
std::fs::read_to_string(&path)
|
||||
.map_err(|e| format!("Failed to read config: {}", e))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn save_config(json_content: String) -> Result<bool, String> {
|
||||
let _parsed: UnifiedConfig = serde_json::from_str(&json_content)
|
||||
.map_err(|e| format!("Invalid OSTP config JSON: {}", e))?;
|
||||
// Strip JSONC comments before validation
|
||||
let mut stripped = json_comments::StripComments::new(json_content.as_bytes());
|
||||
let _parsed: UnifiedConfig = serde_json::from_reader(&mut stripped)
|
||||
.map_err(|e| format!("Invalid configuration: {}", e))?;
|
||||
let path = get_config_path();
|
||||
std::fs::write(path, json_content).map_err(|e| format!("Write error: {}", e))?;
|
||||
std::fs::write(path, json_content).map_err(|e| format!("Failed to write config: {}", e))?;
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
|
|
@ -260,7 +263,12 @@ async fn stop_tunnel(state: tauri::State<'_, AppState>) -> Result<bool, String>
|
|||
None => {}
|
||||
Some(TunnelHandle::InProcess(mut s)) => {
|
||||
if let Some(tx) = s.shutdown_tx.take() { let _ = tx.send(true); }
|
||||
drop(s.handle);
|
||||
s.handle.abort();
|
||||
// Brief wait for cleanup
|
||||
let _ = tokio::time::timeout(
|
||||
std::time::Duration::from_secs(2),
|
||||
s.handle,
|
||||
).await;
|
||||
}
|
||||
Some(TunnelHandle::Helper(h)) => {
|
||||
let _ = h.cmd_tx.send("{\"cmd\":\"stop\"}\n".to_string()).await;
|
||||
|
|
@ -284,7 +292,9 @@ async fn start_tunnel(state: tauri::State<'_, AppState>) -> Result<bool, String>
|
|||
|
||||
let path = get_config_path();
|
||||
let content = std::fs::read_to_string(&path).map_err(|e| e.to_string())?;
|
||||
let unified: UnifiedConfig = serde_json::from_str(&content).map_err(|e| format!("Config parse error: {}", e))?;
|
||||
let mut stripped = json_comments::StripComments::new(content.as_bytes());
|
||||
let unified: UnifiedConfig = serde_json::from_reader(&mut stripped)
|
||||
.map_err(|e| format!("Config parse error: {}", e))?;
|
||||
|
||||
let client_cfg = match unified.mode {
|
||||
AppMode::Client(c) => c,
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ class OstpClientSdk private constructor(private val context: Context) {
|
|||
fun toNativeJson(): String {
|
||||
return JSONObject().apply {
|
||||
put("mode", mode)
|
||||
put("debug", false)
|
||||
put("ostp", JSONObject().apply {
|
||||
put("server_addr", server)
|
||||
put("local_bind_addr", "0.0.0.0:0")
|
||||
|
|
@ -90,6 +91,15 @@ class OstpClientSdk private constructor(private val context: Context) {
|
|||
put("username", turnUsername)
|
||||
put("access_key", turnPassword)
|
||||
})
|
||||
put("exclusions", JSONObject().apply {
|
||||
put("domains", org.json.JSONArray())
|
||||
put("ips", org.json.JSONArray())
|
||||
put("processes", org.json.JSONArray())
|
||||
})
|
||||
put("multiplex", JSONObject().apply {
|
||||
put("enabled", false)
|
||||
put("sessions", 1)
|
||||
})
|
||||
}.toString()
|
||||
}
|
||||
}
|
||||
|
|
@ -223,13 +233,13 @@ class OstpClientSdk private constructor(private val context: Context) {
|
|||
emitLog(line)
|
||||
// Detect state transitions from log content
|
||||
when {
|
||||
line.contains("Bridge connection established") ||
|
||||
line.contains("TUN Tunnel established") -> {
|
||||
line.contains("Connection established") ||
|
||||
line.contains("TUN tunnel established") -> {
|
||||
wasConnected = true
|
||||
}
|
||||
line.contains("Bridge stopped") ||
|
||||
line.contains("Tunnel stopped") ||
|
||||
line.contains("Handshake failed") -> {
|
||||
line.contains("TUN tunnel stopped") ||
|
||||
line.contains("Connection failed") -> {
|
||||
wasConnected = false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -158,7 +158,7 @@ pub extern "system" fn Java_net_ostp_client_OstpClientSdk_stopClient(
|
|||
}
|
||||
|
||||
if let Some(rt) = state.runtime.take() {
|
||||
rt.shutdown_background();
|
||||
rt.shutdown_timeout(std::time::Duration::from_secs(3));
|
||||
}
|
||||
|
||||
state.metrics = None;
|
||||
|
|
@ -173,16 +173,25 @@ pub extern "system" fn Java_net_ostp_client_OstpClientSdk_getMetrics(
|
|||
) -> jstring {
|
||||
let state = match STATE.lock() {
|
||||
Ok(s) => s,
|
||||
Err(_) => return env.new_string("{}").unwrap().into_raw(),
|
||||
Err(_) => return match env.new_string("{}") {
|
||||
Ok(s) => s.into_raw(),
|
||||
Err(_) => std::ptr::null_mut(),
|
||||
},
|
||||
};
|
||||
|
||||
if let Some(m) = &state.metrics {
|
||||
let sent = m.bytes_sent.load(Ordering::Relaxed);
|
||||
let recv = m.bytes_recv.load(Ordering::Relaxed);
|
||||
let json = format!(r#"{{"bytes_sent": {}, "bytes_recv": {}}}"#, sent, recv);
|
||||
env.new_string(json).unwrap().into_raw()
|
||||
match env.new_string(json) {
|
||||
Ok(s) => s.into_raw(),
|
||||
Err(_) => std::ptr::null_mut(),
|
||||
}
|
||||
} else {
|
||||
env.new_string(r#"{"bytes_sent": 0, "bytes_recv": 0}"#).unwrap().into_raw()
|
||||
match env.new_string(r#"{"bytes_sent": 0, "bytes_recv": 0}"#) {
|
||||
Ok(s) => s.into_raw(),
|
||||
Err(_) => std::ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -201,5 +210,8 @@ pub extern "system" fn Java_net_ostp_client_OstpClientSdk_getLogs(
|
|||
Err(_) => "[]".to_string(),
|
||||
};
|
||||
|
||||
env.new_string(json).unwrap().into_raw()
|
||||
match env.new_string(json) {
|
||||
Ok(s) => s.into_raw(),
|
||||
Err(_) => std::ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue