mirror of https://github.com/ospab/ostp.git
fix: remove DNS interception on server, fix TUN routing on Windows and Linux
- ostp-server/relay.rs: remove DNS port 53 interception — DNS queries now pass through to the actual DNS server as regular TCP connections - ostp-client/native_handler.rs (Windows): add explicit gateway/32 route via real interface BEFORE setting default route via TUN to prevent loop - ostp-client/native_handler.rs (Linux): properly detect real gateway and add default route via TUN with metric 10 after server IP exclusion - Remove redundant extra DNS host routes from Windows setup script
This commit is contained in:
parent
c607c40240
commit
2c46750687
|
|
@ -54,19 +54,16 @@ pub async fn run_native_tunnel(
|
||||||
let setup_script = format!(
|
let setup_script = format!(
|
||||||
"$remote_ip = '{}'\n\
|
"$remote_ip = '{}'\n\
|
||||||
$exe_path = '{}'\n\
|
$exe_path = '{}'\n\
|
||||||
$route = Get-NetRoute -DestinationPrefix '0.0.0.0/0' | Where-Object {{ $_.InterfaceAlias -notmatch 'tun' -and $_.InterfaceAlias -notmatch 'wintun' }} | Sort-Object RouteMetric | Select-Object -First 1\n\
|
$route = Get-NetRoute -DestinationPrefix '0.0.0.0/0' | Where-Object {{ $_.InterfaceAlias -notmatch 'ostp' -and $_.InterfaceAlias -notmatch 'tun' -and $_.InterfaceAlias -notmatch 'wintun' }} | Sort-Object RouteMetric | Select-Object -First 1\n\
|
||||||
if ($route) {{\n\
|
if ($route) {{\n\
|
||||||
$gw = $route.NextHop\n\
|
$gw = $route.NextHop\n\
|
||||||
$ifIndex = $route.InterfaceIndex\n\
|
$ifIndex = $route.InterfaceIndex\n\
|
||||||
|
# Route server IP and gateway directly via real interface (bypass TUN)\n\
|
||||||
New-NetRoute -DestinationPrefix \"$remote_ip/32\" -NextHop $gw -InterfaceIndex $ifIndex -RouteMetric 1 -ErrorAction SilentlyContinue\n\
|
New-NetRoute -DestinationPrefix \"$remote_ip/32\" -NextHop $gw -InterfaceIndex $ifIndex -RouteMetric 1 -ErrorAction SilentlyContinue\n\
|
||||||
$dns_ips = Get-DnsClientServerAddress -InterfaceIndex $ifIndex | Select-Object -ExpandProperty ServerAddresses\n\
|
if ($gw -ne '0.0.0.0') {{\n\
|
||||||
foreach ($dns in $dns_ips) {{\n\
|
New-NetRoute -DestinationPrefix \"$gw/32\" -NextHop '0.0.0.0' -InterfaceIndex $ifIndex -RouteMetric 1 -ErrorAction SilentlyContinue\n\
|
||||||
if ($dns -match '^\\d+\\.\\d+\\.\\d+\\.\\d+$') {{\n\
|
|
||||||
New-NetRoute -DestinationPrefix \"$dns/32\" -NextHop $gw -InterfaceIndex $ifIndex -RouteMetric 1 -ErrorAction SilentlyContinue\n\
|
|
||||||
}}\n\
|
}}\n\
|
||||||
}}\n\
|
}}\n\
|
||||||
New-NetRoute -DestinationPrefix \"1.1.1.1/32\" -NextHop $gw -InterfaceIndex $ifIndex -RouteMetric 1 -ErrorAction SilentlyContinue\n\
|
|
||||||
}}\n\
|
|
||||||
New-NetFirewallRule -DisplayName 'OSTP Tunnel In' -Direction Inbound -Program $exe_path -Action Allow -Enabled True -ErrorAction SilentlyContinue\n\
|
New-NetFirewallRule -DisplayName 'OSTP Tunnel In' -Direction Inbound -Program $exe_path -Action Allow -Enabled True -ErrorAction SilentlyContinue\n\
|
||||||
New-NetFirewallRule -DisplayName 'OSTP Tunnel Out' -Direction Outbound -Program $exe_path -Action Allow -Enabled True -ErrorAction SilentlyContinue\n\
|
New-NetFirewallRule -DisplayName 'OSTP Tunnel Out' -Direction Outbound -Program $exe_path -Action Allow -Enabled True -ErrorAction SilentlyContinue\n\
|
||||||
netsh interface ipv4 set interface name=\"ostp_tun\" metric=1\n\
|
netsh interface ipv4 set interface name=\"ostp_tun\" metric=1\n\
|
||||||
|
|
@ -91,8 +88,28 @@ pub async fn run_native_tunnel(
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
{
|
{
|
||||||
// Add default route to tun, bypassing server IP
|
// Get real gateway before routing through TUN
|
||||||
let _ = Command::new("ip").args(["route", "add", &format!("{}/32", server_ip_str), "via", "10.1.0.1"]).output();
|
let gw_out = Command::new("ip")
|
||||||
|
.args(["route", "show", "default"])
|
||||||
|
.output()
|
||||||
|
.ok()
|
||||||
|
.and_then(|o| String::from_utf8(o.stdout).ok());
|
||||||
|
|
||||||
|
let real_gw = gw_out.as_deref().and_then(|s| {
|
||||||
|
// "default via 192.168.1.1 dev eth0" -> "192.168.1.1"
|
||||||
|
s.split_whitespace().skip_while(|w| *w != "via").nth(1).map(|s| s.to_string())
|
||||||
|
});
|
||||||
|
let real_dev = gw_out.as_deref().and_then(|s| {
|
||||||
|
s.split_whitespace().skip_while(|w| *w != "dev").nth(1).map(|s| s.to_string())
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add exclusion route for server IP via real gateway (bypass TUN)
|
||||||
|
if let (Some(ref gw), Some(ref dev)) = (&real_gw, &real_dev) {
|
||||||
|
let _ = Command::new("ip").args(["route", "add", &format!("{}/32", server_ip_str), "via", gw, "dev", dev]).output();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add default route through TUN (lower metric to take priority)
|
||||||
|
let _ = Command::new("ip").args(["route", "add", "default", "via", "10.1.0.1", "dev", "ostp_tun", "metric", "10"]).output();
|
||||||
}
|
}
|
||||||
|
|
||||||
let (stack, tcp_runner, udp_socket, tcp_listener) = StackBuilder::default()
|
let (stack, tcp_runner, udp_socket, tcp_listener) = StackBuilder::default()
|
||||||
|
|
@ -236,7 +253,6 @@ pub async fn run_native_tunnel(
|
||||||
let cleanup_script = format!(
|
let cleanup_script = format!(
|
||||||
"$remote_ip = '{}'\n\
|
"$remote_ip = '{}'\n\
|
||||||
Remove-NetRoute -DestinationPrefix \"$remote_ip/32\" -Confirm:$false -ErrorAction SilentlyContinue\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",
|
netsh interface ipv4 set dnsservers name=\"ostp_tun\" source=dhcp 2>$null\n",
|
||||||
server_ip_str
|
server_ip_str
|
||||||
|
|
@ -247,6 +263,13 @@ pub async fn run_native_tunnel(
|
||||||
.output();
|
.output();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
{
|
||||||
|
// Remove default route via TUN and server exclusion route
|
||||||
|
let _ = Command::new("ip").args(["route", "del", "default", "dev", "ostp_tun"]).output();
|
||||||
|
let _ = Command::new("ip").args(["route", "del", &format!("{}/32", server_ip_str)]).output();
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
fn main() { let x: () = netstack_smoltcp::StackBuilder::default().build().unwrap(); }
|
||||||
|
|
@ -554,7 +554,6 @@ async fn run_server_loop(
|
||||||
stream_tx.clone(),
|
stream_tx.clone(),
|
||||||
connect_tx.clone(),
|
connect_tx.clone(),
|
||||||
outbound.clone(),
|
outbound.clone(),
|
||||||
dns_server.clone(),
|
|
||||||
debug,
|
debug,
|
||||||
).await?;
|
).await?;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ use tokio::net::UdpSocket;
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
|
||||||
use crate::dispatcher::Dispatcher;
|
use crate::dispatcher::Dispatcher;
|
||||||
use crate::dns::DnsServer;
|
|
||||||
use crate::outbound::{self, OutboundConfig};
|
use crate::outbound::{self, OutboundConfig};
|
||||||
use crate::{RemoteState, UiEvent};
|
use crate::{RemoteState, UiEvent};
|
||||||
|
|
||||||
|
|
@ -24,53 +23,11 @@ pub async fn handle_relay_message(
|
||||||
stream_tx: mpsc::UnboundedSender<(u32, u16, Vec<u8>)>,
|
stream_tx: mpsc::UnboundedSender<(u32, u16, Vec<u8>)>,
|
||||||
connect_tx: mpsc::UnboundedSender<(u32, u16, String, Result<(tokio::net::tcp::OwnedWriteHalf, mpsc::Sender<()>), String>)>,
|
connect_tx: mpsc::UnboundedSender<(u32, u16, String, Result<(tokio::net::tcp::OwnedWriteHalf, mpsc::Sender<()>), String>)>,
|
||||||
outbound_cfg: Option<OutboundConfig>,
|
outbound_cfg: Option<OutboundConfig>,
|
||||||
dns_server: std::sync::Arc<DnsServer>,
|
|
||||||
debug: bool,
|
debug: bool,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
match RelayMessage::decode(&payload)? {
|
match RelayMessage::decode(&payload)? {
|
||||||
RelayMessage::Connect(target) => {
|
RelayMessage::Connect(target) => {
|
||||||
let _ = ui_event_tx.send(UiEvent::Log(format!("Relay CONNECT start for [{session_id}:{stream_id}] -> {target}")));
|
let _ = ui_event_tx.send(UiEvent::Log(format!("Relay CONNECT start for [{session_id}:{stream_id}] -> {target}")));
|
||||||
|
|
||||||
// ── DNS Interception ────────────────────────────────────────────────
|
|
||||||
// If client is connecting to port 53 (DNS), we handle it locally
|
|
||||||
// instead of opening a real UDP/TCP socket to the destination.
|
|
||||||
//
|
|
||||||
// Protocol flow:
|
|
||||||
// 1. Client sends Connect("8.8.8.8:53") → we reply ConnectOk
|
|
||||||
// 2. Client sends Data(<dns_query_bytes>) → we resolve & reply Data(<dns_response>) + Close
|
|
||||||
if is_dns_target(&target) {
|
|
||||||
let client_ip = peer_addr.ip();
|
|
||||||
let dns_srv = dns_server.clone();
|
|
||||||
let stream_tx_dns = stream_tx.clone();
|
|
||||||
let (cancel_tx, _) = mpsc::channel::<()>(1);
|
|
||||||
|
|
||||||
// Channel: relay.rs Data handler → DNS resolution task
|
|
||||||
let (dns_query_tx, mut dns_query_rx) = mpsc::unbounded_channel::<Bytes>();
|
|
||||||
|
|
||||||
// Spawn task that waits for the DNS query payload and resolves it
|
|
||||||
tokio::spawn(async move {
|
|
||||||
if let Some(query_bytes) = dns_query_rx.recv().await {
|
|
||||||
if let Some(resp_bytes) = dns_srv.resolve(&query_bytes, client_ip).await {
|
|
||||||
let _ = stream_tx_dns.send((session_id, stream_id, resp_bytes));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Always close the stream after responding
|
|
||||||
let _ = stream_tx_dns.send((session_id, stream_id, Vec::new()));
|
|
||||||
});
|
|
||||||
|
|
||||||
// Store as a RemoteState — Data messages will be forwarded to dns_query_tx
|
|
||||||
remotes.insert((session_id, stream_id), RemoteState {
|
|
||||||
data_tx: dns_query_tx,
|
|
||||||
cancel_tx,
|
|
||||||
is_dns: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Tell the client we are ready to receive its DNS query
|
|
||||||
send_relay_to_stream(session_id, stream_id, RelayMessage::ConnectOk, dispatcher, socket, ui_event_tx).await?;
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Normal TCP Connect ──────────────────────────────────────────────
|
|
||||||
let target_clone = target.clone();
|
let target_clone = target.clone();
|
||||||
let connect_tx_clone = connect_tx.clone();
|
let connect_tx_clone = connect_tx.clone();
|
||||||
let stream_tx_clone = stream_tx.clone();
|
let stream_tx_clone = stream_tx.clone();
|
||||||
|
|
@ -136,10 +93,7 @@ pub async fn handle_relay_message(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if the target address is a DNS server (port 53)
|
|
||||||
fn is_dns_target(target: &str) -> bool {
|
|
||||||
target.ends_with(":53")
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn send_relay_to_stream(
|
pub async fn send_relay_to_stream(
|
||||||
session_id: u32,
|
session_id: u32,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue