mirror of https://github.com/ospab/ostp.git
Fix memory leaks, hang issues, gui helper token vulns, and log spam
This commit is contained in:
parent
feaac0c713
commit
04761fb6a3
|
|
@ -8,7 +8,9 @@
|
|||

|
||||

|
||||
|
||||
**OSTP** (Ospab Stealth Transport Protocol) is a high-performance, censorship-resistant zero-signature transport protocol. It implements a custom, reliable ARQ transport over UDP, as well as a UoT (UDP-over-TCP) mode. Every byte on the wire — including packet headers — is cryptographically indistinguishable from random noise, making it highly resistant to Deep Packet Inspection (DPI), active probing, and statistical traffic analysis.
|
||||
> A fast, custom encrypted transport protocol written in Rust.
|
||||
|
||||
**OSTP** (Ospab Stealth Transport Protocol) is a high-performance transport protocol. It implements a custom ARQ transport over UDP, as well as a UoT (UDP-over-TCP) mode. Every byte on the wire — including packet headers — is cryptographically indistinguishable from random noise, making it highly resistant to Deep Packet Inspection (DPI).
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,9 @@
|
|||

|
||||

|
||||
|
||||
**OSTP** (Ospab Stealth Transport Protocol) — высокопроизводительный, устойчивый к цензуре zero-signature транспортный протокол. Реализует собственный надёжный ARQ-транспорт поверх UDP, а также режим UoT (UDP-over-TCP). Каждый байт, включая заголовки пакетов, криптографически неотличим от случайного шума. Полностью устойчив к Deep Packet Inspection (DPI), активному зондированию и статистическому анализу трафика.
|
||||
> Быстрый кастомный зашифрованный транспортный протокол на Rust.
|
||||
|
||||
**OSTP** (Ospab Stealth Transport Protocol) — кастомный транспортный протокол. Реализует собственный ARQ-транспорт поверх UDP, а также режим UoT (UDP-over-TCP). Каждый байт, включая заголовки пакетов, криптографически неотличим от случайного шума, что делает его устойчивым к системам глубокого анализа трафика (DPI).
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,15 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
||||
<defs>
|
||||
<linearGradient id="g2" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#111827" />
|
||||
<stop offset="100%" stop-color="#374151" />
|
||||
</linearGradient>
|
||||
<linearGradient id="g2_path" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#3B82F6" />
|
||||
<stop offset="100%" stop-color="#14B8A6" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect width="512" height="512" rx="120" fill="url(#g2)" />
|
||||
<path d="M144 256c0-61.9 50.1-112 112-112s112 50.1 112 112-50.1 112-112 112S144 317.9 144 256zm-48 0c0 88.4 71.6 160 160 160s160-71.6 160-160S344.4 96 256 96 96 167.6 96 256z" fill="url(#g2_path)"/>
|
||||
<circle cx="256" cy="256" r="40" fill="#F59E0B" />
|
||||
</svg>
|
||||
|
|
@ -34,9 +34,9 @@ use crate::{
|
|||
Runner,
|
||||
};
|
||||
|
||||
// NOTE: Default buffer could contain 20 AEAD packets
|
||||
const DEFAULT_TCP_SEND_BUFFER_SIZE: u32 = 0x3FFF * 20;
|
||||
const DEFAULT_TCP_RECV_BUFFER_SIZE: u32 = 0x3FFF * 20;
|
||||
// Reduced buffer sizes to 16KB to prevent excessive memory overhead (was 0x3FFF * 20 = 327KB per buffer)
|
||||
const DEFAULT_TCP_SEND_BUFFER_SIZE: u32 = 16384;
|
||||
const DEFAULT_TCP_RECV_BUFFER_SIZE: u32 = 16384;
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
enum TcpSocketState {
|
||||
|
|
@ -542,7 +542,7 @@ impl AsyncWrite for TcpStream {
|
|||
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
|
||||
let mut control = self.control.lock();
|
||||
|
||||
if matches!(control.send_state, TcpSocketState::Closed) {
|
||||
if matches!(control.send_state, TcpSocketState::Closed | TcpSocketState::Closing) {
|
||||
return Ok(()).into();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -255,17 +255,17 @@ pub async fn run_native_tunnel(
|
|||
if !should_bypass {
|
||||
if let Some(proc_name) = crate::tunnel::process_lookup::get_process_name_from_port(local.port()) {
|
||||
if debug {
|
||||
tracing::info!("TUN TCP lookup: port {} -> process {}", local.port(), proc_name);
|
||||
tracing::debug!("TUN TCP lookup: port {} -> process {}", local.port(), proc_name);
|
||||
}
|
||||
if matcher.match_process(&proc_name) {
|
||||
if debug {
|
||||
tracing::info!("TUN TCP BYPASS (Process match): {} → {remote}", proc_name);
|
||||
tracing::debug!("TUN TCP BYPASS (Process match): {} → {remote}", proc_name);
|
||||
}
|
||||
should_bypass = true;
|
||||
}
|
||||
} else {
|
||||
if debug {
|
||||
tracing::info!("TUN TCP lookup: port {} -> no process found", local.port());
|
||||
tracing::debug!("TUN TCP lookup: port {} -> no process found", local.port());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -665,9 +665,7 @@ async fn handle_proxy_client(
|
|||
).await;
|
||||
}
|
||||
|
||||
if true {
|
||||
tracing::info!("proxy CONNECT stream_id={stream_id} target={target}");
|
||||
}
|
||||
tracing::debug!("proxy CONNECT stream_id={stream_id} target={target}");
|
||||
let target_host = if let Some((host, _)) = split_host_port(&target) { host } else { target.clone() };
|
||||
let target_port = match split_host_port(&target) { Some((_, p)) => p, None => 0 };
|
||||
if matcher.should_bypass_target(&target_host, target_port, connect_timeout).await {
|
||||
|
|
|
|||
|
|
@ -50,17 +50,17 @@ pub async fn run_udp_nat(
|
|||
if !should_bypass {
|
||||
if let Some(proc_name) = crate::tunnel::process_lookup::get_process_name_from_port_udp(src.port()) {
|
||||
if debug {
|
||||
tracing::info!("TUN UDP lookup: port {} -> process {}", src.port(), proc_name);
|
||||
tracing::debug!("TUN UDP lookup: port {} -> process {}", src.port(), proc_name);
|
||||
}
|
||||
if matcher_guard.match_process(&proc_name) {
|
||||
should_bypass = true;
|
||||
if debug {
|
||||
tracing::info!("TUN UDP BYPASS (Process match): {} ({} → {})", proc_name, src, dst);
|
||||
tracing::debug!("TUN UDP BYPASS (Process match): {} ({} → {})", proc_name, src, dst);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if debug {
|
||||
tracing::info!("TUN UDP lookup: port {} -> no process found", src.port());
|
||||
tracing::debug!("TUN UDP lookup: port {} -> no process found", src.port());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,10 @@ android {
|
|||
targetSdk = flutter.targetSdkVersion
|
||||
versionCode = flutter.versionCode
|
||||
versionName = flutter.versionName
|
||||
|
||||
ndk {
|
||||
abiFilters += listOf("armeabi-v7a", "arm64-v8a", "x86_64")
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
|
||||
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE"/>
|
||||
|
|
@ -10,6 +11,7 @@
|
|||
android:label="ostp_client"
|
||||
android:name="${applicationName}"
|
||||
android:icon="@mipmap/launcher_icon"
|
||||
android:roundIcon="@mipmap/launcher_icon_round"
|
||||
android:extractNativeLibs="true">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
|
|
@ -45,6 +47,7 @@
|
|||
<service
|
||||
android:name=".OstpVpnService"
|
||||
android:permission="android.permission.BIND_VPN_SERVICE"
|
||||
android:foregroundServiceType="connectedDevice"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="android.net.VpnService"/>
|
||||
|
|
|
|||
|
|
@ -133,6 +133,6 @@ class MainActivity : FlutterActivity() {
|
|||
if (pendingConfigJson != null) {
|
||||
intent.putExtra("configJson", pendingConfigJson)
|
||||
}
|
||||
startService(intent)
|
||||
androidx.core.content.ContextCompat.startForegroundService(this, intent)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import java.io.IOException
|
|||
import androidx.annotation.Keep
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.core.app.ServiceCompat
|
||||
|
||||
@Keep
|
||||
class OstpVpnService : VpnService() {
|
||||
|
|
@ -56,7 +57,7 @@ class OstpVpnService : VpnService() {
|
|||
if (action == "START") {
|
||||
val configJson = intent.getStringExtra("configJson") ?: return START_NOT_STICKY
|
||||
// Launch foreground immediately so Android doesn't kill us
|
||||
startForeground(NOTIF_ID, buildNotification(connecting = true))
|
||||
ServiceCompat.startForeground(this, NOTIF_ID, buildNotification(connecting = true), ServiceInfo.FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE)
|
||||
startVpn(configJson)
|
||||
} else if (action == "STOP") {
|
||||
stopVpn()
|
||||
|
|
@ -271,6 +272,9 @@ class OstpVpnService : VpnService() {
|
|||
|
||||
} catch (e: Throwable) {
|
||||
Log.e("OstpVpnService", "Error starting VPN", e)
|
||||
android.os.Handler(android.os.Looper.getMainLooper()).post {
|
||||
android.widget.Toast.makeText(applicationContext, "VPN Error: ${e.message}", android.widget.Toast.LENGTH_LONG).show()
|
||||
}
|
||||
stopVpn()
|
||||
}
|
||||
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#08080F</color>
|
||||
</resources>
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#fff</color>
|
||||
</resources>
|
||||
|
|
@ -1,2 +1,2 @@
|
|||
org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
|
||||
org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=1G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
|
||||
android.useAndroidX=true
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -9,6 +9,10 @@ import 'package:mobile_scanner/mobile_scanner.dart';
|
|||
import 'app_routing_screen.dart';
|
||||
import 'logs_screen.dart';
|
||||
import 'qr_scanner_screen.dart';
|
||||
import 'package:qr_flutter/qr_flutter.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
|
||||
class SettingsScreen extends StatefulWidget {
|
||||
final SharedPreferences prefs;
|
||||
|
|
@ -39,7 +43,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||
String _tunStack = 'ostp'; // 'system' | 'ostp'
|
||||
bool _muxEnabled = false;
|
||||
late TextEditingController _muxSessionsCtrl;
|
||||
|
||||
bool _isCheckingUpdates = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
|
@ -174,6 +178,11 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||
onPressed: () => Navigator.pop(context),
|
||||
),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.share_rounded),
|
||||
tooltip: 'Share Config',
|
||||
onPressed: _showShareModal,
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.qr_code_scanner_rounded),
|
||||
onPressed: () async {
|
||||
|
|
@ -487,10 +496,190 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 16),
|
||||
|
||||
InkWell(
|
||||
onTap: _isCheckingUpdates ? null : _checkForUpdates,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withOpacity(0.02),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(color: Colors.white.withOpacity(0.05)),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.system_update_rounded, color: Colors.white70, size: 24),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Check for Updates',
|
||||
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16, color: Colors.white),
|
||||
),
|
||||
SizedBox(height: 4),
|
||||
Text(
|
||||
_isCheckingUpdates ? 'Checking...' : 'Check latest release on GitHub',
|
||||
style: TextStyle(fontSize: 13, color: Colors.white54),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (_isCheckingUpdates)
|
||||
const SizedBox(
|
||||
width: 16,
|
||||
height: 16,
|
||||
child: CircularProgressIndicator(strokeWidth: 2, color: Colors.white54),
|
||||
)
|
||||
else
|
||||
const Icon(Icons.arrow_forward_ios_rounded, color: Colors.white54, size: 16),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 40),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String _generateShareUrl() {
|
||||
final host = _serverCtrl.text.trim();
|
||||
final key = Uri.encodeComponent(_keyCtrl.text.trim());
|
||||
if (host.isEmpty || key.isEmpty) return '';
|
||||
|
||||
final queryParams = <String>[];
|
||||
if (_stealthSniCtrl.text.trim().isNotEmpty) {
|
||||
queryParams.add('sni=${Uri.encodeComponent(_stealthSniCtrl.text.trim())}');
|
||||
}
|
||||
if (_pbkCtrl.text.trim().isNotEmpty) {
|
||||
queryParams.add('pbk=${Uri.encodeComponent(_pbkCtrl.text.trim())}');
|
||||
}
|
||||
if (_sidCtrl.text.trim().isNotEmpty) {
|
||||
queryParams.add('sid=${Uri.encodeComponent(_sidCtrl.text.trim())}');
|
||||
}
|
||||
if (_wss) {
|
||||
queryParams.add('wss=true');
|
||||
}
|
||||
if (_transportMode != 'udp') {
|
||||
queryParams.add('type=$_transportMode');
|
||||
}
|
||||
|
||||
final queryString = queryParams.isEmpty ? '' : '?${queryParams.join('&')}';
|
||||
return 'ostp://$key@$host$queryString';
|
||||
}
|
||||
|
||||
void _showShareModal() {
|
||||
final url = _generateShareUrl();
|
||||
if (url.isEmpty) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Server Address and Access Key are required to share.')));
|
||||
return;
|
||||
}
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
|
||||
title: const Text('Share Config', textAlign: TextAlign.center),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: QrImageView(
|
||||
data: url,
|
||||
version: QrVersions.auto,
|
||||
size: 200.0,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
ElevatedButton.icon(
|
||||
onPressed: () {
|
||||
Clipboard.setData(ClipboardData(text: url));
|
||||
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Copied to clipboard')));
|
||||
Navigator.pop(context);
|
||||
},
|
||||
icon: const Icon(Icons.copy_rounded, color: Colors.white),
|
||||
label: const Text('Copy Link', style: TextStyle(color: Colors.white)),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Text('Close'),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _checkForUpdates() async {
|
||||
if (_isCheckingUpdates) return;
|
||||
setState(() { _isCheckingUpdates = true; });
|
||||
try {
|
||||
final packageInfo = await PackageInfo.fromPlatform();
|
||||
final currentVersion = packageInfo.version;
|
||||
|
||||
final response = await http.get(Uri.parse('https://api.github.com/repos/ospab/ostp/releases/latest'));
|
||||
if (response.statusCode == 200) {
|
||||
final data = json.decode(response.body);
|
||||
final latestVersion = (data['tag_name'] as String).replaceAll('v', '');
|
||||
|
||||
final hasUpdate = latestVersion != currentVersion;
|
||||
|
||||
if (!mounted) return;
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||
title: Text(hasUpdate ? 'Update Available!' : 'Up to Date'),
|
||||
content: Text(hasUpdate
|
||||
? 'A new version ($latestVersion) is available on GitHub. You are currently running version $currentVersion.'
|
||||
: 'You are running the latest version ($currentVersion).'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Text('Close'),
|
||||
),
|
||||
if (hasUpdate)
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
final url = Uri.parse(data['html_url'] ?? 'https://github.com/ospab/ostp/releases/latest');
|
||||
launchUrl(url, mode: LaunchMode.externalApplication);
|
||||
},
|
||||
child: const Text('Download'),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
throw Exception('HTTP ${response.statusCode}');
|
||||
}
|
||||
} catch (e) {
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Error checking updates: $e')));
|
||||
} finally {
|
||||
if (mounted) setState(() { _isCheckingUpdates = false; });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -97,6 +97,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
ffi_leak_tracker:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: ffi_leak_tracker
|
||||
sha256: "4093d4ef9ca06ffe2786e73bfb25e22aa92112b9bb4ec941f11e3e6b61489a97"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.2"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -136,6 +144,22 @@ packages:
|
|||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
http:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: http
|
||||
sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.6.0"
|
||||
http_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_parser
|
||||
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.1.2"
|
||||
image:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -224,6 +248,22 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.2.3"
|
||||
package_info_plus:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: package_info_plus
|
||||
sha256: "4bf625947f6c7713ee242296a682e23e44823c09cf9d79e4f1238923c92db852"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.1.0"
|
||||
package_info_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: package_info_plus_platform_interface
|
||||
sha256: db762cb2f4f25ee60fb6359773861b0f199e00b90d237bd85a76a1e806b46ef4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.1.0"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -288,6 +328,22 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.5.0"
|
||||
qr:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: qr
|
||||
sha256: "5a1d2586170e172b8a8c8470bbbffd5eb0cd38a66c0d77155ea138d3af3a4445"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
qr_flutter:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: qr_flutter
|
||||
sha256: "5095f0fc6e3f71d08adef8feccc8cea4f12eec18a2e31c2e8d82cb6019f4b097"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.1.0"
|
||||
screen_retriever:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -453,6 +509,78 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.2"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: typed_data
|
||||
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
url_launcher:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: url_launcher
|
||||
sha256: f6a7e5c4835bb4e3026a04793a4199ca2d14c739ec378fdfe23fc8075d0439f8
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.3.2"
|
||||
url_launcher_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_android
|
||||
sha256: "17bc677f0b301615530dd1d67e0a9828cafa2d0b6b6eae4cd3679b7eac4a273c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.3.30"
|
||||
url_launcher_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_ios
|
||||
sha256: "580fe5dfb51671ae38191d316e027f6b76272b026370708c2d898799750a02b0"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.4.1"
|
||||
url_launcher_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_linux
|
||||
sha256: d5e14138b3bc193a0f63c10a53c94b91d399df0512b1f29b94a043db7482384a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.2"
|
||||
url_launcher_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_macos
|
||||
sha256: "368adf46f71ad3c21b8f06614adb38346f193f3a59ba8fe9a2fd74133070ba18"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.5"
|
||||
url_launcher_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_platform_interface
|
||||
sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.2"
|
||||
url_launcher_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_web
|
||||
sha256: "85c81589622fbc87c1c683aaea164d3604a7777495a79d91e39ffcdec39ddb34"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.3"
|
||||
url_launcher_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_windows
|
||||
sha256: "712c70ab1b99744ff066053cbe3e80c73332b38d46e5e945c98689b2e66fc15f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.5"
|
||||
vector_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -477,6 +605,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
win32:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: win32
|
||||
sha256: ba6f4bba816c8d7e3c1580e170f3786d216951cc6b94babc3b814c08d2cb2738
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.3.0"
|
||||
window_manager:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -511,4 +647,4 @@ packages:
|
|||
version: "3.1.3"
|
||||
sdks:
|
||||
dart: ">=3.11.4 <4.0.0"
|
||||
flutter: ">=3.35.0"
|
||||
flutter: ">=3.38.1"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
name: ostp_client
|
||||
description: "A new Flutter project."
|
||||
description: "OSTP VPN Client by Ospab Foundation."
|
||||
# The following line prevents the package from being accidentally published to
|
||||
# pub.dev using `flutter pub publish`. This is preferred for private packages.
|
||||
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||
|
|
@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
|||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
# In Windows, build-name is used as the major, minor, and patch parts
|
||||
# of the product and file versions while build-number is used as the build suffix.
|
||||
version: 0.2.96+11
|
||||
version: 0.2.97+12
|
||||
|
||||
environment:
|
||||
sdk: ^3.11.4
|
||||
|
|
@ -38,6 +38,10 @@ dependencies:
|
|||
mobile_scanner: ^5.0.0
|
||||
window_manager: ^0.5.1
|
||||
tray_manager: ^0.5.2
|
||||
qr_flutter: ^4.1.0
|
||||
http: ^1.6.0
|
||||
url_launcher: ^6.3.2
|
||||
package_info_plus: ^10.1.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
|
@ -54,7 +58,7 @@ dev_dependencies:
|
|||
flutter_launcher_icons:
|
||||
android: "launcher_icon"
|
||||
ios: false
|
||||
image_path: "android_icon.png"
|
||||
image_path: "../icons/sqare.png"
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
Add-Type -AssemblyName System.Drawing
|
||||
$imgPath = "D:\ospab-projects\ostp\ostp-flutter\android_icon.png"
|
||||
$backupPath = "D:\ospab-projects\ostp\ostp-flutter\android_icon_backup.png"
|
||||
|
||||
# Copy as backup
|
||||
Copy-Item -Path $imgPath -Destination $backupPath -Force
|
||||
|
||||
# Load image
|
||||
$srcImg = [System.Drawing.Image]::FromFile($imgPath)
|
||||
$w = $srcImg.Width
|
||||
$h = $srcImg.Height
|
||||
|
||||
# Create new transparent bitmap of the SAME size
|
||||
$bmp = New-Object System.Drawing.Bitmap($w, $h)
|
||||
$bmp.MakeTransparent()
|
||||
$graphics = [System.Drawing.Graphics]::FromImage($bmp)
|
||||
|
||||
# Set high quality resizing
|
||||
$graphics.InterpolationMode = [System.Drawing.Drawing2D.InterpolationMode]::HighQualityBicubic
|
||||
$graphics.SmoothingMode = [System.Drawing.Drawing2D.SmoothingMode]::HighQuality
|
||||
$graphics.PixelOffsetMode = [System.Drawing.Drawing2D.PixelOffsetMode]::HighQuality
|
||||
|
||||
# Clear with transparent
|
||||
$graphics.Clear([System.Drawing.Color]::Transparent)
|
||||
|
||||
# We want to scale to 50% and center
|
||||
$newW = [math]::Round($w / 2)
|
||||
$newH = [math]::Round($h / 2)
|
||||
$x = [math]::Round(($w - $newW) / 2)
|
||||
$y = [math]::Round(($h - $newH) / 2)
|
||||
|
||||
# Draw image scaled down
|
||||
$rect = New-Object System.Drawing.Rectangle($x, $y, $newW, $newH)
|
||||
$graphics.DrawImage($srcImg, $rect)
|
||||
|
||||
# Dispose graphics and src before saving over
|
||||
$graphics.Dispose()
|
||||
$srcImg.Dispose()
|
||||
|
||||
# Save over original
|
||||
$bmp.Save($imgPath, [System.Drawing.Imaging.ImageFormat]::Png)
|
||||
$bmp.Dispose()
|
||||
|
||||
Write-Output "Successfully resized image."
|
||||
|
|
@ -2665,7 +2665,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "ostp-client"
|
||||
version = "0.2.95"
|
||||
version = "0.2.97"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.22.1",
|
||||
|
|
@ -2699,7 +2699,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "ostp-core"
|
||||
version = "0.2.95"
|
||||
version = "0.2.97"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
|
|
@ -2734,7 +2734,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "ostp-tun"
|
||||
version = "0.2.95"
|
||||
version = "0.2.97"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"libc",
|
||||
|
|
|
|||
|
|
@ -720,7 +720,13 @@ fn launch_as_admin(exe: &std::path::PathBuf, token: &str, port: u16) -> anyhow::
|
|||
|
||||
let exe_wstr: Vec<u16> = exe.as_os_str().encode_wide().chain(Some(0)).collect();
|
||||
let verb_wstr: Vec<u16> = OsStr::new("runas").encode_wide().chain(Some(0)).collect();
|
||||
let params_str = format!("--port {} --token {}", port, token);
|
||||
|
||||
// Write token to temp file for security instead of passing via cmdline
|
||||
let temp_dir = std::env::temp_dir();
|
||||
let token_file = temp_dir.join(format!("ostp_auth_{}.tmp", rand::random::<u32>()));
|
||||
std::fs::write(&token_file, token)?;
|
||||
|
||||
let params_str = format!("--port {} --token-file \"{}\"", port, token_file.display());
|
||||
let params_wstr: Vec<u16> = OsStr::new(¶ms_str).encode_wide().chain(Some(0)).collect();
|
||||
#[link(name = "shell32")] extern "system" { fn ShellExecuteW(h: *mut std::ffi::c_void, op: *const u16, f: *const u16, p: *const u16, d: *const u16, s: i32) -> isize; }
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"$schema": "https://schema.tauri.app/config/2",
|
||||
"productName": "ostp-gui",
|
||||
"version": "0.2.96",
|
||||
"version": "0.2.97",
|
||||
"identifier": "com.ospab.ostp",
|
||||
"build": {
|
||||
"frontendDist": "../src"
|
||||
|
|
@ -23,6 +23,7 @@
|
|||
"bundle": {
|
||||
"active": true,
|
||||
"targets": "all",
|
||||
"publisher": "Ospab Foundation",
|
||||
"icon": [
|
||||
"icons/32x32.png",
|
||||
"icons/128x128.png",
|
||||
|
|
|
|||
|
|
@ -61,23 +61,27 @@ async fn main() -> Result<()> {
|
|||
}
|
||||
}
|
||||
|
||||
let mut expected_token = String::new();
|
||||
let mut expected_token = std::env::var("OSTP_TUN_TOKEN").unwrap_or_default();
|
||||
let mut port = 53211u16;
|
||||
let args: Vec<String> = std::env::args().collect();
|
||||
for i in 1..args.len() {
|
||||
if args[i] == "--port" && i + 1 < args.len() {
|
||||
port = args[i + 1].parse().unwrap_or(53211);
|
||||
}
|
||||
if args[i] == "--token" && i + 1 < args.len() {
|
||||
expected_token = args[i + 1].clone();
|
||||
if args[i] == "--token-file" && i + 1 < args.len() {
|
||||
let path = &args[i + 1];
|
||||
if let Ok(content) = std::fs::read_to_string(path) {
|
||||
expected_token = content.trim().to_string();
|
||||
let _ = std::fs::remove_file(path); // securely delete after reading
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log_to_file("Helper started (TCP mode)");
|
||||
|
||||
if expected_token.is_empty() {
|
||||
log_to_file("FATAL: OSTP_TUN_TOKEN environment variable is required for security. Unauthorized access denied.");
|
||||
return Err(anyhow::anyhow!("OSTP_TUN_TOKEN environment variable is required"));
|
||||
log_to_file("FATAL: Auth token is required for security (--token-file or OSTP_TUN_TOKEN).");
|
||||
return Err(anyhow::anyhow!("Auth token is required"));
|
||||
}
|
||||
|
||||
if let Err(e) = run_server(expected_token, port).await {
|
||||
|
|
|
|||
Loading…
Reference in New Issue