mirror of https://github.com/ospab/ostp.git
Fix STUN bug, improve DNS in TUN, fix config gen, add GHA for clients
This commit is contained in:
parent
10af0ca7a9
commit
7f0afab42a
|
|
@ -0,0 +1,110 @@
|
||||||
|
name: Build GUI Clients
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
branches: [ "main" ]
|
||||||
|
paths:
|
||||||
|
- "ostp-gui/**"
|
||||||
|
- "ostp-flutter/**"
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-windows-gui:
|
||||||
|
name: Build Windows Client (Tauri)
|
||||||
|
runs-on: windows-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 20
|
||||||
|
|
||||||
|
- name: Setup Rust toolchain
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
with:
|
||||||
|
targets: x86_64-pc-windows-msvc
|
||||||
|
|
||||||
|
- name: Cache cargo
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.cargo/registry/index/
|
||||||
|
~/.cargo/registry/cache/
|
||||||
|
~/.cargo/git/db/
|
||||||
|
target/
|
||||||
|
key: cargo-windows-gui-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
|
||||||
|
- name: Download wintun and tun2socks
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
$ProgressPreference = 'SilentlyContinue'
|
||||||
|
|
||||||
|
# Download tun2socks
|
||||||
|
New-Item -ItemType Directory -Force -Path "t2s_tmp"
|
||||||
|
Invoke-WebRequest -Uri "https://github.com/xjasonlyu/tun2socks/releases/download/v2.6.0/tun2socks-windows-amd64.zip" -OutFile "t2s_tmp/t2s.zip"
|
||||||
|
Expand-Archive "t2s_tmp/t2s.zip" -DestinationPath "t2s_tmp/ext" -Force
|
||||||
|
Get-ChildItem "t2s_tmp/ext" -Filter "*.exe" -Recurse | Select-Object -First 1 | Copy-Item -Destination "t2s_tmp/tun2socks-windows-amd64.exe" -Force
|
||||||
|
|
||||||
|
# Download wintun
|
||||||
|
New-Item -ItemType Directory -Force -Path "target/release"
|
||||||
|
Invoke-WebRequest -Uri "https://www.wintun.net/builds/wintun-0.14.1.zip" -OutFile "target/release/wt.zip"
|
||||||
|
Expand-Archive "target/release/wt.zip" -DestinationPath "target/release/wt_tmp" -Force
|
||||||
|
Get-ChildItem "target/release/wt_tmp" -Filter "wintun.dll" -Recurse | Where-Object { $_.FullName -match 'bin[\\/]amd64[\\/]' } | Copy-Item -Destination "target/release/wintun.dll" -Force
|
||||||
|
|
||||||
|
- name: Build Tauri App
|
||||||
|
working-directory: ostp-gui
|
||||||
|
run: |
|
||||||
|
npm install
|
||||||
|
npm run build:installer
|
||||||
|
|
||||||
|
- name: Upload Windows Installer
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: ostp-windows-installer
|
||||||
|
path: ostp-gui/src-tauri/target/release/bundle/nsis/*.exe
|
||||||
|
|
||||||
|
build-android:
|
||||||
|
name: Build Android Client (Flutter)
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Java
|
||||||
|
uses: actions/setup-java@v3
|
||||||
|
with:
|
||||||
|
distribution: 'zulu'
|
||||||
|
java-version: '17'
|
||||||
|
|
||||||
|
- name: Setup Flutter
|
||||||
|
uses: subosito/flutter-action@v2
|
||||||
|
with:
|
||||||
|
flutter-version: '3.19.0'
|
||||||
|
channel: 'stable'
|
||||||
|
|
||||||
|
- name: Setup Rust toolchain
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
with:
|
||||||
|
targets: aarch64-linux-android
|
||||||
|
|
||||||
|
- name: Setup Android NDK
|
||||||
|
uses: nttld/setup-ndk@v1
|
||||||
|
with:
|
||||||
|
ndk-version: r26b
|
||||||
|
|
||||||
|
- name: Install cargo-ndk
|
||||||
|
run: cargo install cargo-ndk
|
||||||
|
|
||||||
|
- name: Build Android APK
|
||||||
|
shell: pwsh
|
||||||
|
working-directory: ostp-flutter
|
||||||
|
run: ./build.ps1
|
||||||
|
|
||||||
|
- name: Upload Android APK
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: ostp-android-apk
|
||||||
|
path: ostp-flutter/ostp-client-release.apk
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
127.0.0.1
|
||||||
|
|
@ -39,22 +39,9 @@ pub struct BridgeMetrics {
|
||||||
pub rtt_ms: portable_atomic::AtomicU32,
|
pub rtt_ms: portable_atomic::AtomicU32,
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn send_datagram(socket: &crate::transport::Transport, frame: &Bytes, webrtc_masquerade: bool) -> std::io::Result<usize> {
|
async fn send_datagram(socket: &crate::transport::Transport, frame: &Bytes, _webrtc_masquerade: bool) -> std::io::Result<usize> {
|
||||||
if webrtc_masquerade {
|
|
||||||
let mut out = bytes::BytesMut::with_capacity(12 + frame.len());
|
|
||||||
// Fake SRTP Header:
|
|
||||||
// [0] 0x80 (Version 2)
|
|
||||||
// [1] 0x60 (Payload Type 96 - dynamic video)
|
|
||||||
// [2..3] Sequence number (dummy 0x1234)
|
|
||||||
// [4..7] Timestamp (dummy)
|
|
||||||
// [8..11] SSRC (dummy)
|
|
||||||
out.extend_from_slice(&[0x80, 0x60, 0x12, 0x34, 0x00, 0x00, 0x00, 0x00, 0x11, 0x22, 0x33, 0x44]);
|
|
||||||
out.extend_from_slice(frame);
|
|
||||||
socket.send(&out.freeze()).await
|
|
||||||
} else {
|
|
||||||
socket.send(frame).await
|
socket.send(frame).await
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
struct SessionState {
|
struct SessionState {
|
||||||
socket: crate::transport::Transport,
|
socket: crate::transport::Transport,
|
||||||
|
|
@ -276,17 +263,13 @@ impl Bridge {
|
||||||
let session_index = sessions.len();
|
let session_index = sessions.len();
|
||||||
let socket_clone = sock.clone();
|
let socket_clone = sock.clone();
|
||||||
let udp_tx_clone = udp_tx.clone();
|
let udp_tx_clone = udp_tx.clone();
|
||||||
let is_webrtc = self.transport_mode == "udp" ;
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let mut buf = vec![0_u8; 65535];
|
let mut buf = vec![0_u8; 65535];
|
||||||
loop {
|
loop {
|
||||||
match socket_clone.recv(&mut buf).await {
|
match socket_clone.recv(&mut buf).await {
|
||||||
Ok(n) => {
|
Ok(n) => {
|
||||||
let inbound = if is_webrtc && n >= 12 && buf[0] == 0x80 {
|
let inbound = Bytes::copy_from_slice(&buf[..n]);
|
||||||
Bytes::copy_from_slice(&buf[12..n])
|
|
||||||
} else {
|
|
||||||
Bytes::copy_from_slice(&buf[..n])
|
|
||||||
};
|
|
||||||
if udp_tx_clone.send((session_index, inbound)).await.is_err() {
|
if udp_tx_clone.send((session_index, inbound)).await.is_err() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -366,17 +349,13 @@ impl Bridge {
|
||||||
let session_index = new_sessions.len();
|
let session_index = new_sessions.len();
|
||||||
let socket_clone = sock.clone();
|
let socket_clone = sock.clone();
|
||||||
let udp_tx_clone = udp_tx.clone();
|
let udp_tx_clone = udp_tx.clone();
|
||||||
let is_webrtc = self.transport_mode == "udp" ;
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let mut buf = vec![0_u8; 65535];
|
let mut buf = vec![0_u8; 65535];
|
||||||
loop {
|
loop {
|
||||||
match socket_clone.recv(&mut buf).await {
|
match socket_clone.recv(&mut buf).await {
|
||||||
Ok(n) => {
|
Ok(n) => {
|
||||||
let inbound = if is_webrtc && n >= 12 && buf[0] == 0x80 {
|
let inbound = Bytes::copy_from_slice(&buf[..n]);
|
||||||
Bytes::copy_from_slice(&buf[12..n])
|
|
||||||
} else {
|
|
||||||
Bytes::copy_from_slice(&buf[..n])
|
|
||||||
};
|
|
||||||
if udp_tx_clone.send((session_index, inbound)).await.is_err() { break; }
|
if udp_tx_clone.send((session_index, inbound)).await.is_err() { break; }
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
|
@ -477,17 +456,13 @@ impl Bridge {
|
||||||
let session_index = new_sessions.len();
|
let session_index = new_sessions.len();
|
||||||
let socket_clone = sock.clone();
|
let socket_clone = sock.clone();
|
||||||
let udp_tx_clone = udp_tx.clone();
|
let udp_tx_clone = udp_tx.clone();
|
||||||
let is_webrtc = self.transport_mode == "udp" ;
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let mut buf = vec![0_u8; 65535];
|
let mut buf = vec![0_u8; 65535];
|
||||||
loop {
|
loop {
|
||||||
match socket_clone.recv(&mut buf).await {
|
match socket_clone.recv(&mut buf).await {
|
||||||
Ok(n) => {
|
Ok(n) => {
|
||||||
let inbound = if is_webrtc && n >= 12 && buf[0] == 0x80 {
|
let inbound = Bytes::copy_from_slice(&buf[..n]);
|
||||||
Bytes::copy_from_slice(&buf[12..n])
|
|
||||||
} else {
|
|
||||||
Bytes::copy_from_slice(&buf[..n])
|
|
||||||
};
|
|
||||||
if udp_tx_clone.send((session_index, inbound)).await.is_err() {
|
if udp_tx_clone.send((session_index, inbound)).await.is_err() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -885,11 +860,7 @@ impl Bridge {
|
||||||
self.metrics.bytes_recv.fetch_add(size as u64, Ordering::Relaxed);
|
self.metrics.bytes_recv.fetch_add(size as u64, Ordering::Relaxed);
|
||||||
tracing::info!("Handshake response received: {} bytes", size);
|
tracing::info!("Handshake response received: {} bytes", size);
|
||||||
|
|
||||||
let inbound = if (self.transport_mode == "udp") && size >= 12 && buf[0] == 0x80 {
|
let inbound = Bytes::copy_from_slice(&buf[..size]);
|
||||||
Bytes::copy_from_slice(&buf[12..size])
|
|
||||||
} else {
|
|
||||||
Bytes::copy_from_slice(&buf[..size])
|
|
||||||
};
|
|
||||||
machine.on_event(OstpEvent::Inbound(inbound))?;
|
machine.on_event(OstpEvent::Inbound(inbound))?;
|
||||||
let rtt_ms = start.elapsed().as_secs_f64() * 1000.0;
|
let rtt_ms = start.elapsed().as_secs_f64() * 1000.0;
|
||||||
tracing::info!("Handshake complete: session={:#010x} rtt={:.1}ms", session_id, rtt_ms);
|
tracing::info!("Handshake complete: session={:#010x} rtt={:.1}ms", session_id, rtt_ms);
|
||||||
|
|
@ -910,7 +881,7 @@ impl Bridge {
|
||||||
self.transport_mode = cfg.transport.mode.clone();
|
self.transport_mode = cfg.transport.mode.clone();
|
||||||
self.stealth_sni = cfg.transport.stealth_sni.clone();
|
self.stealth_sni = cfg.transport.stealth_sni.clone();
|
||||||
self.stealth_port = cfg.transport.stealth_port;
|
self.stealth_port = cfg.transport.stealth_port;
|
||||||
self.reality_enabled = !cfg.reality.pbk.is_empty();
|
self.reality_enabled = cfg.reality.enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn try_connect_transport(
|
async fn try_connect_transport(
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,8 @@ impl Default for TransportConfig {
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||||
pub struct RealityConfig {
|
pub struct RealityConfig {
|
||||||
|
#[serde(default)]
|
||||||
|
pub enabled: bool,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub sni: String,
|
pub sni: String,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
|
@ -207,6 +209,7 @@ struct RawMuxSection {
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
struct RawRealitySection {
|
struct RawRealitySection {
|
||||||
|
enabled: Option<bool>,
|
||||||
sni: Option<String>,
|
sni: Option<String>,
|
||||||
fp: Option<String>,
|
fp: Option<String>,
|
||||||
pbk: Option<String>,
|
pbk: Option<String>,
|
||||||
|
|
@ -260,6 +263,7 @@ impl ClientConfig {
|
||||||
connect_timeout_ms: 15000,
|
connect_timeout_ms: 15000,
|
||||||
},
|
},
|
||||||
reality: RealityConfig {
|
reality: RealityConfig {
|
||||||
|
enabled: raw.reality.as_ref().and_then(|t| t.enabled).unwrap_or(false),
|
||||||
sni: raw.reality.as_ref().and_then(|t| t.sni.clone()).unwrap_or_default(),
|
sni: raw.reality.as_ref().and_then(|t| t.sni.clone()).unwrap_or_default(),
|
||||||
fp: raw.reality.as_ref().and_then(|t| t.fp.clone()).unwrap_or_default(),
|
fp: raw.reality.as_ref().and_then(|t| t.fp.clone()).unwrap_or_default(),
|
||||||
pbk: raw.reality.as_ref().and_then(|t| t.pbk.clone()).unwrap_or_default(),
|
pbk: raw.reality.as_ref().and_then(|t| t.pbk.clone()).unwrap_or_default(),
|
||||||
|
|
|
||||||
|
|
@ -180,11 +180,18 @@ pub async fn run_native_tunnel(
|
||||||
let mut rep = [0u8; 10];
|
let mut rep = [0u8; 10];
|
||||||
if socks.read_exact(&mut rep).await.is_err() || rep[1] != 0 { return; }
|
if socks.read_exact(&mut rep).await.is_err() || rep[1] != 0 { return; }
|
||||||
|
|
||||||
if socks.write_all(&payload).await.is_ok() {
|
let len = payload.len() as u16;
|
||||||
let mut response_buf = [0u8; 4096];
|
let mut dns_req = Vec::with_capacity(2 + payload.len());
|
||||||
if let Ok(n) = socks.read(&mut response_buf).await {
|
dns_req.extend_from_slice(&len.to_be_bytes());
|
||||||
if n > 0 {
|
dns_req.extend_from_slice(&payload);
|
||||||
let _ = tx_clone.lock().await.send((response_buf[..n].to_vec(), dst, src)).await;
|
|
||||||
|
if socks.write_all(&dns_req).await.is_ok() {
|
||||||
|
let mut len_buf = [0u8; 2];
|
||||||
|
if socks.read_exact(&mut len_buf).await.is_ok() {
|
||||||
|
let resp_len = u16::from_be_bytes(len_buf) as usize;
|
||||||
|
let mut response_buf = vec![0u8; resp_len];
|
||||||
|
if socks.read_exact(&mut response_buf).await.is_ok() {
|
||||||
|
let _ = tx_clone.lock().await.send((response_buf, dst, src)).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -428,11 +435,18 @@ pub async fn run_native_tunnel_from_fd(
|
||||||
let mut rep = [0u8; 10];
|
let mut rep = [0u8; 10];
|
||||||
if socks.read_exact(&mut rep).await.is_err() || rep[1] != 0 { return; }
|
if socks.read_exact(&mut rep).await.is_err() || rep[1] != 0 { return; }
|
||||||
|
|
||||||
if socks.write_all(&payload).await.is_ok() {
|
let len = payload.len() as u16;
|
||||||
let mut response_buf = [0u8; 4096];
|
let mut dns_req = Vec::with_capacity(2 + payload.len());
|
||||||
if let Ok(n) = socks.read(&mut response_buf).await {
|
dns_req.extend_from_slice(&len.to_be_bytes());
|
||||||
if n > 0 {
|
dns_req.extend_from_slice(&payload);
|
||||||
let _ = tx_clone.lock().await.send((response_buf[..n].to_vec(), dst, src)).await;
|
|
||||||
|
if socks.write_all(&dns_req).await.is_ok() {
|
||||||
|
let mut len_buf = [0u8; 2];
|
||||||
|
if socks.read_exact(&mut len_buf).await.is_ok() {
|
||||||
|
let resp_len = u16::from_be_bytes(len_buf) as usize;
|
||||||
|
let mut response_buf = vec![0u8; resp_len];
|
||||||
|
if socks.read_exact(&mut response_buf).await.is_ok() {
|
||||||
|
let _ = tx_clone.lock().await.send((response_buf, dst, src)).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -375,11 +375,7 @@ async fn run_server_loop(
|
||||||
loop {
|
loop {
|
||||||
match sock_clone.recv_from(&mut buf).await {
|
match sock_clone.recv_from(&mut buf).await {
|
||||||
Ok((size, peer)) => {
|
Ok((size, peer)) => {
|
||||||
let packet = if size >= 12 && buf[0] == 0x80 {
|
let packet = Bytes::copy_from_slice(&buf[..size]);
|
||||||
Bytes::copy_from_slice(&buf[12..size])
|
|
||||||
} else {
|
|
||||||
Bytes::copy_from_slice(&buf[..size])
|
|
||||||
};
|
|
||||||
if tx.send((packet, peer)).await.is_err() {
|
if tx.send((packet, peer)).await.is_err() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -529,10 +525,7 @@ async fn run_server_loop(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !sent_tcp {
|
if !sent_tcp {
|
||||||
let mut out = bytes::BytesMut::with_capacity(12 + resp.len());
|
let _ = socket.send_to(&resp, peer_addr).await?;
|
||||||
out.extend_from_slice(&[0x80, 0x60, 0x12, 0x34, 0x00, 0x00, 0x00, 0x00, 0x11, 0x22, 0x33, 0x44]);
|
|
||||||
out.extend_from_slice(&resp);
|
|
||||||
let _ = socket.send_to(&out.freeze(), peer_addr).await?;
|
|
||||||
}
|
}
|
||||||
let _ = ui_event_tx.send(UiEvent::Tx { peer: peer_ip, bytes: resp_len });
|
let _ = ui_event_tx.send(UiEvent::Tx { peer: peer_ip, bytes: resp_len });
|
||||||
}
|
}
|
||||||
|
|
@ -617,10 +610,7 @@ async fn run_server_loop(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !sent_tcp {
|
if !sent_tcp {
|
||||||
let mut out = bytes::BytesMut::with_capacity(12 + frame.len());
|
let _ = socket.send_to(&frame, peer_addr).await?;
|
||||||
out.extend_from_slice(&[0x80, 0x60, 0x12, 0x34, 0x00, 0x00, 0x00, 0x00, 0x11, 0x22, 0x33, 0x44]);
|
|
||||||
out.extend_from_slice(&frame);
|
|
||||||
let _ = socket.send_to(&out.freeze(), peer_addr).await?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for sid in dropped_sessions {
|
for sid in dropped_sessions {
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,8 @@ fn parse_ostp_link(link: &str) -> Result<ClientConfig> {
|
||||||
let mut sid = String::new();
|
let mut sid = String::new();
|
||||||
let mut spx = String::new();
|
let mut spx = String::new();
|
||||||
let mut transport_mode = String::from("udp");
|
let mut transport_mode = String::from("udp");
|
||||||
|
let mut tun_enabled = false;
|
||||||
|
let mut tun_dns = None;
|
||||||
|
|
||||||
for (k, v) in parsed.query_pairs() {
|
for (k, v) in parsed.query_pairs() {
|
||||||
match k.as_ref() {
|
match k.as_ref() {
|
||||||
|
|
@ -79,6 +81,8 @@ fn parse_ostp_link(link: &str) -> Result<ClientConfig> {
|
||||||
"sid" => sid = v.into_owned(),
|
"sid" => sid = v.into_owned(),
|
||||||
"spx" => spx = v.into_owned(),
|
"spx" => spx = v.into_owned(),
|
||||||
"type" => transport_mode = v.into_owned(),
|
"type" => transport_mode = v.into_owned(),
|
||||||
|
"tun" => tun_enabled = v == "true",
|
||||||
|
"dns" => tun_dns = Some(v.into_owned()),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -94,12 +98,13 @@ fn parse_ostp_link(link: &str) -> Result<ClientConfig> {
|
||||||
}),
|
}),
|
||||||
socks5_bind: Some("127.0.0.1:1088".to_string()),
|
socks5_bind: Some("127.0.0.1:1088".to_string()),
|
||||||
tun: Some(TunConfig {
|
tun: Some(TunConfig {
|
||||||
enable: false,
|
enable: tun_enabled,
|
||||||
wintun_path: Some("./wintun.dll".to_string()),
|
wintun_path: Some("./wintun.dll".to_string()),
|
||||||
ipv4_address: Some("10.1.0.2/24".to_string()),
|
ipv4_address: Some("10.1.0.2/24".to_string()),
|
||||||
dns: None,
|
dns: tun_dns,
|
||||||
}),
|
}),
|
||||||
reality: Some(RealityConfigRaw {
|
reality: Some(RealityConfigRaw {
|
||||||
|
enabled: true,
|
||||||
sni,
|
sni,
|
||||||
fp,
|
fp,
|
||||||
pbk,
|
pbk,
|
||||||
|
|
@ -338,6 +343,8 @@ struct TunConfig {
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
struct RealityConfigRaw {
|
struct RealityConfigRaw {
|
||||||
|
#[serde(default)]
|
||||||
|
enabled: bool,
|
||||||
sni: String,
|
sni: String,
|
||||||
fp: String,
|
fp: String,
|
||||||
pbk: String,
|
pbk: String,
|
||||||
|
|
@ -601,8 +608,8 @@ async fn run_app() -> Result<()> {
|
||||||
if let Some(ref mode_str) = args.init {
|
if let Some(ref mode_str) = args.init {
|
||||||
let is_server = mode_str == "server";
|
let is_server = mode_str == "server";
|
||||||
let key = generate_secure_key("hex");
|
let key = generate_secure_key("hex");
|
||||||
let content = if is_server {
|
|
||||||
let (priv_key, pub_key, sid) = generate_reality_keys();
|
let (priv_key, pub_key, sid) = generate_reality_keys();
|
||||||
|
let content = if is_server {
|
||||||
format!(r#"{{
|
format!(r#"{{
|
||||||
// OSTP Server Configuration
|
// OSTP Server Configuration
|
||||||
"mode": "server",
|
"mode": "server",
|
||||||
|
|
@ -708,11 +715,12 @@ async fn run_app() -> Result<()> {
|
||||||
|
|
||||||
// Reality (XTLS) / WebRTC Masquerade parameters
|
// Reality (XTLS) / WebRTC Masquerade parameters
|
||||||
"reality": {{
|
"reality": {{
|
||||||
"dest": "www.microsoft.com:443",
|
"enabled": false,
|
||||||
"private_key": "",
|
"sni": "www.microsoft.com",
|
||||||
"pbk": "",
|
"fp": "chrome",
|
||||||
"sid": "",
|
"pbk": "{}",
|
||||||
"sni_list": ["www.microsoft.com"]
|
"sid": "{}",
|
||||||
|
"spx": "/"
|
||||||
}},
|
}},
|
||||||
|
|
||||||
// Transport Mode: "udp" (default WebRTC masquerade) or "uot" (TCP XTLS-Reality)
|
// Transport Mode: "udp" (default WebRTC masquerade) or "uot" (TCP XTLS-Reality)
|
||||||
|
|
@ -727,7 +735,7 @@ async fn run_app() -> Result<()> {
|
||||||
"sessions": 1
|
"sessions": 1
|
||||||
}},
|
}},
|
||||||
"debug": false
|
"debug": false
|
||||||
}}"#, key)
|
}}"#, key, pub_key, sid)
|
||||||
};
|
};
|
||||||
if let Some(parent) = args.config.parent() {
|
if let Some(parent) = args.config.parent() {
|
||||||
if !parent.as_os_str().is_empty() {
|
if !parent.as_os_str().is_empty() {
|
||||||
|
|
@ -1070,6 +1078,7 @@ async fn run_client_directly(client_cfg: ClientConfig) -> Result<()> {
|
||||||
connect_timeout_ms: 5000,
|
connect_timeout_ms: 5000,
|
||||||
},
|
},
|
||||||
reality: ostp_client::config::RealityConfig {
|
reality: ostp_client::config::RealityConfig {
|
||||||
|
enabled: reality_cfg.map(|t| t.enabled).unwrap_or(false),
|
||||||
sni: reality_cfg.map(|t| t.sni.clone()).unwrap_or_default(),
|
sni: reality_cfg.map(|t| t.sni.clone()).unwrap_or_default(),
|
||||||
fp: reality_cfg.map(|t| t.fp.clone()).unwrap_or_default(),
|
fp: reality_cfg.map(|t| t.fp.clone()).unwrap_or_default(),
|
||||||
pbk: reality_cfg.map(|t| t.pbk.clone()).unwrap_or_default(),
|
pbk: reality_cfg.map(|t| t.pbk.clone()).unwrap_or_default(),
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
{
|
||||||
|
// OSTP Server Configuration
|
||||||
|
"mode": "server",
|
||||||
|
"log_level": "info",
|
||||||
|
|
||||||
|
// The address and port the server listens on for incoming OSTP connections.
|
||||||
|
"listen": "0.0.0.0:50000",
|
||||||
|
|
||||||
|
// List of valid keys. Clients must use one of these to connect.
|
||||||
|
"access_keys": [
|
||||||
|
"a1d8795a93553c08b4e89b017a16ca52"
|
||||||
|
],
|
||||||
|
|
||||||
|
// Optional proxy for outbound traffic.
|
||||||
|
"outbound": {
|
||||||
|
"enabled": false,
|
||||||
|
"protocol": "socks5",
|
||||||
|
"address": "127.0.0.1",
|
||||||
|
"port": 9050,
|
||||||
|
// default_action: 'proxy' (all through proxy) or 'direct' (bypass proxy by default).
|
||||||
|
"default_action": "proxy",
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"domain_suffix": [".onion"],
|
||||||
|
"action": "proxy"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
// Web control panel & Management API
|
||||||
|
"api": {
|
||||||
|
"enabled": false,
|
||||||
|
"bind": "0.0.0.0:9090",
|
||||||
|
// Static API token for Relay servers (optional)
|
||||||
|
"token": "",
|
||||||
|
// Secret URL path to hide panel from scanners (e.g. "mySecret123")
|
||||||
|
"webpath": "",
|
||||||
|
// Login credentials for web panel (password stored as SHA256 hash)
|
||||||
|
"username": "",
|
||||||
|
"password_hash": ""
|
||||||
|
},
|
||||||
|
|
||||||
|
// Fallback TCP proxy: unrecognized connections are proxied to a web server (anti-DPI).
|
||||||
|
"fallback": {
|
||||||
|
"enabled": false,
|
||||||
|
"listen": "0.0.0.0:443",
|
||||||
|
// Target web server (e.g., local nginx or caddy)
|
||||||
|
"target": "127.0.0.1:8080"
|
||||||
|
},
|
||||||
|
|
||||||
|
// Reality (XTLS) / UoT Masquerade parameters
|
||||||
|
"reality": {
|
||||||
|
"enabled": false,
|
||||||
|
"dest": "www.microsoft.com:443",
|
||||||
|
"private_key": "6FVg53jUBTt-dJ52F1Zu1RBCcW1gr9K84WdynBb7i80",
|
||||||
|
"pbk": "c9QjERoaqFGoKBd-9ZpNzj51E8B93fcnEQT_cohEk2E",
|
||||||
|
"sid": "960223edfa174fc5",
|
||||||
|
"sni_list": ["www.microsoft.com"]
|
||||||
|
},
|
||||||
|
"debug": false
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue