mirror of
https://github.com/bolucat/Archive.git
synced 2026-04-22 16:07:49 +08:00
Update On Thu Oct 2 20:41:06 CEST 2025
This commit is contained in:
@@ -1138,3 +1138,4 @@ Update On Sun Sep 28 20:32:41 CEST 2025
|
||||
Update On Mon Sep 29 20:38:41 CEST 2025
|
||||
Update On Tue Sep 30 20:35:53 CEST 2025
|
||||
Update On Wed Oct 1 20:41:09 CEST 2025
|
||||
Update On Thu Oct 2 20:40:58 CEST 2025
|
||||
|
||||
@@ -287,6 +287,11 @@ pub struct IVerge {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub network_statistic_widget: Option<NetworkStatisticWidgetConfig>,
|
||||
|
||||
/// PAC URL for automatic proxy configuration
|
||||
/// This field is used to set PAC proxy without exposing it to the frontend UI
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub pac_url: Option<String>,
|
||||
|
||||
/// enable tray text display on Linux systems
|
||||
/// When enabled, shows proxy and TUN mode status as text next to the tray icon
|
||||
/// When disabled, only shows status via icon changes (prevents text display issues on Wayland)
|
||||
|
||||
@@ -4,6 +4,7 @@ pub mod handle;
|
||||
pub mod hotkey;
|
||||
pub mod logger;
|
||||
pub mod manager;
|
||||
pub mod pac;
|
||||
pub mod service;
|
||||
pub mod storage;
|
||||
pub mod sysopt;
|
||||
|
||||
@@ -0,0 +1,332 @@
|
||||
use crate::{config::Config, log_err};
|
||||
use anyhow::{Context, Result};
|
||||
use std::{path::PathBuf, time::Duration};
|
||||
use sysproxy::Autoproxy;
|
||||
use tokio::fs;
|
||||
|
||||
/// PAC module for handling Proxy Auto-Configuration
|
||||
pub struct PacManager;
|
||||
|
||||
// Constants for PAC handling
|
||||
const PAC_DOWNLOAD_TIMEOUT: u64 = 30; // seconds
|
||||
const PAC_MAX_RETRIES: u32 = 3;
|
||||
const PAC_RETRY_DELAY: u64 = 5; // seconds
|
||||
|
||||
impl PacManager {
|
||||
/// Get PAC URL from config
|
||||
pub fn get_pac_url() -> Option<String> {
|
||||
Config::verge().latest().pac_url.clone()
|
||||
}
|
||||
|
||||
/// Check if PAC is enabled (URL is set)
|
||||
pub fn is_pac_enabled() -> bool {
|
||||
Self::get_pac_url().is_some_and(|url| !url.is_empty())
|
||||
}
|
||||
|
||||
/// Download PAC script from URL with retry logic
|
||||
pub async fn download_pac_script(url: &str) -> Result<String> {
|
||||
let client = reqwest::Client::builder()
|
||||
.timeout(Duration::from_secs(PAC_DOWNLOAD_TIMEOUT))
|
||||
.build()
|
||||
.context("failed to build HTTP client")?;
|
||||
|
||||
// Retry logic
|
||||
let mut last_error = None;
|
||||
for attempt in 1..=PAC_MAX_RETRIES {
|
||||
match client.get(url).send().await {
|
||||
Ok(response) => {
|
||||
if response.status().is_success() {
|
||||
match response.text().await {
|
||||
Ok(content) => return Ok(content),
|
||||
Err(e) => {
|
||||
let err =
|
||||
anyhow::anyhow!("failed to read PAC script content: {}", e);
|
||||
log::warn!(target: "app", "Attempt {}/{} failed: {}", attempt, PAC_MAX_RETRIES, err);
|
||||
last_error = Some(err);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let err = anyhow::anyhow!(
|
||||
"failed to download PAC script, status: {}",
|
||||
response.status()
|
||||
);
|
||||
log::warn!(target: "app", "Attempt {}/{} failed: {}", attempt, PAC_MAX_RETRIES, err);
|
||||
last_error = Some(err);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
let err = anyhow::anyhow!("failed to download PAC script: {}", e);
|
||||
log::warn!(target: "app", "Attempt {}/{} failed: {}", attempt, PAC_MAX_RETRIES, err);
|
||||
last_error = Some(err);
|
||||
}
|
||||
}
|
||||
|
||||
// Wait before retrying (except on last attempt)
|
||||
if attempt < PAC_MAX_RETRIES {
|
||||
tokio::time::sleep(Duration::from_secs(PAC_RETRY_DELAY)).await;
|
||||
}
|
||||
}
|
||||
|
||||
Err(last_error.unwrap_or_else(|| {
|
||||
anyhow::anyhow!(
|
||||
"failed to download PAC script after {} attempts",
|
||||
PAC_MAX_RETRIES
|
||||
)
|
||||
}))
|
||||
}
|
||||
|
||||
/// Save PAC script to cache directory
|
||||
pub async fn save_pac_script(script: &str) -> Result<PathBuf> {
|
||||
let cache_dir = crate::utils::dirs::cache_dir()?;
|
||||
let pac_file = cache_dir.join("pac.js");
|
||||
|
||||
fs::write(&pac_file, script)
|
||||
.await
|
||||
.context("failed to save PAC script")?;
|
||||
|
||||
Ok(pac_file)
|
||||
}
|
||||
|
||||
/// Basic validation of PAC script structure - check for required functions
|
||||
pub async fn validate_pac_script(script: &str) -> Result<()> {
|
||||
// A basic validation without using the JS engine - just check if FindProxyForURL function exists
|
||||
if !script.contains("FindProxyForURL") {
|
||||
return Err(anyhow::anyhow!(
|
||||
"PAC script must contain FindProxyForURL function"
|
||||
));
|
||||
}
|
||||
|
||||
// Additional basic checks could be added here if needed
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set system proxy to use PAC URL
|
||||
pub fn set_pac_proxy(url: &str) -> Result<()> {
|
||||
// Check if Autoproxy is supported on this platform
|
||||
if !Autoproxy::is_support() {
|
||||
return Err(anyhow::anyhow!(
|
||||
"PAC proxy is not supported on this platform"
|
||||
));
|
||||
}
|
||||
|
||||
let autoproxy = Autoproxy {
|
||||
enable: true,
|
||||
url: url.to_string(),
|
||||
};
|
||||
|
||||
autoproxy
|
||||
.set_auto_proxy()
|
||||
.context("failed to set PAC proxy")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Disable PAC proxy and revert to direct proxy
|
||||
pub fn disable_pac_proxy() -> Result<()> {
|
||||
// Check if Autoproxy is supported on this platform
|
||||
if !Autoproxy::is_support() {
|
||||
log::info!(target: "app", "PAC proxy is not supported on this platform, skipping disable");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let autoproxy = Autoproxy {
|
||||
enable: false,
|
||||
url: String::new(),
|
||||
};
|
||||
|
||||
autoproxy
|
||||
.set_auto_proxy()
|
||||
.context("failed to disable PAC proxy")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Fallback to direct proxy when PAC fails
|
||||
pub fn fallback_to_direct_proxy() -> Result<()> {
|
||||
log::warn!(target: "app", "Falling back to direct proxy mode");
|
||||
|
||||
// Check if Sysproxy is supported on this platform
|
||||
if !sysproxy::Sysproxy::is_support() {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Direct proxy is not supported on this platform"
|
||||
));
|
||||
}
|
||||
|
||||
// Get the standard proxy settings
|
||||
let port = Config::verge()
|
||||
.latest()
|
||||
.verge_mixed_port
|
||||
.unwrap_or(Config::clash().data().get_mixed_port());
|
||||
|
||||
let (enable, bypass) = {
|
||||
let verge = Config::verge();
|
||||
let verge = verge.latest();
|
||||
(
|
||||
verge.enable_system_proxy.unwrap_or(false),
|
||||
verge.system_proxy_bypass.clone(),
|
||||
)
|
||||
};
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
let default_bypass = "localhost;127.*;192.168.*;10.*;172.16.*;172.17.*;172.18.*;172.19.*;172.20.*;172.21.*;172.22.*;172.23.*;172.24.*;172.25.*;172.26.*;172.27.*;172.28.*;172.29.*;172.30.*;172.31.*;<local>";
|
||||
#[cfg(target_os = "linux")]
|
||||
let default_bypass = "localhost,127.0.0.1,192.168.0.0/16,10.0.0.0/8,172.16.0.0/12,::1";
|
||||
#[cfg(target_os = "macos")]
|
||||
let default_bypass = "127.0.0.1,192.168.0.0/16,10.0.0.0/8,172.16.0.0/12,localhost,*.local,*.crashlytics.com,<local>";
|
||||
|
||||
let sysproxy = sysproxy::Sysproxy {
|
||||
enable,
|
||||
host: String::from("127.0.0.1"),
|
||||
port,
|
||||
bypass: bypass.unwrap_or(default_bypass.into()),
|
||||
};
|
||||
|
||||
sysproxy
|
||||
.set_system_proxy()
|
||||
.context("failed to set direct proxy as fallback")?;
|
||||
|
||||
log::info!(target: "app", "Fallback to direct proxy successful");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Update PAC configuration with error handling and fallback
|
||||
pub async fn update_pac() -> Result<()> {
|
||||
if !Self::is_pac_enabled() {
|
||||
log::info!(target: "app", "PAC is not enabled, skipping update");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Check if Autoproxy is supported on this platform
|
||||
if !Autoproxy::is_support() {
|
||||
log::warn!(target: "app", "PAC proxy is not supported on this platform");
|
||||
// Try to fallback to direct proxy
|
||||
log_err!(Self::fallback_to_direct_proxy());
|
||||
return Err(anyhow::anyhow!(
|
||||
"PAC proxy is not supported on this platform"
|
||||
));
|
||||
}
|
||||
|
||||
let pac_url = Self::get_pac_url().unwrap();
|
||||
log::info!(target: "app", "Updating PAC from URL: {}", pac_url);
|
||||
|
||||
// Download PAC script
|
||||
let script = match Self::download_pac_script(&pac_url).await {
|
||||
Ok(script) => script,
|
||||
Err(e) => {
|
||||
log::error!(target: "app", "Failed to download PAC script: {}", e);
|
||||
// Try to fallback to direct proxy
|
||||
log_err!(Self::fallback_to_direct_proxy());
|
||||
return Err(e);
|
||||
}
|
||||
};
|
||||
|
||||
// Validate PAC script
|
||||
if let Err(e) = Self::validate_pac_script(&script).await {
|
||||
log::error!(target: "app", "PAC script validation failed: {}", e);
|
||||
// Try to fallback to direct proxy
|
||||
log_err!(Self::fallback_to_direct_proxy());
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
// Save PAC script to cache
|
||||
if let Err(e) = Self::save_pac_script(&script).await {
|
||||
log::warn!(target: "app", "Failed to save PAC script to cache: {}", e);
|
||||
// This is not critical, continue with setting the proxy
|
||||
}
|
||||
|
||||
// Set system proxy to use PAC
|
||||
if let Err(e) = Self::set_pac_proxy(&pac_url) {
|
||||
log::error!(target: "app", "Failed to set PAC proxy: {}", e);
|
||||
// Try to fallback to direct proxy
|
||||
log_err!(Self::fallback_to_direct_proxy());
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
log::info!(target: "app", "PAC updated successfully");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Initialize PAC proxy on startup with error handling
|
||||
pub async fn init_pac_proxy() -> Result<()> {
|
||||
if !Self::is_pac_enabled() {
|
||||
log::info!(target: "app", "PAC is not enabled, skipping initialization");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
log::info!(target: "app", "Initializing PAC proxy");
|
||||
if let Err(e) = Self::update_pac().await {
|
||||
log::error!(target: "app", "Failed to initialize PAC proxy: {}", e);
|
||||
return Err(e);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use tokio;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_pac_download() {
|
||||
// Test with a known good PAC URL
|
||||
let pac_url = "https://raw.githubusercontent.com/Slinetrac/clash-nyanpasu/main/test.pac";
|
||||
|
||||
match PacManager::download_pac_script(pac_url).await {
|
||||
Ok(script) => {
|
||||
assert!(!script.is_empty());
|
||||
println!(
|
||||
"Downloaded PAC script: {}",
|
||||
&script[..std::cmp::min(100, script.len())]
|
||||
);
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Failed to download PAC script: {}", e);
|
||||
// This might fail in test environment, so we won't assert failure
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_pac_validation() {
|
||||
let valid_pac_script = r#"
|
||||
function FindProxyForURL(url, host) {
|
||||
return "DIRECT";
|
||||
}
|
||||
"#;
|
||||
|
||||
assert!(
|
||||
PacManager::validate_pac_script(valid_pac_script)
|
||||
.await
|
||||
.is_ok()
|
||||
);
|
||||
|
||||
let invalid_pac_script = r#"
|
||||
function SomeOtherFunction(url, host) {
|
||||
// This script does not contain the required function
|
||||
return "PROXY proxy.example.com:8080";
|
||||
}
|
||||
"#;
|
||||
|
||||
assert!(
|
||||
PacManager::validate_pac_script(invalid_pac_script)
|
||||
.await
|
||||
.is_err()
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_pac_save() {
|
||||
let script = "function FindProxyForURL(url, host) { return 'DIRECT'; }";
|
||||
match PacManager::save_pac_script(script).await {
|
||||
Ok(path) => {
|
||||
assert!(path.exists());
|
||||
// Clean up
|
||||
let _ = tokio::fs::remove_file(path).await;
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Failed to save PAC script: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,10 @@ use std::sync::Arc;
|
||||
use sysproxy::Sysproxy;
|
||||
use tauri::{async_runtime::Mutex as TokioMutex, utils::platform::current_exe};
|
||||
|
||||
// Import PAC manager
|
||||
#[cfg(feature = "default-meta")]
|
||||
use crate::core::pac::PacManager;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
use std::process::Command;
|
||||
|
||||
@@ -66,6 +70,22 @@ impl Sysopt {
|
||||
|
||||
/// init the sysproxy
|
||||
pub fn init_sysproxy(&self) -> Result<()> {
|
||||
// Check if PAC is enabled first
|
||||
#[cfg(feature = "default-meta")]
|
||||
if PacManager::is_pac_enabled() {
|
||||
log::info!(target: "app", "Initializing PAC proxy");
|
||||
// For PAC, we don't set the regular system proxy
|
||||
// Instead, we let the PAC manager handle it
|
||||
tauri::async_runtime::spawn(async {
|
||||
if let Err(e) = PacManager::init_pac_proxy().await {
|
||||
log::error!(target: "app", "Failed to initialize PAC proxy: {}", e);
|
||||
}
|
||||
});
|
||||
// run the system proxy guard
|
||||
self.guard_proxy();
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let port = Config::verge()
|
||||
.latest()
|
||||
.verge_mixed_port
|
||||
@@ -89,7 +109,10 @@ impl Sysopt {
|
||||
|
||||
if enable {
|
||||
let old = Sysproxy::get_system_proxy().ok();
|
||||
current.set_system_proxy()?;
|
||||
if let Err(e) = current.set_system_proxy() {
|
||||
log::error!(target: "app", "Failed to set system proxy: {}", e);
|
||||
return Err(e.into()); // Convert sysproxy::Error to anyhow::Error
|
||||
}
|
||||
|
||||
*self.old_sysproxy.lock() = old;
|
||||
*self.cur_sysproxy.lock() = Some(current);
|
||||
@@ -102,6 +125,18 @@ impl Sysopt {
|
||||
|
||||
/// update the system proxy
|
||||
pub fn update_sysproxy(&self) -> Result<()> {
|
||||
// Check if PAC is enabled first
|
||||
#[cfg(feature = "default-meta")]
|
||||
if PacManager::is_pac_enabled() {
|
||||
log::info!(target: "app", "Updating PAC proxy");
|
||||
// For PAC, we don't set the regular system proxy
|
||||
// Instead, we let the PAC manager handle it
|
||||
tauri::async_runtime::spawn(async {
|
||||
log_err!(PacManager::update_pac().await);
|
||||
});
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut cur_sysproxy = self.cur_sysproxy.lock();
|
||||
let old_sysproxy = self.old_sysproxy.lock();
|
||||
|
||||
@@ -132,6 +167,14 @@ impl Sysopt {
|
||||
|
||||
/// reset the sysproxy
|
||||
pub fn reset_sysproxy(&self) -> Result<()> {
|
||||
// Check if PAC is enabled first
|
||||
#[cfg(feature = "default-meta")]
|
||||
if PacManager::is_pac_enabled() {
|
||||
log::info!(target: "app", "Resetting PAC proxy");
|
||||
// Disable PAC proxy
|
||||
log_err!(PacManager::disable_pac_proxy());
|
||||
}
|
||||
|
||||
let mut cur_sysproxy = self.cur_sysproxy.lock();
|
||||
let mut old_sysproxy = self.old_sysproxy.lock();
|
||||
|
||||
|
||||
@@ -1068,6 +1068,11 @@ export type IVerge = {
|
||||
* 是否启用网络统计信息浮窗
|
||||
*/
|
||||
network_statistic_widget?: NetworkStatisticWidgetConfig | null
|
||||
/**
|
||||
* PAC URL for automatic proxy configuration
|
||||
* This field is used to set PAC proxy without exposing it to the frontend UI
|
||||
*/
|
||||
pac_url?: string | null
|
||||
/**
|
||||
* enable tray text display on Linux systems
|
||||
* When enabled, shows proxy and TUN mode status as text next to the tray icon
|
||||
|
||||
@@ -1,8 +1,21 @@
|
||||
Package/en8811h-firmware = $(call Package/firmware-default,Airoha EN8811H PHY firmware)
|
||||
define Package/en8811h-firmware/install
|
||||
Package/airoha-en8811h-firmware = $(call Package/firmware-default,Airoha EN8811H 2.5G Ethernet PHY firmware)
|
||||
define Package/airoha-en8811h-firmware/install
|
||||
$(INSTALL_DIR) $(1)/lib/firmware/airoha
|
||||
$(CP) \
|
||||
$(PKG_BUILD_DIR)/airoha/*.bin \
|
||||
$(PKG_BUILD_DIR)/airoha/EthMD32.dm.bin \
|
||||
$(PKG_BUILD_DIR)/airoha/EthMD32.DSP.bin \
|
||||
$(1)/lib/firmware/airoha
|
||||
endef
|
||||
$(eval $(call BuildPackage,en8811h-firmware))
|
||||
|
||||
$(eval $(call BuildPackage,airoha-en8811h-firmware))
|
||||
|
||||
Package/airoha-en7581-npu-firmware = $(call Package/firmware-default,Airoha EN7581 NPU firmware)
|
||||
define Package/airoha-en7581-npu-firmware/install
|
||||
$(INSTALL_DIR) $(1)/lib/firmware/airoha
|
||||
$(CP) \
|
||||
$(PKG_BUILD_DIR)/airoha/en7581_npu_data.bin \
|
||||
$(PKG_BUILD_DIR)/airoha/en7581_npu_rv32.bin \
|
||||
$(1)/lib/firmware/airoha
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,airoha-en7581-npu-firmware))
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=hysteria
|
||||
PKG_VERSION:=2.6.3
|
||||
PKG_VERSION:=2.6.4
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:=https://codeload.github.com/apernet/hysteria/tar.gz/app/v$(PKG_VERSION)?
|
||||
PKG_HASH:=bed1ece93dfaa07fbf709136efadaf4ccb09e0375844de3e28c5644ebe518eb0
|
||||
PKG_HASH:=9d989174736654f8608a4ba7c1c49306e4b97c6a19d65d926c10a8521de7b2be
|
||||
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-app-v$(PKG_VERSION)
|
||||
|
||||
PKG_LICENSE:=MIT
|
||||
|
||||
@@ -74,7 +74,8 @@ func (r *rootEnvImpl) DropProxyEnvironment(tag string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return transientStorage.DropScope(r.ctx, tag)
|
||||
transientStorage.Clear(r.ctx)
|
||||
return r.transientStorage.DropScope(r.ctx, tag)
|
||||
}
|
||||
|
||||
type appEnvImpl struct {
|
||||
|
||||
@@ -56,7 +56,16 @@ func (s *scopedTransientStorageImpl) List(ctx context.Context, keyPrefix string)
|
||||
func (s *scopedTransientStorageImpl) Clear(ctx context.Context) {
|
||||
s.access.Lock()
|
||||
defer s.access.Unlock()
|
||||
for _, v := range s.values {
|
||||
if sw, ok := v.(storage.TransientStorageLifecycleReceiver); ok {
|
||||
_ = sw.Close()
|
||||
}
|
||||
}
|
||||
s.values = map[string]interface{}{}
|
||||
for _, v := range s.scopes {
|
||||
v.Clear(ctx)
|
||||
}
|
||||
s.scopes = map[string]storage.ScopedTransientStorage{}
|
||||
}
|
||||
|
||||
func (s *scopedTransientStorageImpl) NarrowScope(ctx context.Context, key string) (storage.ScopedTransientStorage, error) {
|
||||
@@ -74,6 +83,9 @@ func (s *scopedTransientStorageImpl) NarrowScope(ctx context.Context, key string
|
||||
func (s *scopedTransientStorageImpl) DropScope(ctx context.Context, key string) error {
|
||||
s.access.Lock()
|
||||
defer s.access.Unlock()
|
||||
if v, ok := s.scopes[key]; ok {
|
||||
v.Clear(ctx)
|
||||
}
|
||||
delete(s.scopes, key)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package storage
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/v2fly/v2ray-core/v5/common"
|
||||
"github.com/v2fly/v2ray-core/v5/features"
|
||||
)
|
||||
|
||||
@@ -32,3 +33,8 @@ type ScopedPersistentStorageService interface {
|
||||
}
|
||||
|
||||
var ScopedPersistentStorageServiceType = (*ScopedPersistentStorageService)(nil)
|
||||
|
||||
type TransientStorageLifecycleReceiver interface {
|
||||
IsTransientStorageLifecycleReceiver()
|
||||
common.Closable
|
||||
}
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ require (
|
||||
github.com/pion/dtls/v2 v2.2.12
|
||||
github.com/pion/transport/v2 v2.2.10
|
||||
github.com/pires/go-proxyproto v0.8.1
|
||||
github.com/quic-go/quic-go v0.54.0
|
||||
github.com/quic-go/quic-go v0.54.1
|
||||
github.com/refraction-networking/utls v1.8.0
|
||||
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb
|
||||
github.com/stretchr/testify v1.11.1
|
||||
|
||||
+2
-2
@@ -440,8 +440,8 @@ github.com/prometheus/procfs v0.3.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
||||
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
||||
github.com/quic-go/quic-go v0.54.0 h1:6s1YB9QotYI6Ospeiguknbp2Znb/jZYjZLRXn9kMQBg=
|
||||
github.com/quic-go/quic-go v0.54.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY=
|
||||
github.com/quic-go/quic-go v0.54.1 h1:4ZAWm0AhCb6+hE+l5Q1NAL0iRn/ZrMwqHRGQiFwj2eg=
|
||||
github.com/quic-go/quic-go v0.54.1/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/refraction-networking/utls v1.8.0 h1:L38krhiTAyj9EeiQQa2sg+hYb4qwLCqdMcpZrRfbONE=
|
||||
github.com/refraction-networking/utls v1.8.0/go.mod h1:jkSOEkLqn+S/jtpEHPOsVv/4V4EVnelwbMQl4vCWXAM=
|
||||
|
||||
@@ -17,6 +17,8 @@ import (
|
||||
|
||||
core "github.com/v2fly/v2ray-core/v5"
|
||||
"github.com/v2fly/v2ray-core/v5/common"
|
||||
"github.com/v2fly/v2ray-core/v5/common/environment"
|
||||
"github.com/v2fly/v2ray-core/v5/common/environment/envctx"
|
||||
"github.com/v2fly/v2ray-core/v5/common/net"
|
||||
"github.com/v2fly/v2ray-core/v5/common/session"
|
||||
"github.com/v2fly/v2ray-core/v5/transport/internet"
|
||||
@@ -38,12 +40,25 @@ func init() {
|
||||
common.Must(internet.RegisterTransportDialer(protocolName, Dial))
|
||||
}
|
||||
|
||||
type dialerCanceller func()
|
||||
type transportConnectionState struct {
|
||||
scopedDialerMap map[net.Destination]*grpc.ClientConn
|
||||
scopedDialerAccess sync.Mutex
|
||||
}
|
||||
|
||||
var (
|
||||
globalDialerMap map[net.Destination]*grpc.ClientConn
|
||||
globalDialerAccess sync.Mutex
|
||||
)
|
||||
func (t *transportConnectionState) IsTransientStorageLifecycleReceiver() {
|
||||
}
|
||||
|
||||
func (t *transportConnectionState) Close() error {
|
||||
t.scopedDialerAccess.Lock()
|
||||
defer t.scopedDialerAccess.Unlock()
|
||||
for _, conn := range t.scopedDialerMap {
|
||||
_ = conn.Close()
|
||||
}
|
||||
t.scopedDialerMap = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
type dialerCanceller func()
|
||||
|
||||
func dialgRPC(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (net.Conn, error) {
|
||||
grpcSettings := streamSettings.ProtocolSettings.(*Config)
|
||||
@@ -70,25 +85,36 @@ func dialgRPC(ctx context.Context, dest net.Destination, streamSettings *interne
|
||||
}
|
||||
|
||||
func getGrpcClient(ctx context.Context, dest net.Destination, dialOption grpc.DialOption, streamSettings *internet.MemoryStreamConfig) (*grpc.ClientConn, dialerCanceller, error) {
|
||||
globalDialerAccess.Lock()
|
||||
defer globalDialerAccess.Unlock()
|
||||
transportEnvironment := envctx.EnvironmentFromContext(ctx).(environment.TransportEnvironment)
|
||||
state, err := transportEnvironment.TransientStorage().Get(ctx, "grpc-transport-connection-state")
|
||||
if err != nil {
|
||||
state = &transportConnectionState{}
|
||||
transportEnvironment.TransientStorage().Put(ctx, "grpc-transport-connection-state", state)
|
||||
state, err = transportEnvironment.TransientStorage().Get(ctx, "grpc-transport-connection-state")
|
||||
if err != nil {
|
||||
return nil, nil, newError("failed to get grpc transport connection state").Base(err)
|
||||
}
|
||||
}
|
||||
stateTyped := state.(*transportConnectionState)
|
||||
|
||||
if globalDialerMap == nil {
|
||||
globalDialerMap = make(map[net.Destination]*grpc.ClientConn)
|
||||
stateTyped.scopedDialerAccess.Lock()
|
||||
defer stateTyped.scopedDialerAccess.Unlock()
|
||||
|
||||
if stateTyped.scopedDialerMap == nil {
|
||||
stateTyped.scopedDialerMap = make(map[net.Destination]*grpc.ClientConn)
|
||||
}
|
||||
|
||||
canceller := func() {
|
||||
globalDialerAccess.Lock()
|
||||
defer globalDialerAccess.Unlock()
|
||||
delete(globalDialerMap, dest)
|
||||
stateTyped.scopedDialerAccess.Lock()
|
||||
defer stateTyped.scopedDialerAccess.Unlock()
|
||||
delete(stateTyped.scopedDialerMap, dest)
|
||||
}
|
||||
|
||||
// TODO Should support chain proxy to the same destination
|
||||
if client, found := globalDialerMap[dest]; found && client.GetState() != connectivity.Shutdown {
|
||||
if client, found := stateTyped.scopedDialerMap[dest]; found && client.GetState() != connectivity.Shutdown {
|
||||
return client, canceller, nil
|
||||
}
|
||||
|
||||
conn, err := grpc.Dial(
|
||||
conn, err := grpc.NewClient(
|
||||
dest.Address.String()+":"+dest.Port.String(),
|
||||
dialOption,
|
||||
grpc.WithConnectParams(grpc.ConnectParams{
|
||||
@@ -117,6 +143,14 @@ func getGrpcClient(ctx context.Context, dest net.Destination, dialOption grpc.Di
|
||||
return internet.DialSystem(detachedContext, net.TCPDestination(address, port), streamSettings.SocketSettings)
|
||||
}),
|
||||
)
|
||||
globalDialerMap[dest] = conn
|
||||
canceller = func() {
|
||||
stateTyped.scopedDialerAccess.Lock()
|
||||
defer stateTyped.scopedDialerAccess.Unlock()
|
||||
delete(stateTyped.scopedDialerMap, dest)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
}
|
||||
}
|
||||
stateTyped.scopedDialerMap[dest] = conn
|
||||
return conn, canceller, err
|
||||
}
|
||||
|
||||
@@ -67,116 +67,4 @@ public static class ProcUtils
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task ProcessKill(int pid)
|
||||
{
|
||||
try
|
||||
{
|
||||
await ProcessKill(Process.GetProcessById(pid), false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task ProcessKill(Process? proc, bool review)
|
||||
{
|
||||
if (proc is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GetProcessKeyInfo(proc, review, out var procId, out var fileName, out var processName);
|
||||
|
||||
try
|
||||
{
|
||||
if (Utils.IsNonWindows())
|
||||
{
|
||||
proc?.Kill(true);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
proc?.Kill();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
proc?.Close();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
proc?.Dispose();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
|
||||
await Task.Delay(300);
|
||||
await ProcessKillByKeyInfo(review, procId, fileName, processName);
|
||||
}
|
||||
|
||||
private static void GetProcessKeyInfo(Process? proc, bool review, out int? procId, out string? fileName, out string? processName)
|
||||
{
|
||||
procId = null;
|
||||
fileName = null;
|
||||
processName = null;
|
||||
if (!review)
|
||||
{
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
procId = proc?.Id;
|
||||
fileName = proc?.MainModule?.FileName;
|
||||
processName = proc?.ProcessName;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task ProcessKillByKeyInfo(bool review, int? procId, string? fileName, string? processName)
|
||||
{
|
||||
if (review && procId != null && fileName != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var lstProc = Process.GetProcessesByName(processName);
|
||||
foreach (var proc2 in lstProc)
|
||||
{
|
||||
if (proc2.Id == procId)
|
||||
{
|
||||
Logging.SaveLog($"{_tag}, KillProcess not completing the job, procId");
|
||||
await ProcessKill(proc2, false);
|
||||
}
|
||||
if (proc2.MainModule != null && proc2.MainModule?.FileName == fileName)
|
||||
{
|
||||
Logging.SaveLog($"{_tag}, KillProcess not completing the job, fileName");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -7,11 +7,11 @@ namespace ServiceLib.Common;
|
||||
* http://stackoverflow.com/questions/6266820/working-example-of-createjobobject-setinformationjobobject-pinvoke-in-net
|
||||
*/
|
||||
|
||||
public sealed class Job : IDisposable
|
||||
public sealed class WindowsJob : IDisposable
|
||||
{
|
||||
private IntPtr handle = IntPtr.Zero;
|
||||
|
||||
public Job()
|
||||
public WindowsJob()
|
||||
{
|
||||
handle = CreateJobObject(IntPtr.Zero, null);
|
||||
var extendedInfoPtr = IntPtr.Zero;
|
||||
@@ -94,7 +94,7 @@ namespace ServiceLib.Common;
|
||||
}
|
||||
}
|
||||
|
||||
~Job()
|
||||
~WindowsJob()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
@@ -8,7 +8,7 @@ public sealed class AppManager
|
||||
private Config _config;
|
||||
private int? _statePort;
|
||||
private int? _statePort2;
|
||||
private Job? _processJob;
|
||||
private WindowsJob? _processJob;
|
||||
public static AppManager Instance => _instance.Value;
|
||||
public Config Config => _config;
|
||||
|
||||
|
||||
@@ -178,5 +178,6 @@ public class ProcessService : IDisposable
|
||||
}
|
||||
|
||||
_isDisposed = true;
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user