mirror of
https://github.com/house-of-vanity/v2-uri-parser.git
synced 2025-12-17 07:27:51 +00:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f5a4fc91e5 | ||
| e9ceb58b80 | |||
| 67fe482a7c | |||
| 7c741c8e62 | |||
| 1a4a296fae | |||
| 1249d15aad | |||
|
|
6e4a5da563 | ||
|
|
e1aca3b282 | ||
| 113c8254ab | |||
| 4fcffd57ca | |||
| aadbb61a90 | |||
| 32746af0a0 | |||
|
|
adb0d4bdb9 | ||
|
|
cb52c3d912 |
@@ -16,6 +16,15 @@ Options:
|
|||||||
--socksport <PORT> Optional SOCKS5 proxy port for inbound
|
--socksport <PORT> Optional SOCKS5 proxy port for inbound
|
||||||
--httpport <PORT> Optional HTTP proxy port for inbound
|
--httpport <PORT> Optional HTTP proxy port for inbound
|
||||||
--get-metadata Only print config meta data
|
--get-metadata Only print config meta data
|
||||||
|
--run Run xray-core with the generated config
|
||||||
|
--xray-binary <PATH> Path to xray-core binary (default: xray from PATH)
|
||||||
-h, --help Print help
|
-h, --help Print help
|
||||||
-V, --version Print version
|
-V, --version Print version
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### Install as Windows Task to start automatically
|
||||||
|
Run this command via PowerShell
|
||||||
|
```
|
||||||
|
powershell -ExecutionPolicy Bypass -Command "irm https://raw.githubusercontent.com/house-of-vanity/v2-uri-parser/main/scripts/win-service-installer.ps1 -OutFile $env:TEMP\v2.ps1; & $env:TEMP\v2.ps1"
|
||||||
|
```
|
||||||
|
|||||||
115
scripts/win-service-installer.ps1
Normal file
115
scripts/win-service-installer.ps1
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
# Check for admin rights at start
|
||||||
|
$isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
|
||||||
|
|
||||||
|
if (-not $isAdmin) {
|
||||||
|
Write-Host "Administrator rights required. Restarting..." -ForegroundColor Yellow
|
||||||
|
$scriptPath = $MyInvocation.MyCommand.Path
|
||||||
|
if ([string]::IsNullOrEmpty($scriptPath)) {
|
||||||
|
$scriptPath = "$env:TEMP\v2proxy-installer.ps1"
|
||||||
|
}
|
||||||
|
Start-Process powershell -Verb RunAs -ArgumentList "-ExecutionPolicy Bypass -NoExit -File `"$scriptPath`""
|
||||||
|
exit
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create user binary directory
|
||||||
|
$binPath = "$env:USERPROFILE\.local\bin"
|
||||||
|
New-Item -ItemType Directory -Force -Path $binPath
|
||||||
|
|
||||||
|
# Download v2parser
|
||||||
|
$v2repo = "house-of-vanity/v2-uri-parser"
|
||||||
|
$v2release = Invoke-RestMethod "https://api.github.com/repos/$v2repo/releases/latest"
|
||||||
|
$v2asset = $v2release.assets | Where-Object { $_.name -eq "v2parser-x86_64-pc-windows-msvc.zip" }
|
||||||
|
$v2zip = "$env:TEMP\v2parser.zip"
|
||||||
|
Invoke-WebRequest -Uri $v2asset.browser_download_url -OutFile $v2zip
|
||||||
|
Expand-Archive -Path $v2zip -DestinationPath $binPath -Force
|
||||||
|
Remove-Item $v2zip -ErrorAction SilentlyContinue
|
||||||
|
|
||||||
|
# Download Xray-core
|
||||||
|
$xrayRepo = "XTLS/Xray-core"
|
||||||
|
$xrayRelease = Invoke-RestMethod "https://api.github.com/repos/$xrayRepo/releases/latest"
|
||||||
|
$xrayAsset = $xrayRelease.assets | Where-Object { $_.name -eq "Xray-windows-64.zip" }
|
||||||
|
$xrayZip = "$env:TEMP\xray.zip"
|
||||||
|
Invoke-WebRequest -Uri $xrayAsset.browser_download_url -OutFile $xrayZip
|
||||||
|
Expand-Archive -Path $xrayZip -DestinationPath $binPath -Force
|
||||||
|
Remove-Item $xrayZip -ErrorAction SilentlyContinue
|
||||||
|
|
||||||
|
# Request server location
|
||||||
|
$serverLocation = Read-Host "Enter server location (e.g., US-NY, DE-Berlin, JP-Tokyo)"
|
||||||
|
$serverLocation = $serverLocation -replace '[^a-zA-Z0-9-]', '-'
|
||||||
|
|
||||||
|
# Request proxy URI from user
|
||||||
|
do {
|
||||||
|
$uri = Read-Host "Enter proxy URI (vless://, vmess://, shadowsocks://, trojan://, or socks://)"
|
||||||
|
$validPrefix = $uri -match "^(vless|vmess|shadowsocks|trojan|socks)://"
|
||||||
|
if (-not $validPrefix) {
|
||||||
|
Write-Host "Invalid URI. Must start with vless://, vmess://, shadowsocks://, trojan://, or socks://" -ForegroundColor Red
|
||||||
|
}
|
||||||
|
} while (-not $validPrefix)
|
||||||
|
|
||||||
|
# Find available port
|
||||||
|
$port = Read-Host "Enter HTTP port (default: 1080)"
|
||||||
|
if ([string]::IsNullOrWhiteSpace($port)) {
|
||||||
|
$port = 1080
|
||||||
|
}
|
||||||
|
|
||||||
|
$port = [int]$port
|
||||||
|
while ($true) {
|
||||||
|
$listener = $null
|
||||||
|
try {
|
||||||
|
$listener = New-Object System.Net.Sockets.TcpListener([System.Net.IPAddress]::Loopback, $port)
|
||||||
|
$listener.Start()
|
||||||
|
$listener.Stop()
|
||||||
|
Write-Host "Port $port is available" -ForegroundColor Green
|
||||||
|
break
|
||||||
|
} catch {
|
||||||
|
Write-Host "Port $port is in use, trying $($port + 1)" -ForegroundColor Yellow
|
||||||
|
$port++
|
||||||
|
} finally {
|
||||||
|
if ($listener) { $listener.Stop() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create unique task name
|
||||||
|
$taskName = "V2ProxyService $serverLocation $port"
|
||||||
|
$v2parserPath = Join-Path $binPath "v2parser.exe"
|
||||||
|
$xrayPath = Join-Path $binPath "xray.exe"
|
||||||
|
|
||||||
|
# Create batch file wrapper
|
||||||
|
$batchPath = Join-Path $binPath "v2proxy-$serverLocation.bat"
|
||||||
|
$batchContent = "@echo off`ncd /d `"$binPath`"`n`"$v2parserPath`" `"$uri`" --httpport $port --run --xray-binary `"$xrayPath`""
|
||||||
|
Set-Content -Path $batchPath -Value $batchContent -Encoding ASCII
|
||||||
|
|
||||||
|
# Remove existing task if exists
|
||||||
|
Unregister-ScheduledTask -TaskName $taskName -Confirm:$false -ErrorAction SilentlyContinue
|
||||||
|
|
||||||
|
# Create scheduled task action
|
||||||
|
$action = New-ScheduledTaskAction -Execute $batchPath -WorkingDirectory $binPath
|
||||||
|
|
||||||
|
# Create trigger for system startup
|
||||||
|
$trigger = New-ScheduledTaskTrigger -AtStartup
|
||||||
|
|
||||||
|
# Create principal to run with highest privileges
|
||||||
|
$principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest
|
||||||
|
|
||||||
|
# Create settings
|
||||||
|
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable -RestartCount 3 -RestartInterval (New-TimeSpan -Minutes 1)
|
||||||
|
|
||||||
|
# Register scheduled task
|
||||||
|
Register-ScheduledTask -TaskName $taskName -Action $action -Trigger $trigger -Principal $principal -Settings $settings -Description "V2Ray Proxy Service - $serverLocation"
|
||||||
|
|
||||||
|
# Start task immediately
|
||||||
|
Start-ScheduledTask -TaskName $taskName
|
||||||
|
|
||||||
|
Start-Sleep -Seconds 2
|
||||||
|
|
||||||
|
# Check if task is running
|
||||||
|
$task = Get-ScheduledTask -TaskName $taskName
|
||||||
|
$taskInfo = Get-ScheduledTaskInfo -TaskName $taskName
|
||||||
|
|
||||||
|
Write-Host "`nTask '$taskName' created!" -ForegroundColor Green
|
||||||
|
Write-Host "Status: $($task.State)" -ForegroundColor Cyan
|
||||||
|
Write-Host "Last Run: $($taskInfo.LastRunTime)" -ForegroundColor Cyan
|
||||||
|
Write-Host "Last Result: $($taskInfo.LastTaskResult)" -ForegroundColor Cyan
|
||||||
|
Write-Host "Proxy is running on http://127.0.0.1:$port" -ForegroundColor Cyan
|
||||||
|
|
||||||
|
Read-Host "`nPress Enter to exit"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
use clap::{value_parser, Arg, Command};
|
use clap::{Arg, Command, value_parser};
|
||||||
pub mod config_models;
|
pub mod config_models;
|
||||||
mod parser;
|
mod parser;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
@@ -54,7 +54,10 @@ async fn main() {
|
|||||||
let httpport = matches.get_one::<u16>("httpport").copied();
|
let httpport = matches.get_one::<u16>("httpport").copied();
|
||||||
let get_metadata = matches.get_flag("get_metadata");
|
let get_metadata = matches.get_flag("get_metadata");
|
||||||
let run_mode = matches.get_flag("run");
|
let run_mode = matches.get_flag("run");
|
||||||
let xray_binary = matches.get_one::<String>("xray_binary").map(|s| s.as_str()).unwrap_or("xray-core");
|
let xray_binary = matches
|
||||||
|
.get_one::<String>("xray_binary")
|
||||||
|
.map(|s| s.as_str())
|
||||||
|
.unwrap_or("xray-core");
|
||||||
|
|
||||||
if get_metadata {
|
if get_metadata {
|
||||||
print!("{}", parser::get_metadata(uri));
|
print!("{}", parser::get_metadata(uri));
|
||||||
|
|||||||
@@ -18,17 +18,17 @@ pub fn get_metadata(uri: &str) -> String {
|
|||||||
name: data.remarks,
|
name: data.remarks,
|
||||||
host: data.host.clone(),
|
host: data.host.clone(),
|
||||||
address: data.address.clone(),
|
address: data.address.clone(),
|
||||||
port: data.port.clone(),
|
port: data.port,
|
||||||
protocol,
|
protocol,
|
||||||
};
|
};
|
||||||
let serialized = serde_json::to_string(&meta_data).unwrap();
|
|
||||||
return serialized;
|
serde_json::to_string(&meta_data).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_json_config(uri: &str, socks_port: Option<u16>, http_port: Option<u16>) -> String {
|
pub fn create_json_config(uri: &str, socks_port: Option<u16>, http_port: Option<u16>) -> String {
|
||||||
let config = create_config(uri, socks_port, http_port);
|
let config = create_config(uri, socks_port, http_port);
|
||||||
let serialized = serde_json::to_string(&config).unwrap();
|
|
||||||
return serialized;
|
serde_json::to_string(&config).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_config(
|
pub fn create_config(
|
||||||
@@ -42,11 +42,11 @@ pub fn create_config(
|
|||||||
socks_port,
|
socks_port,
|
||||||
http_port,
|
http_port,
|
||||||
});
|
});
|
||||||
let config = config_models::Config {
|
|
||||||
|
config_models::Config {
|
||||||
outbounds: vec![outbound_object],
|
outbounds: vec![outbound_object],
|
||||||
inbounds: inbound_config,
|
inbounds: inbound_config,
|
||||||
};
|
}
|
||||||
return config;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_outbound_object(uri: &str) -> config_models::Outbound {
|
pub fn create_outbound_object(uri: &str) -> config_models::Outbound {
|
||||||
@@ -56,7 +56,7 @@ pub fn create_outbound_object(uri: &str) -> config_models::Outbound {
|
|||||||
let allow_insecure = data.allowInsecure == Some(String::from("true"))
|
let allow_insecure = data.allowInsecure == Some(String::from("true"))
|
||||||
|| data.allowInsecure == Some(String::from("1"));
|
|| data.allowInsecure == Some(String::from("1"));
|
||||||
|
|
||||||
let outbound = Outbound {
|
Outbound {
|
||||||
protocol: name,
|
protocol: name,
|
||||||
tag: String::from("proxy"),
|
tag: String::from("proxy"),
|
||||||
streamSettings: StreamSettings {
|
streamSettings: StreamSettings {
|
||||||
@@ -79,7 +79,7 @@ pub fn create_outbound_object(uri: &str) -> config_models::Outbound {
|
|||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
wsSettings: if network_type == String::from("ws") {
|
wsSettings: if network_type == "ws" {
|
||||||
Some(WsSettings {
|
Some(WsSettings {
|
||||||
Host: data.host.clone(),
|
Host: data.host.clone(),
|
||||||
path: data.path.clone(),
|
path: data.path.clone(),
|
||||||
@@ -88,7 +88,7 @@ pub fn create_outbound_object(uri: &str) -> config_models::Outbound {
|
|||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
tcpSettings: if network_type == String::from("tcp") {
|
tcpSettings: if network_type == "tcp" {
|
||||||
Some(TCPSettings {
|
Some(TCPSettings {
|
||||||
header: Some(TCPHeader {
|
header: Some(TCPHeader {
|
||||||
r#type: Some(data.header_type.unwrap_or(String::from("none"))),
|
r#type: Some(data.header_type.unwrap_or(String::from("none"))),
|
||||||
@@ -109,7 +109,7 @@ pub fn create_outbound_object(uri: &str) -> config_models::Outbound {
|
|||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
grpcSettings: if network_type == String::from("grpc") {
|
grpcSettings: if network_type == "grpc" {
|
||||||
Some(GRPCSettings {
|
Some(GRPCSettings {
|
||||||
authority: data.authority,
|
authority: data.authority,
|
||||||
multiMode: Some(false),
|
multiMode: Some(false),
|
||||||
@@ -118,7 +118,7 @@ pub fn create_outbound_object(uri: &str) -> config_models::Outbound {
|
|||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
quicSettings: if network_type == String::from("quic") {
|
quicSettings: if network_type == "quic" {
|
||||||
Some(QuicSettings {
|
Some(QuicSettings {
|
||||||
header: Some(NonHeaderObject {
|
header: Some(NonHeaderObject {
|
||||||
r#type: Some(String::from("none")),
|
r#type: Some(String::from("none")),
|
||||||
@@ -129,7 +129,7 @@ pub fn create_outbound_object(uri: &str) -> config_models::Outbound {
|
|||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
kcpSettings: if network_type == String::from("kcp") {
|
kcpSettings: if network_type == "kcp" {
|
||||||
Some(KCPSettings {
|
Some(KCPSettings {
|
||||||
mtu: None,
|
mtu: None,
|
||||||
tti: None,
|
tti: None,
|
||||||
@@ -143,7 +143,7 @@ pub fn create_outbound_object(uri: &str) -> config_models::Outbound {
|
|||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
xhttpSettings: if network_type == String::from("xhttp") {
|
xhttpSettings: if network_type == "xhttp" {
|
||||||
Some(XHTTPSettings {
|
Some(XHTTPSettings {
|
||||||
host: data.host.clone(),
|
host: data.host.clone(),
|
||||||
path: data.path.clone(),
|
path: data.path.clone(),
|
||||||
@@ -155,14 +155,12 @@ pub fn create_outbound_object(uri: &str) -> config_models::Outbound {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
settings: outbound_settings,
|
settings: outbound_settings,
|
||||||
};
|
}
|
||||||
|
|
||||||
return outbound;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_uri_data(uri: &str) -> (String, RawData, OutboundSettings) {
|
fn get_uri_data(uri: &str) -> (String, RawData, OutboundSettings) {
|
||||||
let protocol = uri_identifier::get_uri_protocol(uri);
|
let protocol = uri_identifier::get_uri_protocol(uri);
|
||||||
return match protocol {
|
match protocol {
|
||||||
Some(uri_identifier::Protocols::Vless) => {
|
Some(uri_identifier::Protocols::Vless) => {
|
||||||
let d = vless::data::get_data(uri);
|
let d = vless::data::get_data(uri);
|
||||||
let s = vless::create_outbound_settings(&d);
|
let s = vless::create_outbound_settings(&d);
|
||||||
@@ -194,5 +192,5 @@ fn get_uri_data(uri: &str) -> (String, RawData, OutboundSettings) {
|
|||||||
None => {
|
None => {
|
||||||
panic!("The protocol is not supported");
|
panic!("The protocol is not supported");
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,14 +5,14 @@ use crate::{
|
|||||||
parser::shadow_socks::models,
|
parser::shadow_socks::models,
|
||||||
utils::{url_decode, url_decode_str},
|
utils::{url_decode, url_decode_str},
|
||||||
};
|
};
|
||||||
use base64::{engine::general_purpose, Engine};
|
use base64::{Engine, engine::general_purpose};
|
||||||
|
|
||||||
pub fn get_data(uri: &str) -> RawData {
|
pub fn get_data(uri: &str) -> RawData {
|
||||||
let data = uri.split_once("ss://").unwrap().1;
|
let data = uri.split_once("ss://").unwrap().1;
|
||||||
let (raw_data, name) = data.split_once("#").unwrap_or((data, ""));
|
let (raw_data, name) = data.split_once("#").unwrap_or((data, ""));
|
||||||
let (raw_uri, _) = raw_data.split_once("?").unwrap_or((raw_data, ""));
|
let (raw_uri, _) = raw_data.split_once("?").unwrap_or((raw_data, ""));
|
||||||
let parsed_address = parse_ss_address(raw_uri);
|
let parsed_address = parse_ss_address(raw_uri);
|
||||||
return RawData {
|
RawData {
|
||||||
remarks: url_decode(Some(String::from(name))).unwrap_or(String::from("")),
|
remarks: url_decode(Some(String::from(name))).unwrap_or(String::from("")),
|
||||||
server_method: url_decode(Some(parsed_address.method)),
|
server_method: url_decode(Some(parsed_address.method)),
|
||||||
address: Some(parsed_address.address),
|
address: Some(parsed_address.address),
|
||||||
@@ -42,7 +42,7 @@ pub fn get_data(uri: &str) -> RawData {
|
|||||||
allowInsecure: None,
|
allowInsecure: None,
|
||||||
vnext_security: None,
|
vnext_security: None,
|
||||||
username: None,
|
username: None,
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_ss_address(raw_data: &str) -> models::ShadowSocksAddress {
|
fn parse_ss_address(raw_data: &str) -> models::ShadowSocksAddress {
|
||||||
@@ -65,10 +65,10 @@ fn parse_ss_address(raw_data: &str) -> models::ShadowSocksAddress {
|
|||||||
.split_once(":")
|
.split_once(":")
|
||||||
.expect("No `:` found in the decoded base64");
|
.expect("No `:` found in the decoded base64");
|
||||||
|
|
||||||
return models::ShadowSocksAddress {
|
models::ShadowSocksAddress {
|
||||||
method: String::from(method),
|
method: String::from(method),
|
||||||
password: String::from(password),
|
password: String::from(password),
|
||||||
address: parsed.host().unwrap().to_string(),
|
address: parsed.host().unwrap().to_string(),
|
||||||
port: parsed.port().unwrap().as_u16(),
|
port: parsed.port().unwrap().as_u16(),
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ mod models;
|
|||||||
use crate::config_models::*;
|
use crate::config_models::*;
|
||||||
|
|
||||||
pub fn create_outbound_settings(data: &RawData) -> OutboundSettings {
|
pub fn create_outbound_settings(data: &RawData) -> OutboundSettings {
|
||||||
return OutboundSettings::ShadowSocks(ShadowSocksOutboundSettings {
|
OutboundSettings::ShadowSocks(ShadowSocksOutboundSettings {
|
||||||
servers: vec![ShadowSocksServerObject {
|
servers: vec![ShadowSocksServerObject {
|
||||||
address: data.address.clone(),
|
address: data.address.clone(),
|
||||||
port: data.port,
|
port: data.port,
|
||||||
@@ -11,5 +11,5 @@ pub fn create_outbound_settings(data: &RawData) -> OutboundSettings {
|
|||||||
level: Some(0),
|
level: Some(0),
|
||||||
method: data.server_method.clone(),
|
method: data.server_method.clone(),
|
||||||
}],
|
}],
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,14 +5,14 @@ use crate::{
|
|||||||
parser::socks::models,
|
parser::socks::models,
|
||||||
utils::{url_decode, url_decode_str},
|
utils::{url_decode, url_decode_str},
|
||||||
};
|
};
|
||||||
use base64::{engine::general_purpose, Engine};
|
use base64::{Engine, engine::general_purpose};
|
||||||
|
|
||||||
pub fn get_data(uri: &str) -> RawData {
|
pub fn get_data(uri: &str) -> RawData {
|
||||||
let data = uri.split_once("://").unwrap().1;
|
let data = uri.split_once("://").unwrap().1;
|
||||||
let (raw_data, name) = data.split_once("#").unwrap_or((data, ""));
|
let (raw_data, name) = data.split_once("#").unwrap_or((data, ""));
|
||||||
let (raw_uri, _) = raw_data.split_once("?").unwrap_or((raw_data, ""));
|
let (raw_uri, _) = raw_data.split_once("?").unwrap_or((raw_data, ""));
|
||||||
let parsed_address = parse_socks_address(raw_uri);
|
let parsed_address = parse_socks_address(raw_uri);
|
||||||
return RawData {
|
RawData {
|
||||||
remarks: url_decode(Some(String::from(name))).unwrap_or(String::from("")),
|
remarks: url_decode(Some(String::from(name))).unwrap_or(String::from("")),
|
||||||
username: url_decode(parsed_address.username),
|
username: url_decode(parsed_address.username),
|
||||||
address: Some(parsed_address.address),
|
address: Some(parsed_address.address),
|
||||||
@@ -42,7 +42,7 @@ pub fn get_data(uri: &str) -> RawData {
|
|||||||
quic_security: None,
|
quic_security: None,
|
||||||
allowInsecure: None,
|
allowInsecure: None,
|
||||||
vnext_security: None,
|
vnext_security: None,
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_socks_address(raw_data: &str) -> models::SocksAddress {
|
fn parse_socks_address(raw_data: &str) -> models::SocksAddress {
|
||||||
@@ -54,7 +54,7 @@ fn parse_socks_address(raw_data: &str) -> models::SocksAddress {
|
|||||||
|
|
||||||
let parsed = address_wo_slash.parse::<Uri>().unwrap();
|
let parsed = address_wo_slash.parse::<Uri>().unwrap();
|
||||||
|
|
||||||
return match maybe_userinfo {
|
match maybe_userinfo {
|
||||||
Some(userinfo) => {
|
Some(userinfo) => {
|
||||||
let url_decoded = url_decode_str(&userinfo).unwrap_or(userinfo);
|
let url_decoded = url_decode_str(&userinfo).unwrap_or(userinfo);
|
||||||
let username_and_password = general_purpose::STANDARD
|
let username_and_password = general_purpose::STANDARD
|
||||||
@@ -64,7 +64,7 @@ fn parse_socks_address(raw_data: &str) -> models::SocksAddress {
|
|||||||
std::str::from_utf8(&a).expect("Base64 did not yield a valid utf-8 string"),
|
std::str::from_utf8(&a).expect("Base64 did not yield a valid utf-8 string"),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.unwrap_or(String::from(url_decoded.clone()));
|
.unwrap_or(url_decoded.clone());
|
||||||
|
|
||||||
let (username, password) = username_and_password
|
let (username, password) = username_and_password
|
||||||
.split_once(":")
|
.split_once(":")
|
||||||
@@ -83,5 +83,5 @@ fn parse_socks_address(raw_data: &str) -> models::SocksAddress {
|
|||||||
address: parsed.host().unwrap().to_string(),
|
address: parsed.host().unwrap().to_string(),
|
||||||
port: parsed.port().unwrap().as_u16(),
|
port: parsed.port().unwrap().as_u16(),
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ mod models;
|
|||||||
use crate::config_models::*;
|
use crate::config_models::*;
|
||||||
|
|
||||||
pub fn create_outbound_settings(data: &RawData) -> OutboundSettings {
|
pub fn create_outbound_settings(data: &RawData) -> OutboundSettings {
|
||||||
return OutboundSettings::Socks(SocksOutboundSettings {
|
OutboundSettings::Socks(SocksOutboundSettings {
|
||||||
servers: vec![SocksServerObject {
|
servers: vec![SocksServerObject {
|
||||||
users: match (&data.username, &data.uuid) {
|
users: match (&data.username, &data.uuid) {
|
||||||
(Some(username), Some(uuid)) => Some(vec![SocksUser {
|
(Some(username), Some(uuid)) => Some(vec![SocksUser {
|
||||||
@@ -16,5 +16,5 @@ pub fn create_outbound_settings(data: &RawData) -> OutboundSettings {
|
|||||||
port: data.port,
|
port: data.port,
|
||||||
level: Some(0),
|
level: Some(0),
|
||||||
}],
|
}],
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ pub fn get_data(uri: &str) -> RawData {
|
|||||||
let parsed_address = parse_trojan_address(data.split_once("?").unwrap().0);
|
let parsed_address = parse_trojan_address(data.split_once("?").unwrap().0);
|
||||||
let query: Vec<(&str, &str)> = querystring::querify(raw_query);
|
let query: Vec<(&str, &str)> = querystring::querify(raw_query);
|
||||||
|
|
||||||
return RawData {
|
RawData {
|
||||||
remarks: url_decode(Some(String::from(name))).unwrap_or(String::from("")),
|
remarks: url_decode(Some(String::from(name))).unwrap_or(String::from("")),
|
||||||
uuid: Some(parsed_address.uuid),
|
uuid: Some(parsed_address.uuid),
|
||||||
port: Some(parsed_address.port),
|
port: Some(parsed_address.port),
|
||||||
@@ -42,7 +42,7 @@ pub fn get_data(uri: &str) -> RawData {
|
|||||||
allowInsecure: get_parameter_value(&query, "allowInsecure"),
|
allowInsecure: get_parameter_value(&query, "allowInsecure"),
|
||||||
server_method: None,
|
server_method: None,
|
||||||
username: None,
|
username: None,
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_trojan_address(raw_data: &str) -> models::TrojanAddress {
|
fn parse_trojan_address(raw_data: &str) -> models::TrojanAddress {
|
||||||
@@ -56,9 +56,9 @@ fn parse_trojan_address(raw_data: &str) -> models::TrojanAddress {
|
|||||||
|
|
||||||
let parsed = address_wo_slash.parse::<Uri>().unwrap();
|
let parsed = address_wo_slash.parse::<Uri>().unwrap();
|
||||||
|
|
||||||
return models::TrojanAddress {
|
models::TrojanAddress {
|
||||||
uuid: url_decode(Some(uuid)).unwrap(),
|
uuid: url_decode(Some(uuid)).unwrap(),
|
||||||
address: parsed.host().unwrap().to_string(),
|
address: parsed.host().unwrap().to_string(),
|
||||||
port: parsed.port().unwrap().as_u16(),
|
port: parsed.port().unwrap().as_u16(),
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,12 +3,12 @@ mod models;
|
|||||||
use crate::config_models::*;
|
use crate::config_models::*;
|
||||||
|
|
||||||
pub fn create_outbound_settings(data: &RawData) -> OutboundSettings {
|
pub fn create_outbound_settings(data: &RawData) -> OutboundSettings {
|
||||||
return OutboundSettings::Trojan(TrojanOutboundSettings {
|
OutboundSettings::Trojan(TrojanOutboundSettings {
|
||||||
servers: vec![TrojanServerObject {
|
servers: vec![TrojanServerObject {
|
||||||
address: data.address.clone(),
|
address: data.address.clone(),
|
||||||
port: data.port,
|
port: data.port,
|
||||||
password: data.uuid.clone(),
|
password: data.uuid.clone(),
|
||||||
level: Some(0),
|
level: Some(0),
|
||||||
}],
|
}],
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ pub fn get_uri_protocol(uri: &str) -> Option<Protocols> {
|
|||||||
if uri.starts_with("trojan://") {
|
if uri.starts_with("trojan://") {
|
||||||
return Some(Protocols::Trojan);
|
return Some(Protocols::Trojan);
|
||||||
}
|
}
|
||||||
return None;
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -34,8 +34,10 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
#[test]
|
#[test]
|
||||||
fn return_none_for_invalid_uri() {
|
fn return_none_for_invalid_uri() {
|
||||||
let protocol = get_uri_protocol("123-vless://3d1c3f04-729d-59d3-bdb6-3f3f4352e173@root.ii.one:2083?security=reality&sni=www.spamhaus.org&fp=safari&pbk=7xhH4b_VkliBxGulljcyPOH-bYUA2dl-XAdZAsfhk04&sid=6ba85179e30d4fc2&type=tcp&flow=xtls-rprx-vision#Ha-ac");
|
let protocol = get_uri_protocol(
|
||||||
assert!(matches!(protocol, None));
|
"123-vless://3d1c3f04-729d-59d3-bdb6-3f3f4352e173@root.ii.one:2083?security=reality&sni=www.spamhaus.org&fp=safari&pbk=7xhH4b_VkliBxGulljcyPOH-bYUA2dl-XAdZAsfhk04&sid=6ba85179e30d4fc2&type=tcp&flow=xtls-rprx-vision#Ha-ac",
|
||||||
|
);
|
||||||
|
assert!(protocol.is_none());
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn recognize_vless_protocol() {
|
fn recognize_vless_protocol() {
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ pub fn get_data(uri: &str) -> RawData {
|
|||||||
let parsed_address = parse_vless_address(data.split_once("?").unwrap().0);
|
let parsed_address = parse_vless_address(data.split_once("?").unwrap().0);
|
||||||
let query: Vec<(&str, &str)> = querystring::querify(raw_query);
|
let query: Vec<(&str, &str)> = querystring::querify(raw_query);
|
||||||
|
|
||||||
return RawData {
|
RawData {
|
||||||
remarks: url_decode(Some(String::from(name))).unwrap_or(String::from("")),
|
remarks: url_decode(Some(String::from(name))).unwrap_or(String::from("")),
|
||||||
uuid: Some(parsed_address.uuid),
|
uuid: Some(parsed_address.uuid),
|
||||||
port: Some(parsed_address.port),
|
port: Some(parsed_address.port),
|
||||||
@@ -42,7 +42,7 @@ pub fn get_data(uri: &str) -> RawData {
|
|||||||
allowInsecure: get_parameter_value(&query, "allowInsecure"),
|
allowInsecure: get_parameter_value(&query, "allowInsecure"),
|
||||||
server_method: None,
|
server_method: None,
|
||||||
username: None,
|
username: None,
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_vless_address(raw_data: &str) -> models::VlessAddress {
|
fn parse_vless_address(raw_data: &str) -> models::VlessAddress {
|
||||||
@@ -56,9 +56,9 @@ fn parse_vless_address(raw_data: &str) -> models::VlessAddress {
|
|||||||
|
|
||||||
let parsed = address_wo_slash.parse::<Uri>().unwrap();
|
let parsed = address_wo_slash.parse::<Uri>().unwrap();
|
||||||
|
|
||||||
return models::VlessAddress {
|
models::VlessAddress {
|
||||||
uuid: url_decode(Some(uuid)).unwrap(),
|
uuid: url_decode(Some(uuid)).unwrap(),
|
||||||
address: parsed.host().unwrap().to_string(),
|
address: parsed.host().unwrap().to_string(),
|
||||||
port: parsed.port().unwrap().as_u16(),
|
port: parsed.port().unwrap().as_u16(),
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ mod models;
|
|||||||
use crate::config_models::*;
|
use crate::config_models::*;
|
||||||
|
|
||||||
pub fn create_outbound_settings(data: &RawData) -> OutboundSettings {
|
pub fn create_outbound_settings(data: &RawData) -> OutboundSettings {
|
||||||
return OutboundSettings::Vless(VlessOutboundSettings {
|
OutboundSettings::Vless(VlessOutboundSettings {
|
||||||
vnext: vec![VnextServerObject {
|
vnext: vec![VnextServerObject {
|
||||||
port: data.port,
|
port: data.port,
|
||||||
address: data.address.clone(),
|
address: data.address.clone(),
|
||||||
@@ -15,5 +15,5 @@ pub fn create_outbound_settings(data: &RawData) -> OutboundSettings {
|
|||||||
security: None,
|
security: None,
|
||||||
}]),
|
}]),
|
||||||
}],
|
}],
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,30 +1,27 @@
|
|||||||
use crate::config_models::RawData;
|
use crate::config_models::RawData;
|
||||||
use crate::parser::vmess::models::VmessAddress;
|
use crate::parser::vmess::models::VmessAddress;
|
||||||
use crate::utils::{get_parameter_value, url_decode, url_decode_str};
|
use crate::utils::{get_parameter_value, url_decode, url_decode_str};
|
||||||
use base64::{engine::general_purpose, Engine};
|
use base64::{Engine, engine::general_purpose};
|
||||||
use http::Uri;
|
use http::Uri;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
pub fn get_data(uri: &str) -> RawData {
|
pub fn get_data(uri: &str) -> RawData {
|
||||||
let data = uri.split_once("vmess://").unwrap().1;
|
let data = uri.split_once("vmess://").unwrap().1;
|
||||||
|
|
||||||
return match general_purpose::STANDARD
|
match general_purpose::STANDARD.decode(url_decode_str(data).unwrap_or(String::from(data))) {
|
||||||
.decode(url_decode_str(data).unwrap_or(String::from(data)))
|
|
||||||
{
|
|
||||||
Ok(decoded) => get_raw_data_from_base64(&decoded),
|
Ok(decoded) => get_raw_data_from_base64(&decoded),
|
||||||
Err(_) => get_raw_data_from_uri(data),
|
Err(_) => get_raw_data_from_uri(data),
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_raw_data_from_base64(decoded_base64: &Vec<u8>) -> RawData {
|
fn get_raw_data_from_base64(decoded_base64: &[u8]) -> RawData {
|
||||||
let json_str = std::str::from_utf8(decoded_base64).unwrap();
|
let json_str = std::str::from_utf8(decoded_base64).unwrap();
|
||||||
let json = serde_json::from_str::<Value>(json_str).unwrap();
|
let json = serde_json::from_str::<Value>(json_str).unwrap();
|
||||||
|
|
||||||
return RawData {
|
RawData {
|
||||||
remarks: url_decode(get_str_field(&json, "ps")).unwrap_or(String::from("")),
|
remarks: url_decode(get_str_field(&json, "ps")).unwrap_or(String::from("")),
|
||||||
uuid: get_str_field(&json, "id"),
|
uuid: get_str_field(&json, "id"),
|
||||||
port: get_str_field(&json, "port")
|
port: get_str_field(&json, "port").map(|s| s.parse::<u16>().expect("port is not a number")),
|
||||||
.and_then(|s| Some(s.parse::<u16>().expect("port is not a number"))),
|
|
||||||
address: get_str_field(&json, "add"),
|
address: get_str_field(&json, "add"),
|
||||||
alpn: url_decode(get_str_field(&json, "alpn")),
|
alpn: url_decode(get_str_field(&json, "alpn")),
|
||||||
path: url_decode(get_str_field(&json, "path")),
|
path: url_decode(get_str_field(&json, "path")),
|
||||||
@@ -59,11 +56,11 @@ fn get_raw_data_from_base64(decoded_base64: &Vec<u8>) -> RawData {
|
|||||||
allowInsecure: None,
|
allowInsecure: None,
|
||||||
server_method: None,
|
server_method: None,
|
||||||
username: None,
|
username: None,
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_str_field(json: &Value, field: &str) -> Option<String> {
|
fn get_str_field(json: &Value, field: &str) -> Option<String> {
|
||||||
return json.get(field).and_then(|v| v.as_str()).map(String::from);
|
json.get(field).and_then(|v| v.as_str()).map(String::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_raw_data_from_uri(data: &str) -> RawData {
|
fn get_raw_data_from_uri(data: &str) -> RawData {
|
||||||
@@ -75,7 +72,7 @@ fn get_raw_data_from_uri(data: &str) -> RawData {
|
|||||||
let parsed_address = parse_vmess_address(data.split_once("?").unwrap().0);
|
let parsed_address = parse_vmess_address(data.split_once("?").unwrap().0);
|
||||||
let query: Vec<(&str, &str)> = querystring::querify(raw_query);
|
let query: Vec<(&str, &str)> = querystring::querify(raw_query);
|
||||||
|
|
||||||
return RawData {
|
RawData {
|
||||||
remarks: url_decode(Some(String::from(name))).unwrap_or(String::from("")),
|
remarks: url_decode(Some(String::from(name))).unwrap_or(String::from("")),
|
||||||
uuid: Some(parsed_address.uuid),
|
uuid: Some(parsed_address.uuid),
|
||||||
port: Some(parsed_address.port),
|
port: Some(parsed_address.port),
|
||||||
@@ -105,7 +102,7 @@ fn get_raw_data_from_uri(data: &str) -> RawData {
|
|||||||
allowInsecure: get_parameter_value(&query, "allowInsecure"),
|
allowInsecure: get_parameter_value(&query, "allowInsecure"),
|
||||||
server_method: None,
|
server_method: None,
|
||||||
username: None,
|
username: None,
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_vmess_address(raw_data: &str) -> VmessAddress {
|
fn parse_vmess_address(raw_data: &str) -> VmessAddress {
|
||||||
@@ -119,9 +116,9 @@ fn parse_vmess_address(raw_data: &str) -> VmessAddress {
|
|||||||
|
|
||||||
let parsed = address_wo_slash.parse::<Uri>().unwrap();
|
let parsed = address_wo_slash.parse::<Uri>().unwrap();
|
||||||
|
|
||||||
return VmessAddress {
|
VmessAddress {
|
||||||
uuid: url_decode(Some(uuid)).unwrap(),
|
uuid: url_decode(Some(uuid)).unwrap(),
|
||||||
address: parsed.host().unwrap().to_string(),
|
address: parsed.host().unwrap().to_string(),
|
||||||
port: parsed.port().unwrap().as_u16(),
|
port: parsed.port().unwrap().as_u16(),
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ mod models;
|
|||||||
use crate::config_models::*;
|
use crate::config_models::*;
|
||||||
|
|
||||||
pub fn create_outbound_settings(data: &RawData) -> OutboundSettings {
|
pub fn create_outbound_settings(data: &RawData) -> OutboundSettings {
|
||||||
return OutboundSettings::Vmess(VmessOutboundSettings {
|
OutboundSettings::Vmess(VmessOutboundSettings {
|
||||||
vnext: vec![VnextServerObject {
|
vnext: vec![VnextServerObject {
|
||||||
port: data.port,
|
port: data.port,
|
||||||
address: data.address.clone(),
|
address: data.address.clone(),
|
||||||
@@ -15,5 +15,5 @@ pub fn create_outbound_settings(data: &RawData) -> OutboundSettings {
|
|||||||
security: data.vnext_security.clone(),
|
security: data.vnext_security.clone(),
|
||||||
}]),
|
}]),
|
||||||
}],
|
}],
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,25 +7,19 @@ pub struct InboundGenerationOptions {
|
|||||||
|
|
||||||
pub fn generate_inbound_config(options: InboundGenerationOptions) -> Vec<config_models::Inbound> {
|
pub fn generate_inbound_config(options: InboundGenerationOptions) -> Vec<config_models::Inbound> {
|
||||||
let mut inbounds: Vec<config_models::Inbound> = vec![];
|
let mut inbounds: Vec<config_models::Inbound> = vec![];
|
||||||
match options.socks_port {
|
if let Some(port) = options.socks_port {
|
||||||
Some(port) => {
|
|
||||||
inbounds.push(generate_socks_inbound(port));
|
inbounds.push(generate_socks_inbound(port));
|
||||||
}
|
}
|
||||||
None => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
match options.http_port {
|
if let Some(port) = options.http_port {
|
||||||
Some(port) => {
|
|
||||||
inbounds.push(generate_http_inbound(port));
|
inbounds.push(generate_http_inbound(port));
|
||||||
}
|
}
|
||||||
None => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
return inbounds;
|
inbounds
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_http_inbound(http_port: u16) -> config_models::Inbound {
|
pub fn generate_http_inbound(http_port: u16) -> config_models::Inbound {
|
||||||
return config_models::Inbound {
|
config_models::Inbound {
|
||||||
protocol: String::from("http"),
|
protocol: String::from("http"),
|
||||||
port: http_port,
|
port: http_port,
|
||||||
tag: String::from("http-in"),
|
tag: String::from("http-in"),
|
||||||
@@ -42,11 +36,11 @@ pub fn generate_http_inbound(http_port: u16) -> config_models::Inbound {
|
|||||||
String::from("quic"),
|
String::from("quic"),
|
||||||
]),
|
]),
|
||||||
}),
|
}),
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_socks_inbound(socks_port: u16) -> config_models::Inbound {
|
pub fn generate_socks_inbound(socks_port: u16) -> config_models::Inbound {
|
||||||
return config_models::Inbound {
|
config_models::Inbound {
|
||||||
protocol: String::from("socks"),
|
protocol: String::from("socks"),
|
||||||
port: socks_port,
|
port: socks_port,
|
||||||
tag: String::from("socks-in"),
|
tag: String::from("socks-in"),
|
||||||
@@ -63,5 +57,5 @@ pub fn generate_socks_inbound(socks_port: u16) -> config_models::Inbound {
|
|||||||
String::from("quic"),
|
String::from("quic"),
|
||||||
]),
|
]),
|
||||||
}),
|
}),
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
pub mod inbound_generator;
|
pub mod inbound_generator;
|
||||||
|
|
||||||
pub fn url_decode_str(value: &str) -> Option<String> {
|
pub fn url_decode_str(value: &str) -> Option<String> {
|
||||||
return urlencoding::decode(value)
|
urlencoding::decode(value)
|
||||||
.ok()
|
.ok()
|
||||||
.map(|decoded| decoded.into_owned());
|
.map(|decoded| decoded.into_owned())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn url_decode(value: Option<String>) -> Option<String> {
|
pub fn url_decode(value: Option<String>) -> Option<String> {
|
||||||
return value.and_then(|s| {
|
value.and_then(|s| {
|
||||||
urlencoding::decode(&s)
|
urlencoding::decode(&s)
|
||||||
.ok()
|
.ok()
|
||||||
.map(|decoded| decoded.into_owned())
|
.map(|decoded| decoded.into_owned())
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_raw_json(input: &str) -> Option<serde_json::Value> {
|
pub fn parse_raw_json(input: &str) -> Option<serde_json::Value> {
|
||||||
@@ -24,8 +24,5 @@ pub fn parse_raw_json(input: &str) -> Option<serde_json::Value> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_parameter_value(query: &Vec<(&str, &str)>, param: &str) -> Option<String> {
|
pub fn get_parameter_value(query: &Vec<(&str, &str)>, param: &str) -> Option<String> {
|
||||||
return query
|
query.iter().find(|q| q.0 == param).map(|q| q.1.to_string())
|
||||||
.iter()
|
|
||||||
.find(|q| String::from(q.0) == String::from(param))
|
|
||||||
.map(|q| q.1.to_string());
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use std::process::Stdio;
|
|
||||||
use tokio::process::{Child, Command};
|
|
||||||
use tempfile::NamedTempFile;
|
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
use std::process::Stdio;
|
||||||
|
use tempfile::NamedTempFile;
|
||||||
|
use tokio::process::{Child, Command};
|
||||||
|
|
||||||
pub struct XrayRunner {
|
pub struct XrayRunner {
|
||||||
process: Option<Child>,
|
process: Option<Child>,
|
||||||
@@ -16,7 +16,11 @@ impl XrayRunner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn start(&mut self, config_json: &str, xray_binary: &str) -> Result<(), Box<dyn std::error::Error>> {
|
pub async fn start(
|
||||||
|
&mut self,
|
||||||
|
config_json: &str,
|
||||||
|
xray_binary: &str,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
// Create temporary config file with .json extension
|
// Create temporary config file with .json extension
|
||||||
let mut temp_file = NamedTempFile::with_suffix(".json")?;
|
let mut temp_file = NamedTempFile::with_suffix(".json")?;
|
||||||
temp_file.write_all(config_json.as_bytes())?;
|
temp_file.write_all(config_json.as_bytes())?;
|
||||||
@@ -46,7 +50,7 @@ impl XrayRunner {
|
|||||||
println!("Stopping xray-core process...");
|
println!("Stopping xray-core process...");
|
||||||
|
|
||||||
// Try graceful shutdown first
|
// Try graceful shutdown first
|
||||||
if let Err(_) = process.kill().await {
|
if (process.kill().await).is_err() {
|
||||||
eprintln!("Failed to kill xray-core process gracefully");
|
eprintln!("Failed to kill xray-core process gracefully");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,7 +62,7 @@ impl XrayRunner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Cleanup config file
|
// Cleanup config file
|
||||||
if let Some(_) = self.config_file.take() {
|
if self.config_file.take().is_some() {
|
||||||
println!("Cleaned up temporary config file");
|
println!("Cleaned up temporary config file");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,6 +72,7 @@ impl XrayRunner {
|
|||||||
|
|
||||||
impl Drop for XrayRunner {
|
impl Drop for XrayRunner {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
|
#[allow(unused_mut)]
|
||||||
if let Some(mut process) = self.process.take() {
|
if let Some(mut process) = self.process.take() {
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
{
|
{
|
||||||
@@ -89,11 +94,11 @@ impl Drop for XrayRunner {
|
|||||||
pub async fn wait_for_shutdown_signal() {
|
pub async fn wait_for_shutdown_signal() {
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
{
|
{
|
||||||
|
use futures::stream::StreamExt;
|
||||||
use signal_hook::consts::signal::*;
|
use signal_hook::consts::signal::*;
|
||||||
use signal_hook_tokio::Signals;
|
use signal_hook_tokio::Signals;
|
||||||
use futures::stream::StreamExt;
|
|
||||||
|
|
||||||
let mut signals = Signals::new(&[SIGINT, SIGTERM]).expect("Failed to create signals");
|
let mut signals = Signals::new([SIGINT, SIGTERM]).expect("Failed to create signals");
|
||||||
|
|
||||||
while let Some(signal) = signals.next().await {
|
while let Some(signal) = signals.next().await {
|
||||||
match signal {
|
match signal {
|
||||||
|
|||||||
Reference in New Issue
Block a user