From 258ae1804d497ba1fac827f22db2e7ddfd5d7acf Mon Sep 17 00:00:00 2001 From: "github-action[bot]" Date: Sun, 7 Apr 2024 20:25:37 +0200 Subject: [PATCH] Update On Sun Apr 7 20:25:36 CEST 2024 --- .github/update.log | 1 + .../src/main/golang/native/config/fetch.go | 2 +- clash-meta/adapter/provider/parser.go | 5 +- clash-meta/adapter/provider/provider.go | 4 +- clash-meta/component/geodata/init.go | 4 +- clash-meta/component/http/http.go | 6 +- clash-meta/component/mmdb/mmdb.go | 4 +- clash-meta/component/resource/vehicle.go | 2 +- clash-meta/config/utils.go | 2 +- clash-meta/hub/updater/updater.go | 4 +- clash-meta/rules/provider/parse.go | 5 +- clash-nyanpasu/.github/workflows/release.yml | 12 + clash-nyanpasu/README.md | 7 + clash-nyanpasu/backend/tauri/src/cmds.rs | 35 +- .../backend/tauri/src/core/commands/mod.rs | 44 ++- .../backend/tauri/src/core/storage.rs | 6 + .../backend/tauri/src/core/tray/mod.rs | 4 +- clash-nyanpasu/backend/tauri/src/main.rs | 17 +- .../backend/tauri/src/utils/help.rs | 35 +- .../backend/tauri/src/utils/init/mod.rs | 17 +- clash-nyanpasu/manifest/version.json | 4 +- clash-nyanpasu/package.json | 2 +- clash-nyanpasu/pnpm-lock.yaml | 8 +- .../src/components/setting/setting-verge.tsx | 4 +- clash-nyanpasu/src/services/cmds.ts | 4 + echo/.github/workflows/cd.yaml | 9 +- echo/README.md | 300 +----------------- echo/internal/cli/app.go | 2 +- echo/internal/cli/config.go | 6 +- echo/internal/constant/constant.go | 21 +- echo/internal/relay/conf/cfg.go | 45 ++- echo/internal/relay/relay.go | 124 ++------ echo/internal/relay/server.go | 6 +- echo/internal/transporter/base.go | 55 ++++ echo/internal/transporter/interface.go | 70 ++-- echo/internal/transporter/mux.go | 63 ++++ echo/internal/transporter/raw.go | 84 ++--- echo/internal/transporter/raw_mux.go | 134 +++----- echo/internal/transporter/ws.go | 78 ++--- echo/internal/transporter/wss.go | 70 ++-- echo/internal/transporter/wss_mux.go | 151 +++------ .../transporter => pkg/buffer}/buffer.go | 2 +- echo/pkg/sub/clash_types.go | 4 +- echo/test/relay_test.go | 36 +-- mihomo/adapter/provider/parser.go | 5 +- mihomo/adapter/provider/provider.go | 4 +- mihomo/component/geodata/init.go | 4 +- mihomo/component/http/http.go | 6 +- mihomo/component/mmdb/mmdb.go | 4 +- mihomo/component/resource/vehicle.go | 2 +- mihomo/config/utils.go | 2 +- mihomo/hub/updater/updater.go | 4 +- mihomo/rules/provider/parse.go | 5 +- openwrt-packages/luci-app-store/Makefile | 2 +- .../luci-app-store/root/bin/is-opkg | 8 +- small/chinadns-ng/Makefile | 4 +- .../model/cbi/shadowsocksr/client-config.lua | 20 ++ small/luci-app-ssr-plus/po/zh-cn/ssr-plus.po | 21 ++ .../usr/share/shadowsocksr/gen_config.lua | 27 +- suyu/.ci/scripts/windows/docker.sh | 10 +- suyu/.forgejo/workflows/verify.yml | 4 +- xray-core/app/proxyman/outbound/handler.go | 33 +- xray-core/go.mod | 10 +- xray-core/go.sum | 20 +- xray-core/proxy/freedom/freedom.go | 3 + .../workflows/releases-linux-binary.yml | 54 +++- yass/CMakeLists.txt | 26 +- yass/src/core/debug.cpp | 5 +- yass/src/core/rand_util_posix.cpp | 3 +- .../crypto/fipsmodule/rand/getrandom_fillin.h | 2 + .../boringssl/src/include/openssl/target.h | 9 +- .../googleurl-override/build/build_config.h | 5 + yass/third_party/quiche/CMakeLists.txt | 1 - yass/tools/build.go | 4 + youtube-dl/youtube_dl/postprocessor/ffmpeg.py | 16 +- yt-dlp/yt_dlp/cookies.py | 6 +- yt-dlp/yt_dlp/extractor/nhk.py | 200 +++++++++--- yt-dlp/yt_dlp/extractor/patreon.py | 44 ++- yt-dlp/yt_dlp/extractor/tiktok.py | 1 + yt-dlp/yt_dlp/extractor/vk.py | 15 +- 80 files changed, 1034 insertions(+), 1053 deletions(-) create mode 100644 echo/internal/transporter/base.go rename echo/{internal/transporter => pkg/buffer}/buffer.go (98%) diff --git a/.github/update.log b/.github/update.log index 6a0002be8d..fc7c345ef7 100644 --- a/.github/update.log +++ b/.github/update.log @@ -609,3 +609,4 @@ Update On Wed Apr 3 20:26:29 CEST 2024 Update On Thu Apr 4 20:27:14 CEST 2024 Update On Fri Apr 5 20:26:37 CEST 2024 Update On Sat Apr 6 20:26:05 CEST 2024 +Update On Sun Apr 7 20:25:26 CEST 2024 diff --git a/clash-meta-android/core/src/main/golang/native/config/fetch.go b/clash-meta-android/core/src/main/golang/native/config/fetch.go index d919f6e745..c1b25105c2 100644 --- a/clash-meta-android/core/src/main/golang/native/config/fetch.go +++ b/clash-meta-android/core/src/main/golang/native/config/fetch.go @@ -25,7 +25,7 @@ type Status struct { } func openUrl(ctx context.Context, url string) (io.ReadCloser, error) { - response, err := clashHttp.HttpRequest(ctx, url, http.MethodGet, http.Header{"User-Agent": {"ClashMetaForAndroid/" + app.VersionName()}}, nil, "") + response, err := clashHttp.HttpRequest(ctx, url, http.MethodGet, http.Header{"User-Agent": {"ClashMetaForAndroid/" + app.VersionName()}}, nil) if err != nil { return nil, err diff --git a/clash-meta/adapter/provider/parser.go b/clash-meta/adapter/provider/parser.go index bbaf29be31..1094668d46 100644 --- a/clash-meta/adapter/provider/parser.go +++ b/clash-meta/adapter/provider/parser.go @@ -88,16 +88,13 @@ func ParseProxyProvider(name string, mapping map[string]any) (types.ProxyProvide path := C.Path.Resolve(schema.Path) vehicle = resource.NewFileVehicle(path) case "http": - var path string + path := C.Path.GetPathByHash("proxies", schema.URL) if schema.Path != "" { path = C.Path.Resolve(schema.Path) if !features.CMFA && !C.Path.IsSafePath(path) { return nil, fmt.Errorf("%w: %s", errSubPath, path) } - } else { - path = C.Path.GetPathByHash("proxies", schema.URL) } - vehicle = resource.NewHTTPVehicle(schema.URL, path, schema.Proxy, schema.Header) default: return nil, fmt.Errorf("%w: %s", errVehicleType, schema.Type) diff --git a/clash-meta/adapter/provider/provider.go b/clash-meta/adapter/provider/provider.go index aa5b823350..2715a30972 100644 --- a/clash-meta/adapter/provider/provider.go +++ b/clash-meta/adapter/provider/provider.go @@ -125,7 +125,7 @@ func (pp *proxySetProvider) getSubscriptionInfo() { ctx, cancel := context.WithTimeout(context.Background(), time.Second*90) defer cancel() resp, err := mihomoHttp.HttpRequest(ctx, pp.Vehicle().(*resource.HTTPVehicle).Url(), - http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil, "") + http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil) if err != nil { return } @@ -134,7 +134,7 @@ func (pp *proxySetProvider) getSubscriptionInfo() { userInfoStr := strings.TrimSpace(resp.Header.Get("subscription-userinfo")) if userInfoStr == "" { resp2, err := mihomoHttp.HttpRequest(ctx, pp.Vehicle().(*resource.HTTPVehicle).Url(), - http.MethodGet, http.Header{"User-Agent": {"Quantumultx"}}, nil, "") + http.MethodGet, http.Header{"User-Agent": {"Quantumultx"}}, nil) if err != nil { return } diff --git a/clash-meta/component/geodata/init.go b/clash-meta/component/geodata/init.go index 64022f013b..834567a447 100644 --- a/clash-meta/component/geodata/init.go +++ b/clash-meta/component/geodata/init.go @@ -47,7 +47,7 @@ func InitGeoSite() error { func downloadGeoSite(path string) (err error) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*90) defer cancel() - resp, err := mihomoHttp.HttpRequest(ctx, C.GeoSiteUrl, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil, "") + resp, err := mihomoHttp.HttpRequest(ctx, C.GeoSiteUrl, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil) if err != nil { return } @@ -66,7 +66,7 @@ func downloadGeoSite(path string) (err error) { func downloadGeoIP(path string) (err error) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*90) defer cancel() - resp, err := mihomoHttp.HttpRequest(ctx, C.GeoIpUrl, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil, "") + resp, err := mihomoHttp.HttpRequest(ctx, C.GeoIpUrl, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil) if err != nil { return } diff --git a/clash-meta/component/http/http.go b/clash-meta/component/http/http.go index ef37e328b8..21d65d2e96 100644 --- a/clash-meta/component/http/http.go +++ b/clash-meta/component/http/http.go @@ -16,7 +16,11 @@ import ( "github.com/metacubex/mihomo/listener/inner" ) -func HttpRequest(ctx context.Context, url, method string, header map[string][]string, body io.Reader, specialProxy string) (*http.Response, error) { +func HttpRequest(ctx context.Context, url, method string, header map[string][]string, body io.Reader) (*http.Response, error) { + return HttpRequestWithProxy(ctx, url, method, header, body, "") +} + +func HttpRequestWithProxy(ctx context.Context, url, method string, header map[string][]string, body io.Reader, specialProxy string) (*http.Response, error) { method = strings.ToUpper(method) urlRes, err := URL.Parse(url) if err != nil { diff --git a/clash-meta/component/mmdb/mmdb.go b/clash-meta/component/mmdb/mmdb.go index 120739fc15..81156bc62d 100644 --- a/clash-meta/component/mmdb/mmdb.go +++ b/clash-meta/component/mmdb/mmdb.go @@ -82,7 +82,7 @@ func IPInstance() IPReader { func DownloadMMDB(path string) (err error) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*90) defer cancel() - resp, err := mihomoHttp.HttpRequest(ctx, C.MmdbUrl, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil, "") + resp, err := mihomoHttp.HttpRequest(ctx, C.MmdbUrl, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil) if err != nil { return } @@ -115,7 +115,7 @@ func ASNInstance() ASNReader { func DownloadASN(path string) (err error) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*90) defer cancel() - resp, err := mihomoHttp.HttpRequest(ctx, C.ASNUrl, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil, "") + resp, err := mihomoHttp.HttpRequest(ctx, C.ASNUrl, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil) if err != nil { return } diff --git a/clash-meta/component/resource/vehicle.go b/clash-meta/component/resource/vehicle.go index 2f71c2e6c6..2d71be9455 100644 --- a/clash-meta/component/resource/vehicle.go +++ b/clash-meta/component/resource/vehicle.go @@ -54,7 +54,7 @@ func (h *HTTPVehicle) Path() string { func (h *HTTPVehicle) Read() ([]byte, error) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*20) defer cancel() - resp, err := mihomoHttp.HttpRequest(ctx, h.url, http.MethodGet, h.header, nil, h.proxy) + resp, err := mihomoHttp.HttpRequestWithProxy(ctx, h.url, http.MethodGet, h.header, nil, h.proxy) if err != nil { return nil, err } diff --git a/clash-meta/config/utils.go b/clash-meta/config/utils.go index 596199ca18..66bf3441f2 100644 --- a/clash-meta/config/utils.go +++ b/clash-meta/config/utils.go @@ -20,7 +20,7 @@ import ( func downloadForBytes(url string) ([]byte, error) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*90) defer cancel() - resp, err := mihomoHttp.HttpRequest(ctx, url, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil, "") + resp, err := mihomoHttp.HttpRequest(ctx, url, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil) if err != nil { return nil, err } diff --git a/clash-meta/hub/updater/updater.go b/clash-meta/hub/updater/updater.go index e0d3a39ca1..02ff07ba26 100644 --- a/clash-meta/hub/updater/updater.go +++ b/clash-meta/hub/updater/updater.go @@ -234,7 +234,7 @@ const MaxPackageFileSize = 32 * 1024 * 1024 func downloadPackageFile() (err error) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*90) defer cancel() - resp, err := mihomoHttp.HttpRequest(ctx, packageURL, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil, "") + resp, err := mihomoHttp.HttpRequest(ctx, packageURL, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil) if err != nil { return fmt.Errorf("http request failed: %w", err) } @@ -415,7 +415,7 @@ func copyFile(src, dst string) error { func getLatestVersion() (version string, err error) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() - resp, err := mihomoHttp.HttpRequest(ctx, versionURL, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil, "") + resp, err := mihomoHttp.HttpRequest(ctx, versionURL, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil) if err != nil { return "", fmt.Errorf("get Latest Version fail: %w", err) } diff --git a/clash-meta/rules/provider/parse.go b/clash-meta/rules/provider/parse.go index e7920adf6c..a20da28d5c 100644 --- a/clash-meta/rules/provider/parse.go +++ b/clash-meta/rules/provider/parse.go @@ -62,15 +62,12 @@ func ParseRuleProvider(name string, mapping map[string]interface{}, parse func(t path := C.Path.Resolve(schema.Path) vehicle = resource.NewFileVehicle(path) case "http": - var path string + path := C.Path.GetPathByHash("rules", schema.URL) if schema.Path != "" { path = C.Path.Resolve(schema.Path) if !features.CMFA && !C.Path.IsSafePath(path) { return nil, fmt.Errorf("%w: %s", errSubPath, path) } - } else { - path = C.Path.GetPathByHash("rules", schema.URL) - } vehicle = resource.NewHTTPVehicle(schema.URL, path, schema.Proxy, nil) default: diff --git a/clash-nyanpasu/.github/workflows/release.yml b/clash-nyanpasu/.github/workflows/release.yml index 31a7418843..2d81116cb4 100644 --- a/clash-nyanpasu/.github/workflows/release.yml +++ b/clash-nyanpasu/.github/workflows/release.yml @@ -171,3 +171,15 @@ jobs: TELEGRAM_TOKEN: ${{ secrets.TELEGRAM_TOKEN }} TELEGRAM_TO: "@keikolog" GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Post Tweet + uses: rg-wood/send-tweet-action@v1 + with: + status: | + Clash Nyanpasu ${{ github.event.release.tag_name }} Released! + + Download Link: https://github.com/LibNyanpasu/clash-nyanpasu/releases/tag/v${{ github.event.release.tag_name }} + consumer-key: ${{ secrets.TWITTER_CONSUMER_KEY }} + consumer-secret: ${{ secrets.TWITTER_CONSUMER_SECRET }} + access-token: ${{ secrets.TWITTER_ACCESS_TOKEN }} + access-token-secret: ${{ secrets.TWITTER_ACCESS_TOKEN_SECRET }} diff --git a/clash-nyanpasu/README.md b/clash-nyanpasu/README.md index d5649a84b2..3da481c2d7 100644 --- a/clash-nyanpasu/README.md +++ b/clash-nyanpasu/README.md @@ -14,6 +14,12 @@ A Clash GUI based on Dev Build Status Nyanpasu stars GitHub Downloads (all assets, all releases) + Nyanpasu License +

+ +

+ Nyanpasu Twitter

## Features @@ -80,6 +86,7 @@ Issue and PR welcome! Clash Nyanpasu was based on or inspired by these projects and so on: - [zzzgydi/clash-verge](https://github.com/zzzgydi/clash-verge): A Clash GUI based on tauri. Supports Windows, macOS and Linux. +- [clash-verge-rev/clash-verge-rev](https://github.com/clash-verge-rev/clash-verge-rev): Another fork of Clash Verge. Some patches are included for bug fixes. - [tauri-apps/tauri](https://github.com/tauri-apps/tauri): Build smaller, faster, and more secure desktop applications with a web frontend. - [Dreamacro/clash](https://github.com/Dreamacro/clash): A rule-based tunnel in Go. - [MetaCubeX/Clash.Meta](https://github.com/MetaCubeX/mihomo): A rule-based tunnel in Go. diff --git a/clash-nyanpasu/backend/tauri/src/cmds.rs b/clash-nyanpasu/backend/tauri/src/cmds.rs index 03cd2e9822..c5b47be8b2 100644 --- a/clash-nyanpasu/backend/tauri/src/cmds.rs +++ b/clash-nyanpasu/backend/tauri/src/cmds.rs @@ -384,41 +384,46 @@ pub fn get_custom_app_dir() -> CmdResult> { pub async fn set_custom_app_dir(app_handle: tauri::AppHandle, path: String) -> CmdResult { use crate::utils::{self, dialog::migrate_dialog, winreg::set_app_dir}; use rust_i18n::t; - use std::{path::PathBuf, time::Duration}; + use std::path::PathBuf; let path_str = path.clone(); let path = PathBuf::from(path); // show a dialog to ask whether to migrate the data - let res = tauri::async_runtime::spawn_blocking(move || { - let msg = t!("dialog.custom_app_dir_migrate", path = path_str).to_string(); + let res = + tauri::async_runtime::spawn_blocking(move || { + let msg = t!("dialog.custom_app_dir_migrate", path = path_str).to_string(); - if migrate_dialog(&msg) { - let app_exe = tauri::utils::platform::current_exe()?; - let app_exe = dunce::canonicalize(app_exe)?.to_string_lossy().to_string(); - std::thread::spawn(move || { + if migrate_dialog(&msg) { + let app_exe = tauri::utils::platform::current_exe()?; + let app_exe = dunce::canonicalize(app_exe)?.to_string_lossy().to_string(); std::process::Command::new("powershell") .arg("-Command") .arg( format!( - r#"Start-Process '{}' -ArgumentList 'migrate-home-dir','{}' -Verb runAs"#, + r#"Start-Process '{}' -ArgumentList 'migrate-home-dir','"{}"' -Verb runAs"#, app_exe.as_str(), path_str.as_str() ) .as_str(), ).spawn().unwrap(); utils::help::quit_application(&app_handle); - }); - } else { - set_app_dir(&path)?; - } - Ok::<_, anyhow::Error>(()) - }) - .await; + } else { + set_app_dir(&path)?; + } + Ok::<_, anyhow::Error>(()) + }) + .await; wrap_err!(wrap_err!(res)?)?; Ok(()) } +#[tauri::command] +pub fn restart_application(app_handle: tauri::AppHandle) -> CmdResult { + crate::utils::help::restart_application(&app_handle); + Ok(()) +} + #[cfg(not(windows))] #[tauri::command] pub async fn set_custom_app_dir(_path: String) -> CmdResult { diff --git a/clash-nyanpasu/backend/tauri/src/core/commands/mod.rs b/clash-nyanpasu/backend/tauri/src/core/commands/mod.rs index 9a4dbd706d..af5484acf6 100644 --- a/clash-nyanpasu/backend/tauri/src/core/commands/mod.rs +++ b/clash-nyanpasu/backend/tauri/src/core/commands/mod.rs @@ -1,5 +1,10 @@ +use std::str::FromStr; + use anyhow::Ok; -use clap::{Parser, Subcommand}; +use clap::{Args, Parser, Subcommand}; +use tauri::utils::platform::current_exe; + +use crate::utils; #[derive(Parser, Debug)] #[command(name = "clash-nyanpasu", version, about, long_about = None)] @@ -12,16 +17,52 @@ pub struct Cli { enum Commands { #[command(about = "Migrate home directory to another path.")] MigrateHomeDir { target_path: String }, + #[command(about = "A launch bridge to resolve the delay exit issue.")] + Launch { + // FIXME: why the raw arg is not working? + #[arg(raw = true)] + args: Vec, + }, +} + +struct DelayedExitGuard; +impl DelayedExitGuard { + pub fn new() -> Self { + Self + } +} +impl Drop for DelayedExitGuard { + fn drop(&mut self) { + std::thread::sleep(std::time::Duration::from_secs(5)); + } } pub fn parse() -> anyhow::Result<()> { let cli = Cli::parse(); if let Some(commands) = &cli.command { + let guard = DelayedExitGuard::new(); match commands { Commands::MigrateHomeDir { target_path } => { self::handler::migrate_home_dir_handler(target_path).unwrap(); } + Commands::Launch { args } => { + let _ = utils::init::check_singleton().unwrap(); + let appimage: Option = { + #[cfg(target_os = "linux")] + { + std::env::var_os("APPIMAGE").map(|s| s.to_string_lossy().to_string()) + } + #[cfg(not(target_os = "linux"))] + None + }; + let path = match appimage { + Some(appimage) => std::path::PathBuf::from_str(&appimage).unwrap(), + None => current_exe().unwrap(), + }; + std::process::Command::new(path).args(args).spawn().unwrap(); + } } + drop(guard); std::process::exit(0); } Ok(()) // bypass @@ -36,6 +77,7 @@ mod handler { use std::{path::PathBuf, process::Command, str::FromStr, thread, time::Duration}; use sysinfo::System; use tauri::utils::platform::current_exe; + println!("target path {}", target_path); let token = Token::with_current_process()?; if let PrivilegeLevel::NotPrivileged = token.privilege_level()? { diff --git a/clash-nyanpasu/backend/tauri/src/core/storage.rs b/clash-nyanpasu/backend/tauri/src/core/storage.rs index cbb571e21d..c37c8cffa2 100644 --- a/clash-nyanpasu/backend/tauri/src/core/storage.rs +++ b/clash-nyanpasu/backend/tauri/src/core/storage.rs @@ -28,3 +28,9 @@ impl Storage { rocksdb::DB::destroy(&rocksdb::Options::default(), &self.path) } } + +impl Drop for Storage { + fn drop(&mut self) { + self.destroy().unwrap(); + } +} diff --git a/clash-nyanpasu/backend/tauri/src/core/tray/mod.rs b/clash-nyanpasu/backend/tauri/src/core/tray/mod.rs index 777114f581..13ea8415bc 100644 --- a/clash-nyanpasu/backend/tauri/src/core/tray/mod.rs +++ b/clash-nyanpasu/backend/tauri/src/core/tray/mod.rs @@ -2,7 +2,7 @@ use crate::{cmds, config::Config, feat, utils, utils::resolve}; use anyhow::Result; use rust_i18n::t; use tauri::{ - api, AppHandle, CustomMenuItem, Manager, SystemTrayEvent, SystemTrayMenu, SystemTrayMenuItem, + AppHandle, CustomMenuItem, SystemTrayEvent, SystemTrayMenu, SystemTrayMenuItem, SystemTraySubmenu, }; use tracing_attributes::instrument; @@ -151,7 +151,7 @@ impl Tray { "open_core_dir" => crate::log_err!(cmds::open_core_dir()), "open_logs_dir" => crate::log_err!(cmds::open_logs_dir()), "restart_clash" => feat::restart_clash_core(), - "restart_app" => api::process::restart(&app_handle.env()), + "restart_app" => utils::help::restart_application(app_handle), "quit" => { utils::help::quit_application(app_handle); } diff --git a/clash-nyanpasu/backend/tauri/src/main.rs b/clash-nyanpasu/backend/tauri/src/main.rs index ebb87e02a6..a38c0ce775 100644 --- a/clash-nyanpasu/backend/tauri/src/main.rs +++ b/clash-nyanpasu/backend/tauri/src/main.rs @@ -13,9 +13,8 @@ mod utils; use crate::{ config::Config, core::{commands, handle::Handle}, - utils::{dirs, init, resolve}, + utils::{init, resolve}, }; -use anyhow::Context; use tauri::{api, Manager, SystemTray}; rust_i18n::i18n!("../../locales"); @@ -54,16 +53,7 @@ fn main() -> std::io::Result<()> { tauri_plugin_deep_link::prepare("moe.elaina.clash.nyanpasu"); // 单例检测 - let placeholder = dirs::get_single_instance_placeholder(); - let single_instance_result: anyhow::Result<()> = - single_instance::SingleInstance::new(&placeholder) - .context("failed to create single instance") - .map(|instance| { - if !instance.is_single() { - println!("app exists"); - std::process::exit(0); - } - }); + let single_instance_result = utils::init::check_singleton(); // Use system locale as default let locale = { @@ -86,7 +76,7 @@ fn main() -> std::io::Result<()> { rust_i18n::set_locale(verge.as_str()); // show a dialog to print the single instance error - single_instance_result.unwrap(); + let _singleton = single_instance_result.unwrap(); // hold the guard until the end of the program #[allow(unused_mut)] let mut builder = tauri::Builder::default() @@ -179,6 +169,7 @@ fn main() -> std::io::Result<()> { cmds::get_proxies, cmds::select_proxy, cmds::update_proxy_provider, + cmds::restart_application, ]); #[cfg(target_os = "macos")] diff --git a/clash-nyanpasu/backend/tauri/src/utils/help.rs b/clash-nyanpasu/backend/tauri/src/utils/help.rs index 642d64314e..5d5096c59c 100644 --- a/clash-nyanpasu/backend/tauri/src/utils/help.rs +++ b/clash-nyanpasu/backend/tauri/src/utils/help.rs @@ -14,7 +14,10 @@ use std::{ str::FromStr, }; use tauri::{ - api::shell::{open, Program}, + api::{ + process::current_binary, + shell::{open, Program}, + }, AppHandle, Manager, }; use tracing::{debug, warn}; @@ -216,14 +219,36 @@ pub fn get_max_scale_factor() -> f64 { } #[instrument(skip(app_handle))] -pub fn quit_application(app_handle: &AppHandle) { +fn cleanup_processes(app_handle: &AppHandle) { let _ = super::resolve::save_window_state(app_handle, true); - super::resolve::resolve_reset(); tauri::api::process::kill_children(); +} + +#[instrument(skip(app_handle))] +pub fn quit_application(app_handle: &AppHandle) { + cleanup_processes(app_handle); + app_handle.exit(0); + std::process::exit(0); +} + +#[instrument(skip(app_handle))] +pub fn restart_application(app_handle: &AppHandle) { + cleanup_processes(app_handle); + let env = app_handle.env(); + let path = current_binary(&env).unwrap(); + let arg = std::env::args().collect::>(); + let mut args = vec!["launch".to_string(), "--".to_string()]; + // filter out the first arg + if arg.len() > 1 { + args.extend(arg.iter().skip(1).cloned()); + } + tracing::info!("restart app: {:#?} with args: {:#?}", path, args); + std::process::Command::new(path) + .args(args) + .spawn() + .expect("application failed to start"); app_handle.exit(0); - // flush all data to disk - crate::core::storage::Storage::global().destroy().unwrap(); std::process::exit(0); } diff --git a/clash-nyanpasu/backend/tauri/src/utils/init/mod.rs b/clash-nyanpasu/backend/tauri/src/utils/init/mod.rs index 7e8a3b7de7..3ccb36a50c 100644 --- a/clash-nyanpasu/backend/tauri/src/utils/init/mod.rs +++ b/clash-nyanpasu/backend/tauri/src/utils/init/mod.rs @@ -2,7 +2,7 @@ use crate::{ config::*, utils::{dialog::migrate_dialog, dirs, help}, }; -use anyhow::Result; +use anyhow::{Context, Result}; use fs_extra::dir::CopyOptions; use runas::Command as RunasCommand; use rust_i18n::t; @@ -196,6 +196,21 @@ pub fn init_service() -> Result<()> { Ok(()) } +pub fn check_singleton() -> Result { + let placeholder = super::dirs::get_single_instance_placeholder(); + for i in 0..5 { + let instance = single_instance::SingleInstance::new(&placeholder) + .context("failed to create single instance")?; + if instance.is_single() { + return Ok(instance); + } + if i != 4 { + std::thread::sleep(std::time::Duration::from_secs(1)); + } + } + anyhow::bail!("single instance check failed: app still exists after 4s"); +} + pub fn do_config_migration(old_app_dir: &PathBuf, app_dir: &PathBuf) -> anyhow::Result<()> { let copy_option = CopyOptions::new(); let copy_option = copy_option.overwrite(true); diff --git a/clash-nyanpasu/manifest/version.json b/clash-nyanpasu/manifest/version.json index 3d99d907ff..62a400b675 100644 --- a/clash-nyanpasu/manifest/version.json +++ b/clash-nyanpasu/manifest/version.json @@ -2,7 +2,7 @@ "manifest_version": 1, "latest": { "mihomo": "v1.18.3", - "mihomo_alpha": "alpha-5bae0b6", + "mihomo_alpha": "alpha-c44949b", "clash_rs": "v0.1.15", "clash_premium": "2023-09-05-gdcc8d87" }, @@ -36,5 +36,5 @@ "darwin-x64": "clash-darwin-amd64-n{}.gz" } }, - "updated_at": "2024-04-05T22:19:27.268Z" + "updated_at": "2024-04-06T22:19:13.513Z" } diff --git a/clash-nyanpasu/package.json b/clash-nyanpasu/package.json index e6ebfa2d0f..c5c04f5c5a 100644 --- a/clash-nyanpasu/package.json +++ b/clash-nyanpasu/package.json @@ -91,7 +91,7 @@ "react-markdown": "9.0.1", "react-router-dom": "6.22.3", "react-transition-group": "4.4.5", - "react-virtuoso": "4.7.7", + "react-virtuoso": "4.7.8", "recoil": "0.7.7", "swr": "2.2.5" }, diff --git a/clash-nyanpasu/pnpm-lock.yaml b/clash-nyanpasu/pnpm-lock.yaml index c940be7fb1..305360b441 100644 --- a/clash-nyanpasu/pnpm-lock.yaml +++ b/clash-nyanpasu/pnpm-lock.yaml @@ -93,8 +93,8 @@ dependencies: specifier: 4.4.5 version: 4.4.5(react-dom@18.2.0)(react@18.2.0) react-virtuoso: - specifier: 4.7.7 - version: 4.7.7(react-dom@18.2.0)(react@18.2.0) + specifier: 4.7.8 + version: 4.7.8(react-dom@18.2.0)(react@18.2.0) recoil: specifier: 0.7.7 version: 0.7.7(react-dom@18.2.0)(react@18.2.0) @@ -6251,8 +6251,8 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false - /react-virtuoso@4.7.7(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-n9NdMNaAtxHYH6e3H6zr1Kb08sp+1XPVnfE4cEMgrvmPBLugd9eeJtQo/1uA+SHhGaPX3uqZOuQsfKbX1r8P/A==} + /react-virtuoso@4.7.8(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-P0BHOsLrmfnXv1bY9Nja07nvFciRGNgM7lTRHMcVDteTDb9tLtHzajBapKGUZ5zdyUOEVWvuW6ufQxzdGN2AKw==} engines: {node: '>=10'} peerDependencies: react: '>=16 || >=17 || >= 18' diff --git a/clash-nyanpasu/src/components/setting/setting-verge.tsx b/clash-nyanpasu/src/components/setting/setting-verge.tsx index bd507e85e4..d7c0cc3a80 100644 --- a/clash-nyanpasu/src/components/setting/setting-verge.tsx +++ b/clash-nyanpasu/src/components/setting/setting-verge.tsx @@ -7,6 +7,7 @@ import { openAppDir, openCoreDir, openLogsDir, + restartApplication, setCustomAppDir, } from "@/services/cmds"; import { sleep } from "@/utils"; @@ -22,7 +23,6 @@ import { Typography, } from "@mui/material"; import { open } from "@tauri-apps/api/dialog"; -import { relaunch } from "@tauri-apps/api/process"; import { checkUpdate } from "@tauri-apps/api/updater"; import { useAsyncEffect, useLockFn } from "ahooks"; import { useRef, useState } from "react"; @@ -123,7 +123,7 @@ const SettingVerge = ({ onError }: Props) => { body: t("App directory changed successfully"), }); await sleep(1000); - await relaunch(); + await restartApplication(); } catch (err: any) { useMessage(err.message || err.toString(), { title: t("Error"), diff --git a/clash-nyanpasu/src/services/cmds.ts b/clash-nyanpasu/src/services/cmds.ts index fd825604a7..4834f9d06a 100644 --- a/clash-nyanpasu/src/services/cmds.ts +++ b/clash-nyanpasu/src/services/cmds.ts @@ -264,3 +264,7 @@ export async function getCustomAppDir() { export async function setCustomAppDir(path: string) { return invoke("set_custom_app_dir", { path }); } + +export async function restartApplication() { + return invoke("restart_application"); +} diff --git a/echo/.github/workflows/cd.yaml b/echo/.github/workflows/cd.yaml index 9a6cd48e67..a2723fedc6 100644 --- a/echo/.github/workflows/cd.yaml +++ b/echo/.github/workflows/cd.yaml @@ -166,9 +166,12 @@ jobs: echo "Creating manifest list..." AMD64_DIGEST=$(ls amd64/) ARM64_DIGEST=$(ls arm64/) - docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ - --append ${{ env.REGISTRY_IMAGE }}@sha256:$AMD64_DIGEST \ - --append ${{ env.REGISTRY_IMAGE }}@sha256:$ARM64_DIGEST + echo "AMD64_DIGEST: $AMD64_DIGEST" + echo "ARM64_DIGEST: $ARM64_DIGEST" + docker buildx imagetools create \ + --tag ${{ env.REGISTRY_IMAGE }}:latest \ + ${{ env.REGISTRY_IMAGE }}@sha256:$AMD64_DIGEST \ + ${{ env.REGISTRY_IMAGE }}@sha256:$ARM64_DIGEST - name: Inspect image run: | diff --git a/echo/README.md b/echo/README.md index 91bb48475f..dcb17f3af5 100644 --- a/echo/README.md +++ b/echo/README.md @@ -8,126 +8,11 @@ ## Ehco Relay - 让流量转发更简单 -我们很高兴地宣布,ehco 现在提供 SaaS(软件即服务)版本!这是一个全托管的解决方案,旨在为那些希望在不搭建和管理自己的服务器的情况下享受 ehco 强大流量转发能力的用户提供服务。 +ehco 现在提供 SaaS(软件即服务)版本!这是一个全托管的解决方案,旨在为那些希望在不搭建和管理自己的服务器的情况下享受 ehco 强大流量转发能力的用户提供服务。 - [Ehco Relay 官方网站](https://ehco-relay.cc) - [Ehco Relay 文档](https://docs.ehco-relay.cc/) -### 为什么选择 Ehco Relay? - -- **即刻启动**:无需复杂的配置或服务器管理,立即获得高性能的流量转发服务。 -- **全面托管**:我们的团队会处理所有后端事务,包括维护、更新和故障排除,让您可以专注于您的主要业务。 -- **灵活的计划**:无论您是个人开发者还是大型企业,我们都提供多种计划来满足您的需求。 -- **安全可靠**:利用最先进的加密技术,确保您的数据传输安全无忧。 - -欢迎访问官网来了解更多信息,并开始您的免费试用。只需几个简单的步骤,您就可以轻松地设置并运行 ehco,享受无缝的流量转发服务。 - -## 使用场景 - -
连接内网服务 - -本地无法链接集群内的服务,可以通过 ehco 将本地流量转发到集群内,方便本地开发和调试 - -e.g. 本地开发调试连接内网服务 db, db host: xxx-rds.xxx.us-east-1.rds.amazonaws.com - -1. 在 k8s 内 启动一个 ehco pod. 启动命令如下: - `ehco -l 0.0.0.0:3306 -r xxx-rds.xxx.us-east-1.rds.amazonaws.com:3306` - -2. 使用 kube port-forward 将本地的 3306 端口转发到 ehco pod 的 3306 端口 - `kubectl port-forward pod/ehco-pod 3306:3306` - -3. 本地使用客户端连接 - `mysql -h 127.0.0.1:3306 -u root -p` -
- -
中转 proxy 客户端,提供负载均衡功能 - -从 **v1.1.4-dev(nightly)** 开始, ehco 支持了从 clash proxy provider 读取 proxy 配置并复写成 ehco 的 relay 配置 -从而实现了 ehco 作为代理客户端的前置代理,提供负载均衡,流量监控等功能 - -e.g. - -1. 配置 ehco 的 config.json 并启动 - -```json -{ - "web_host": "12.0.0.1", - "web_port": 9000, - "sub_configs": [ - { - "name": "nas", - "url": "your url" - } - ] -} -``` - -`ehco -c config.json` - -2. 访问 ehco 的 web 界面 获取 ehco 的 proxy provider url - -`http://:` - -![](monitor/web.png) - -ehco 会将每个 clash proxy provider 转换成两个新 clash provider - -- 会将每个的 proxy 转换成一个 relay -- 会将 proxy 按最长前缀**分组**,并将每个分组转换成开启负载均衡的 relay - -举个例子 - -```yaml -proxies: - - name: us-1 - server: s1 - password: - port: 1 - - name: us-2 - server: s2 - port: 2 - - name: jb-1 - server: s3 - password: pass - port: 3 -``` - -上面这个包含 3 个 proxy 的会被转换成 5 个 relay: - -- us-1 relay to s1:1 -- us-2 relay to s2:2 -- jb-1 relay to s3:3 - us-lb relay to s1:1,s2:2 -- jb-1-lb relay to s3:3 - -3. 将 ehco 的 proxy provider url 配置到 clash 的配置文件中 - -```yaml -proxy-providers: - ehco: - type: http - url: http://:/clash_proxy_provider/?sub_name= - ehco-lb: - type: http - url: http://:/clash_proxy_provider/?sub_name=name&grouped=true -``` - -你就能得到一个支持负载均衡的 clash proxy client 了,并且还能在 dashboard 上看到流量监控哟 -![](monitor/proxy-traffic.png) - -
- -
WIP: 隧道连接到 proxy 集群 -
- -## 安装 - -- ehco 提供预编译的的二进制 [release](https://github.com/Ehco1996/ehco/releases) 页面下载 - -- ehco 提供 [nightly build](https://github.com/Ehco1996/ehco/releases/tag/v0.0.0-nightly) - -- ehco 提供 docker 镜像 `docker pull ehco1996/ehco` - ## 主要功能 - tcp/udp relay @@ -135,185 +20,4 @@ proxy-providers: - proxy server (内嵌了完整班版本的 xray) - 监控报警 (prometheus/grafana) - WebAPI (http://web_host:web_port) - -## 中转使用介绍 - -使用隧道需要至少两台主机, 并且在两台主机上都安装了 ehco - -- 中转机器 A 假设机器 A 的 IP 是 1.1.1.1 -- 落地机器 B 假设机器 B 的 IP 是 2.2.2.2 并且落地机器 B 的 5555 端口跑着一个 SS/v2ray/任意 tcp/udp 服务 - -
案例一 不用隧道直接通过中转机器中转用户流量 -直接在中转机器 A 上输入: `ehco -l 0.0.0.0:1234 -r 2.2.2.2:5555` - -> 该命令表示将所有从中转机器 A 的 1234 端口进入的流量直接转发到落地机器 B 的 5555 端口 - -用户即可通过 中转机器 A 的 1234 端口访问到落地机器 B 的 5555 端口的 SS/v2ray 服务了 - -
- -
案例二 用 mwss 隧道中转用户流量 -在落地机器 B 上输入: `ehco -l 0.0.0.0:443 -lt mwss -r 127.0.0.1:5555` - -> 该命令表示将所有从落地机器 B 的 443 端口进入的 wss 流量解密后转发到落地机器 B 的 5555 端口 - -在中转机器 A 上输入: `ehco -l 0.0.0.0:1234 -r wss://2.2.2.2:443 -tt mwss` - -> 该命令表示将所有从 A 的 1234 端口进入的流量通过 wss 加密后转发到落地机器 B 的 443 端口 - -用户即可通过 中转机器 A 的 1234 端口访问到落地机器 B 的 5555 端口的 SS/v2ray 服务了 - -
- -## 内嵌 Xray 功能介绍 - -
ehco 内的 xray 服务端 -从 `v1.1.2` 开始,ehco 内置了完整版本的 [xray](https://github.com/XTLS/Xray-core) 后端,可以通过标准的 xray 配置文件来启动内置的 xray server, 配置的 key 为 `xray_config`: - -- 单端口多用户的 ss [xray_ss.json](examples/xray_ss.json) -- 单端口多用户的 trojan [xray_trojan.json](examples/xray_trojan.json) -
- -
用户流量统计 - -从 `v1.1.2` 开始,ehco 支持通过 api 下方用户配置和上报用户流量,配置的 key 为 `sync_traffic_endpoint`: - -ehco 会每隔 60s 发送一次 GET 请求,从 `sync_traffic_endpoint` 同步一次用户配置,到 xray server 里,期望的 API 返回格式如下: - -```json -{ - "users": [ - { - "user_id": 1, - "method": "user1", - "password": 1024, - "level": 1024, - "upload_traffic": 1024, - "download_traffic": 1024, - "protocol": "trojan/ss" - }, - { - "user_id": 2, - "method": "user1", - "password": 1024, - "level": 1024, - "upload_traffic": 1024, - "download_traffic": 1024, - "protocol": "trojan/ss" - } - ] -} -``` - -ehco 会每隔 60s 发送一次 POST 请求至 `sync_traffic_endpoint` ,上报当前 xray server 所有用户的流量使用情况,发送的请求格式如下: - -```json -{ - "data": [ - { - "user_id": 1, - "upload_traffic": 1024, - "download_traffic": 1024 - }, - { - "user_id": 2, - "upload_traffic": 1024, - "download_traffic": 1024 - } - ] -} -``` - -需要注意的是,如果想使用此功能,对 xray 的完整配置文件有如下限制 - -- 的配置文件必须包开启 `stats` 和 `api` 功能 -- ss inbound 的 `tag` 必须为 `ss_proxy` -- trojan inbound 的 `tag` 必须为 `trojan_proxy` - -一个完整的例子可以参考 [xray_ss.json](examples/xray_ss.json) 和 [xray_trojan.json](examples/xray_trojan.json) - -
- -## 配置文件格式 - -> ehco 支持从 `配置文件` / `http接口` 里读取 `json` 格式的配置并启动 -> (更多例子可以参考项目里的 [config.json](examples/config.json) 文件): - -
热重载配置 - -- 大于 1.1.0 版本的 ehco 支持热重载配置 -- 通过 `kill -HUP pid` 信号来热重载配置 -- 通过配置 `reload_interval` 来指定配置文件的路径 -- 通过访问 POST `http://web_host:web_port/reload/` 接口来热重载配置 -
- -## 监控报警 - -- dashboard 和 prometheus 规则可以从`monitor`文件夹下找到,可以自行导入 - -- 类似 Smoking Ping 的延迟监控 - -![](monitor/ping.png) - -- 流量监控 - -![](monitor/traffic.png) - -## Benchmark(Apple m1) - -iperf: - -```sh -# run iperf server on 5201 -iperf3 -s - -# 直接转发 -# run relay server listen 1234 to 9001 (raw) -go run cmd/ehco/main.go -l 0.0.0.0:1234 -r 0.0.0.0:5201 - -# 直接转发END - -# 通过ws隧道转发 -# listen 1235 relay over ws to 1236 -go run cmd/ehco/main.go -l 0.0.0.0:1235 -r ws://0.0.0.0:1236 -tt ws - -# listen 1236 through ws relay to 5201 -go run cmd/ehco/main.go -l 0.0.0.0:1236 -lt ws -r 0.0.0.0:5201 -# 通过ws隧道转发END - -# 通过wss隧道转发 -# listen 1234 relay over wss to 1236 -go run cmd/ehco/main.go -l 0.0.0.0:1235 -r wss://0.0.0.0:1236 -tt wss - -# listen 1236 through wss relay to 5201 -go run cmd/ehco/main.go -l 0.0.0.0:1236 -lt wss -r 0.0.0.0:5201 -# 通过wss隧道转发END - -# 通过mwss隧道转发 和wss相比 速度会慢,但是能减少延迟 -# listen 1237 relay over mwss to 1238 -go run cmd/ehco/main.go -l 0.0.0.0:1237 -r wss://0.0.0.0:1238 -tt mwss - -# listen 1238 through mwss relay to 5201 -go run cmd/ehco/main.go -l 0.0.0.0:1238 -lt mwss -r 0.0.0.0:5201 -# 通过mwss隧道转发END - -# run through file -go run cmd/ehco/main.go -c config.json - -# benchmark tcp -iperf3 -c 0.0.0.0 -p 1234 - -# benchmark tcp through wss -iperf3 -c 0.0.0.0 -p 1235 - -# benchmark upd -iperf3 -c 0.0.0.0 -p 1234 -u -b 1G --length 1024 -``` - -``` -| iperf | raw | relay(raw) | relay(ws) | relay(wss) | relay(mwss) | relay(mtcp) | -| ----- | -------------- | ------------- | ------------ | ------------ | -------------- | -------------- | -| tcp | 123 Gbits/sec | 55 Gbits/sec | 41 Gbits/sec | 10 Gbits/sec | 5.78 Gbits/sec | 22.2 Gbits/sec | -| udp | 14.5 Gbits/sec | 3.3 Gbits/sec | 直接转发 | 直接转发 | 直接转发 | 直接转发 | - -``` +- [更多功能请探索文档](https://docs.ehco-relay.cc/) diff --git a/echo/internal/cli/app.go b/echo/internal/cli/app.go index 7d83cdbbd6..88fd998a33 100644 --- a/echo/internal/cli/app.go +++ b/echo/internal/cli/app.go @@ -10,7 +10,7 @@ import ( cli "github.com/urfave/cli/v2" ) -var cliLogger = log.MustNewLogger("info").Sugar().Named("cli-app") +var cliLogger = log.MustNewLogger("info").Sugar().Named("cli") func startAction(ctx *cli.Context) error { cfg, err := InitConfigAndComponents() diff --git a/echo/internal/cli/config.go b/echo/internal/cli/config.go index 0822271454..46412ccfcf 100644 --- a/echo/internal/cli/config.go +++ b/echo/internal/cli/config.go @@ -49,10 +49,10 @@ func loadConfig() (cfg *config.Config, err error) { } } - // init tls + // init tls when need for _, cfg := range cfg.RelayConfigs { - if cfg.ListenType == constant.Listen_WSS || cfg.ListenType == constant.Listen_MWSS || - cfg.TransportType == constant.Transport_WSS || cfg.TransportType == constant.Transport_MWSS { + if cfg.ListenType == constant.RelayTypeWSS || cfg.ListenType == constant.RelayTypeMWSS || + cfg.TransportType == constant.RelayTypeWSS || cfg.TransportType == constant.RelayTypeMWSS { if err := tls.InitTlsCfg(); err != nil { return nil, err } diff --git a/echo/internal/constant/constant.go b/echo/internal/constant/constant.go index b9ede39ada..a4c815b2ba 100644 --- a/echo/internal/constant/constant.go +++ b/echo/internal/constant/constant.go @@ -20,19 +20,16 @@ const ( SmuxMaxAliveDuration = 10 * time.Minute SmuxMaxStreamCnt = 5 - Listen_RAW = "raw" - Listen_WS = "ws" - Listen_WSS = "wss" - Listen_MWSS = "mwss" - Listen_MTCP = "mtcp" - - Transport_RAW = "raw" - Transport_WS = "ws" - Transport_WSS = "wss" - Transport_MWSS = "mwss" - Transport_MTCP = "mtcp" - // todo add udp buffer size BUFFER_POOL_SIZE = 1024 // support 512 connections BUFFER_SIZE = 20 * 1024 // 20KB the maximum packet size of shadowsocks is about 16 KiB ) + +// relay type +const ( + RelayTypeRaw = "raw" + RelayTypeWS = "ws" + RelayTypeWSS = "wss" + RelayTypeMWSS = "mwss" + RelayTypeMTCP = "mtcp" +) diff --git a/echo/internal/relay/conf/cfg.go b/echo/internal/relay/conf/cfg.go index d9ad952db1..7b53b83cbf 100644 --- a/echo/internal/relay/conf/cfg.go +++ b/echo/internal/relay/conf/cfg.go @@ -5,6 +5,8 @@ import ( "fmt" "github.com/Ehco1996/ehco/internal/constant" + + "github.com/Ehco1996/ehco/pkg/lb" "go.uber.org/zap" ) @@ -22,19 +24,19 @@ func (r *Config) Validate() error { if r.Adjust() != nil { return errors.New("adjust config failed") } - if r.ListenType != constant.Listen_RAW && - r.ListenType != constant.Listen_WS && - r.ListenType != constant.Listen_WSS && - r.ListenType != constant.Listen_MTCP && - r.ListenType != constant.Listen_MWSS { + if r.ListenType != constant.RelayTypeRaw && + r.ListenType != constant.RelayTypeWS && + r.ListenType != constant.RelayTypeWSS && + r.ListenType != constant.RelayTypeMTCP && + r.ListenType != constant.RelayTypeMWSS { return fmt.Errorf("invalid listen type:%s", r.ListenType) } - if r.TransportType != constant.Transport_RAW && - r.TransportType != constant.Transport_WS && - r.TransportType != constant.Transport_WSS && - r.TransportType != constant.Transport_MTCP && - r.TransportType != constant.Transport_MWSS { + if r.TransportType != constant.RelayTypeRaw && + r.TransportType != constant.RelayTypeWS && + r.TransportType != constant.RelayTypeWSS && + r.TransportType != constant.RelayTypeMTCP && + r.TransportType != constant.RelayTypeMWSS { return fmt.Errorf("invalid transport type:%s", r.ListenType) } @@ -106,16 +108,31 @@ func (r *Config) Different(new *Config) bool { } // todo make this shorter and more readable -func (r *Config) defaultLabel() string { - defaultLabel := fmt.Sprintf("", - r.Listen, r.ListenType, r.TCPRemotes, r.UDPRemotes, r.TransportType) +func (r *Config) DefaultLabel() string { + defaultLabel := fmt.Sprintf("", + r.Listen, r.TCPRemotes, r.TransportType) return defaultLabel } func (r *Config) Adjust() error { if r.Label == "" { - r.Label = r.defaultLabel() + r.Label = r.DefaultLabel() zap.S().Debugf("label is empty, set default label:%s", r.Label) } return nil } + +func (r *Config) ToTCPRemotes() lb.RoundRobin { + tcpNodeList := make([]*lb.Node, len(r.TCPRemotes)) + for idx, addr := range r.TCPRemotes { + tcpNodeList[idx] = &lb.Node{ + Address: addr, + Label: fmt.Sprintf("%s-%s", r.Label, addr), + } + } + return lb.NewRoundRobin(tcpNodeList) +} + +func (r *Config) GetLoggerName() string { + return fmt.Sprintf("%s(%s<->%s)", r.Label, r.ListenType, r.TransportType) +} diff --git a/echo/internal/relay/relay.go b/echo/internal/relay/relay.go index a832f4cf19..0964b66eba 100644 --- a/echo/internal/relay/relay.go +++ b/echo/internal/relay/relay.go @@ -1,138 +1,50 @@ package relay import ( - "net" - "go.uber.org/zap" "github.com/Ehco1996/ehco/internal/cmgr" - "github.com/Ehco1996/ehco/internal/constant" "github.com/Ehco1996/ehco/internal/relay/conf" "github.com/Ehco1996/ehco/internal/transporter" ) type Relay struct { - Name string // unique name for all relay - TransportType string - ListenType string - - TP transporter.RelayTransporter - - LocalTCPAddr *net.TCPAddr - closeTcpF func() error - cfg *conf.Config l *zap.SugaredLogger + + relayServer transporter.RelayServer +} + +func (r *Relay) UniqueID() string { + return r.cfg.Label } func NewRelay(cfg *conf.Config, connMgr cmgr.Cmgr) (*Relay, error) { - localTCPAddr, err := net.ResolveTCPAddr("tcp", cfg.Listen) + base := transporter.NewBaseTransporter(cfg, connMgr) + s, err := transporter.NewRelayServer(cfg.ListenType, base) if err != nil { return nil, err } - r := &Relay{ - cfg: cfg, - l: zap.S().Named("relay"), - - Name: cfg.Label, - LocalTCPAddr: localTCPAddr, - ListenType: cfg.ListenType, - TransportType: cfg.TransportType, - TP: transporter.NewRelayTransporter(cfg, connMgr), + relayServer: s, + cfg: cfg, + l: zap.S().Named("relay"), } - return r, nil } func (r *Relay) ListenAndServe() error { errCh := make(chan error) - - if len(r.cfg.TCPRemotes) > 0 { - switch r.ListenType { - case constant.Listen_RAW: - go func() { - errCh <- r.RunLocalTCPServer() - }() - case constant.Listen_MTCP: - go func() { - errCh <- r.RunLocalMTCPServer() - }() - case constant.Listen_WS: - go func() { - errCh <- r.RunLocalWSServer() - }() - case constant.Listen_WSS: - go func() { - errCh <- r.RunLocalWSSServer() - }() - case constant.Listen_MWSS: - go func() { - errCh <- r.RunLocalMWSSServer() - }() - } - } + go func() { + r.l.Infof("Start TCP Relay Server:%s", r.cfg.DefaultLabel()) + errCh <- r.relayServer.ListenAndServe() + }() return <-errCh } func (r *Relay) Close() { - r.l.Infof("Close relay label: %s", r.Name) - if r.closeTcpF != nil { - err := r.closeTcpF() - if err != nil { - r.l.Errorf(err.Error()) - } + r.l.Infof("Close TCP Relay Server:%s", r.cfg.DefaultLabel()) + if err := r.relayServer.Close(); err != nil { + r.l.Errorf(err.Error()) } } - -func (r *Relay) RunLocalTCPServer() error { - rawServer, err := transporter.NewRawServer(r.LocalTCPAddr.String(), r.TP) - if err != nil { - return err - } - r.closeTcpF = func() error { - return rawServer.Close() - } - r.l.Infof("Start TCP relay Server: %s", r.Name) - return rawServer.ListenAndServe() -} - -func (r *Relay) RunLocalMTCPServer() error { - tp := r.TP.(*transporter.RawClient) - mTCPServer := transporter.NewMTCPServer(r.LocalTCPAddr.String(), tp, r.l.Named("MTCPServer")) - r.closeTcpF = func() error { - return mTCPServer.Close() - } - r.l.Infof("Start MTCP relay Server: %s", r.Name) - return mTCPServer.ListenAndServe() -} - -func (r *Relay) RunLocalWSServer() error { - tp := r.TP.(*transporter.RawClient) - wsServer := transporter.NewWSServer(r.LocalTCPAddr.String(), tp, r.l.Named("WSServer")) - r.closeTcpF = func() error { - return wsServer.Close() - } - r.l.Infof("Start WS relay Server: %s", r.Name) - return wsServer.ListenAndServe() -} - -func (r *Relay) RunLocalWSSServer() error { - tp := r.TP.(*transporter.RawClient) - wssServer := transporter.NewWSSServer(r.LocalTCPAddr.String(), tp, r.l.Named("WSSServer")) - r.closeTcpF = func() error { - return wssServer.Close() - } - r.l.Infof("Start WSS relay Server: %s", r.Name) - return wssServer.ListenAndServe() -} - -func (r *Relay) RunLocalMWSSServer() error { - tp := r.TP.(*transporter.RawClient) - mwssServer := transporter.NewMWSSServer(r.LocalTCPAddr.String(), tp, r.l.Named("MWSSServer")) - r.closeTcpF = func() error { - return mwssServer.Close() - } - r.l.Infof("Start MWSS relay Server: %s", r.Name) - return mwssServer.ListenAndServe() -} diff --git a/echo/internal/relay/server.go b/echo/internal/relay/server.go index c00ce99875..9ce52eca98 100644 --- a/echo/internal/relay/server.go +++ b/echo/internal/relay/server.go @@ -46,18 +46,18 @@ func NewServer(cfg *config.Config) (*Server, error) { } func (s *Server) startOneRelay(r *Relay) { - s.relayM.Store(r.Name, r) + s.relayM.Store(r.UniqueID(), r) // mute closed network error for tcp server and mute http.ErrServerClosed for http server when config reload if err := r.ListenAndServe(); err != nil && !errors.Is(err, net.ErrClosed) && !errors.Is(err, http.ErrServerClosed) { - s.l.Errorf("start relay %s meet error: %s", r.Name, err) + s.l.Errorf("start relay %s meet error: %s", r.UniqueID(), err) s.errCH <- err } } func (s *Server) stopOneRelay(r *Relay) { r.Close() - s.relayM.Delete(r.Name) + s.relayM.Delete(r.UniqueID()) } func (s *Server) Start(ctx context.Context) error { diff --git a/echo/internal/transporter/base.go b/echo/internal/transporter/base.go new file mode 100644 index 0000000000..6751279894 --- /dev/null +++ b/echo/internal/transporter/base.go @@ -0,0 +1,55 @@ +package transporter + +import ( + "net" + + "github.com/Ehco1996/ehco/internal/cmgr" + "github.com/Ehco1996/ehco/internal/conn" + "github.com/Ehco1996/ehco/internal/metrics" + "github.com/Ehco1996/ehco/internal/relay/conf" + + "github.com/Ehco1996/ehco/pkg/lb" + "go.uber.org/zap" +) + +type baseTransporter struct { + cmgr cmgr.Cmgr + cfg *conf.Config + tCPRemotes lb.RoundRobin + l *zap.SugaredLogger +} + +func NewBaseTransporter(cfg *conf.Config, cmgr cmgr.Cmgr) *baseTransporter { + return &baseTransporter{ + cfg: cfg, + cmgr: cmgr, + tCPRemotes: cfg.ToTCPRemotes(), + l: zap.S().Named(cfg.GetLoggerName()), + } +} + +func (b *baseTransporter) GetTCPListenAddr() (*net.TCPAddr, error) { + return net.ResolveTCPAddr("tcp", b.cfg.Listen) +} + +func (b *baseTransporter) GetRemote() *lb.Node { + return b.tCPRemotes.Next() +} + +func (b *baseTransporter) RelayTCPConn(c net.Conn, handshakeF TCPHandShakeF) error { + remote := b.GetRemote() + metrics.CurConnectionCount.WithLabelValues(remote.Label, metrics.METRIC_CONN_TYPE_TCP).Inc() + defer metrics.CurConnectionCount.WithLabelValues(remote.Label, metrics.METRIC_CONN_TYPE_TCP).Dec() + + clonedRemote := remote.Clone() + rc, err := handshakeF(clonedRemote) + if err != nil { + return err + } + b.l.Infof("RelayTCPConn from %s to %s", c.LocalAddr(), remote.Address) + relayConn := conn.NewRelayConn( + b.cfg.Label, c, rc, conn.WithHandshakeDuration(clonedRemote.HandShakeDuration)) + b.cmgr.AddConnection(relayConn) + defer b.cmgr.RemoveConnection(relayConn) + return relayConn.Transport(remote.Label) +} diff --git a/echo/internal/transporter/interface.go b/echo/internal/transporter/interface.go index e0fd5f430b..da40fe537c 100644 --- a/echo/internal/transporter/interface.go +++ b/echo/internal/transporter/interface.go @@ -1,42 +1,54 @@ package transporter import ( - "fmt" "net" - "github.com/Ehco1996/ehco/internal/cmgr" "github.com/Ehco1996/ehco/internal/constant" - "github.com/Ehco1996/ehco/internal/relay/conf" "github.com/Ehco1996/ehco/pkg/lb" ) -// RelayTransporter -type RelayTransporter interface { - dialRemote(remote *lb.Node) (net.Conn, error) - HandleTCPConn(c net.Conn, remote *lb.Node) error - GetRemote() *lb.Node +type TCPHandShakeF func(remote *lb.Node) (net.Conn, error) + +type RelayClient interface { + TCPHandShake(remote *lb.Node) (net.Conn, error) + RelayTCPConn(c net.Conn, handshakeF TCPHandShakeF) error } -func NewRelayTransporter(cfg *conf.Config, connMgr cmgr.Cmgr) RelayTransporter { - tcpNodeList := make([]*lb.Node, len(cfg.TCPRemotes)) - for idx, addr := range cfg.TCPRemotes { - tcpNodeList[idx] = &lb.Node{ - Address: addr, - Label: fmt.Sprintf("%s-%s", cfg.Label, addr), - } +func NewRelayClient(relayType string, base *baseTransporter) (RelayClient, error) { + switch relayType { + case constant.RelayTypeRaw: + return newRawClient(base) + case constant.RelayTypeWS: + return newWsClient(base) + case constant.RelayTypeWSS: + return newWssClient(base) + case constant.RelayTypeMWSS: + return newMwssClient(base) + case constant.RelayTypeMTCP: + return newMtcpClient(base) + default: + panic("unsupported transport type") + } +} + +type RelayServer interface { + ListenAndServe() error + Close() error +} + +func NewRelayServer(relayType string, base *baseTransporter) (RelayServer, error) { + switch relayType { + case constant.RelayTypeRaw: + return newRawServer(base) + case constant.RelayTypeWS: + return newWsServer(base) + case constant.RelayTypeWSS: + return newWssServer(base) + case constant.RelayTypeMWSS: + return newMwssServer(base) + case constant.RelayTypeMTCP: + return newMtcpServer(base) + default: + panic("unsupported transport type") } - raw := newRawClient(cfg.Label, lb.NewRoundRobin(tcpNodeList), connMgr) - switch cfg.TransportType { - case constant.Transport_RAW: - return raw - case constant.Transport_WS: - return newWsClient(raw) - case constant.Transport_WSS: - return newWSSClient(raw) - case constant.Transport_MWSS: - return newMWSSClient(raw) - case constant.Transport_MTCP: - return newMTCPClient(raw) - } - return nil } diff --git a/echo/internal/transporter/mux.go b/echo/internal/transporter/mux.go index ae82212532..5bc9d3d586 100644 --- a/echo/internal/transporter/mux.go +++ b/echo/internal/transporter/mux.go @@ -136,3 +136,66 @@ func (tr *smuxTransporter) Dial(ctx context.Context, addr string) (conn net.Conn curSM.streamList = append(curSM.streamList, stream) return stream, nil } + +type muxServer interface { + ListenAndServe() error + Accept() (net.Conn, error) + Close() error + mux(net.Conn) +} + +func newMuxServer(listenAddr string, l *zap.SugaredLogger) *muxServerImpl { + return &muxServerImpl{ + errChan: make(chan error, 1), + connChan: make(chan net.Conn, 1024), + listenAddr: listenAddr, + l: l, + } +} + +type muxServerImpl struct { + errChan chan error + connChan chan net.Conn + + listenAddr string + l *zap.SugaredLogger +} + +func (s *muxServerImpl) Accept() (net.Conn, error) { + select { + case conn := <-s.connChan: + return conn, nil + case err := <-s.errChan: + return nil, err + } +} + +func (s *muxServerImpl) mux(conn net.Conn) { + defer conn.Close() + + cfg := smux.DefaultConfig() + cfg.KeepAliveDisabled = true + session, err := smux.Server(conn, cfg) + if err != nil { + s.l.Debugf("server err %s - %s : %s", conn.RemoteAddr(), s.listenAddr, err) + return + } + defer session.Close() // nolint: errcheck + + s.l.Debugf("session init %s %s", conn.RemoteAddr(), s.listenAddr) + defer s.l.Debugf("session close %s >-< %s", conn.RemoteAddr(), s.listenAddr) + + for { + stream, err := session.AcceptStream() + if err != nil { + s.l.Errorf("accept stream err: %s", err) + break + } + select { + case s.connChan <- stream: + default: + stream.Close() // nolint: errcheck + s.l.Infof("%s - %s: connection queue is full", conn.RemoteAddr(), conn.LocalAddr()) + } + } +} diff --git a/echo/internal/transporter/raw.go b/echo/internal/transporter/raw.go index d9954780bd..c52df4651d 100644 --- a/echo/internal/transporter/raw.go +++ b/echo/internal/transporter/raw.go @@ -5,40 +5,33 @@ import ( "net" "time" - "go.uber.org/zap" - - "github.com/Ehco1996/ehco/internal/cmgr" - "github.com/Ehco1996/ehco/internal/conn" "github.com/Ehco1996/ehco/internal/constant" "github.com/Ehco1996/ehco/internal/metrics" "github.com/Ehco1996/ehco/pkg/lb" ) +var ( + _ RelayClient = &RawClient{} + _ RelayServer = &RawServer{} +) + type RawClient struct { - relayLabel string - cmgr cmgr.Cmgr - tCPRemotes lb.RoundRobin - l *zap.SugaredLogger + *baseTransporter + + dialer *net.Dialer } -func newRawClient(relayLabel string, tcpRemotes lb.RoundRobin, cmgr cmgr.Cmgr) *RawClient { +func newRawClient(base *baseTransporter) (*RawClient, error) { r := &RawClient{ - cmgr: cmgr, - relayLabel: relayLabel, - tCPRemotes: tcpRemotes, - l: zap.S().Named(relayLabel), + baseTransporter: base, + dialer: &net.Dialer{Timeout: constant.DialTimeOut}, } - return r + return r, nil } -func (raw *RawClient) GetRemote() *lb.Node { - return raw.tCPRemotes.Next() -} - -func (raw *RawClient) dialRemote(remote *lb.Node) (net.Conn, error) { +func (raw *RawClient) TCPHandShake(remote *lb.Node) (net.Conn, error) { t1 := time.Now() - d := net.Dialer{Timeout: constant.DialTimeOut} - rc, err := d.Dial("tcp", remote.Address) + rc, err := raw.dialer.Dial("tcp", remote.Address) if err != nil { return nil, err } @@ -48,38 +41,32 @@ func (raw *RawClient) dialRemote(remote *lb.Node) (net.Conn, error) { return rc, nil } -func (raw *RawClient) HandleTCPConn(c net.Conn, remote *lb.Node) error { - metrics.CurConnectionCount.WithLabelValues(remote.Label, metrics.METRIC_CONN_TYPE_TCP).Inc() - defer metrics.CurConnectionCount.WithLabelValues(remote.Label, metrics.METRIC_CONN_TYPE_TCP).Dec() - - clonedRemote := remote.Clone() - rc, err := raw.dialRemote(clonedRemote) - if err != nil { - return err - } - raw.l.Infof("HandleTCPConn from %s to %s", c.LocalAddr(), remote.Address) - relayConn := conn.NewRelayConn(raw.relayLabel, c, rc, conn.WithHandshakeDuration(clonedRemote.HandShakeDuration)) - raw.cmgr.AddConnection(relayConn) - defer raw.cmgr.RemoveConnection(relayConn) - return relayConn.Transport(remote.Label) -} - type RawServer struct { - rtp RelayTransporter - lis *net.TCPListener - l *zap.SugaredLogger + *baseTransporter + localTCPAddr *net.TCPAddr + lis *net.TCPListener + relayer RelayClient } -func NewRawServer(addr string, rtp RelayTransporter) (*RawServer, error) { - tcpAddr, err := net.ResolveTCPAddr("tcp", addr) +func newRawServer(base *baseTransporter) (*RawServer, error) { + addr, err := base.GetTCPListenAddr() if err != nil { return nil, err } - lis, err := net.ListenTCP("tcp", tcpAddr) + lis, err := net.ListenTCP("tcp", addr) if err != nil { return nil, err } - return &RawServer{lis: lis, rtp: rtp}, nil + relayer, err := NewRelayClient(base.cfg.TransportType, base) + if err != nil { + return nil, err + } + return &RawServer{ + lis: lis, + baseTransporter: base, + localTCPAddr: addr, + relayer: relayer, + }, nil } func (s *RawServer) Close() error { @@ -93,13 +80,8 @@ func (s *RawServer) ListenAndServe() error { return err } go func(c net.Conn) { - remote := s.rtp.GetRemote() - metrics.CurConnectionCount.WithLabelValues(remote.Label, metrics.METRIC_CONN_TYPE_TCP).Inc() - defer metrics.CurConnectionCount.WithLabelValues(remote.Label, metrics.METRIC_CONN_TYPE_TCP).Dec() - if err := s.rtp.HandleTCPConn(c, remote); err != nil { - s.l.Errorf("HandleTCPConn meet error tp:%s from:%s to:%s err:%s", - s.rtp, - c.RemoteAddr(), remote.Address, err) + if err := s.RelayTCPConn(c, s.relayer.TCPHandShake); err != nil { + s.l.Errorf("RelayTCPConn error: %s", err.Error()) } }(c) } diff --git a/echo/internal/transporter/raw_mux.go b/echo/internal/transporter/raw_mux.go index ef1669d5cd..7e52861cb8 100644 --- a/echo/internal/transporter/raw_mux.go +++ b/echo/internal/transporter/raw_mux.go @@ -1,4 +1,3 @@ -// nolint: errcheck package transporter import ( @@ -7,29 +6,32 @@ import ( "time" "github.com/xtaci/smux" - "go.uber.org/zap" - "github.com/Ehco1996/ehco/internal/conn" - "github.com/Ehco1996/ehco/internal/constant" "github.com/Ehco1996/ehco/internal/metrics" "github.com/Ehco1996/ehco/pkg/lb" ) -type MTCPClient struct { +var ( + _ RelayClient = &MtcpClient{} + _ RelayServer = &MtcpServer{} +) + +type MtcpClient struct { *RawClient - dialer *net.Dialer - mtp *smuxTransporter + muxTP *smuxTransporter } -func newMTCPClient(raw *RawClient) *MTCPClient { - dialer := &net.Dialer{Timeout: constant.DialTimeOut} - c := &MTCPClient{dialer: dialer, RawClient: raw} - mtp := NewSmuxTransporter(raw.l.Named("mtcp"), c.initNewSession) - c.mtp = mtp - return c +func newMtcpClient(base *baseTransporter) (*MtcpClient, error) { + raw, err := newRawClient(base) + if err != nil { + return nil, err + } + c := &MtcpClient{RawClient: raw} + c.muxTP = NewSmuxTransporter(raw.l.Named("mtcp"), c.initNewSession) + return c, nil } -func (c *MTCPClient) initNewSession(ctx context.Context, addr string) (*smux.Session, error) { +func (c *MtcpClient) initNewSession(ctx context.Context, addr string) (*smux.Session, error) { rc, err := c.dialer.Dial("tcp", addr) if err != nil { return nil, err @@ -45,9 +47,9 @@ func (c *MTCPClient) initNewSession(ctx context.Context, addr string) (*smux.Ses return session, nil } -func (s *MTCPClient) dialRemote(remote *lb.Node) (net.Conn, error) { +func (s *MtcpClient) TCPHandShake(remote *lb.Node) (net.Conn, error) { t1 := time.Now() - mtcpc, err := s.mtp.Dial(context.TODO(), remote.Address) + mtcpc, err := s.muxTP.Dial(context.TODO(), remote.Address) if err != nil { return nil, err } @@ -57,87 +59,28 @@ func (s *MTCPClient) dialRemote(remote *lb.Node) (net.Conn, error) { return mtcpc, nil } -func (s *MTCPClient) HandleTCPConn(c net.Conn, remote *lb.Node) error { - clonedRemote := remote.Clone() - mtcpc, err := s.dialRemote(clonedRemote) +type MtcpServer struct { + *RawServer + *muxServerImpl +} + +func newMtcpServer(base *baseTransporter) (*MtcpServer, error) { + raw, err := newRawServer(base) if err != nil { - return err + return nil, err } - s.l.Infof("HandleTCPConn from:%s to:%s", c.LocalAddr(), remote.Address) - relayConn := conn.NewRelayConn(s.relayLabel, c, mtcpc, conn.WithHandshakeDuration(clonedRemote.HandShakeDuration)) - s.cmgr.AddConnection(relayConn) - defer s.cmgr.RemoveConnection(relayConn) - return relayConn.Transport(remote.Label) + s := &MtcpServer{ + RawServer: raw, + muxServerImpl: newMuxServer(base.cfg.Listen, base.l.Named("mtcp")), + } + + return s, nil } -type MTCPServer struct { - raw *RawClient - listenAddr string - listener net.Listener - l *zap.SugaredLogger - - errChan chan error - connChan chan net.Conn -} - -func NewMTCPServer(listenAddr string, raw *RawClient, l *zap.SugaredLogger) *MTCPServer { - return &MTCPServer{ - l: l, - raw: raw, - listenAddr: listenAddr, - errChan: make(chan error, 1), - connChan: make(chan net.Conn, 1024), - } -} - -func (s *MTCPServer) mux(conn net.Conn) { - defer conn.Close() - - cfg := smux.DefaultConfig() - cfg.KeepAliveDisabled = true - session, err := smux.Server(conn, cfg) - if err != nil { - s.l.Debugf("server err %s - %s : %s", conn.RemoteAddr(), s.listenAddr, err) - return - } - defer session.Close() - - s.l.Debugf("session init %s %s", conn.RemoteAddr(), s.listenAddr) - defer s.l.Debugf("session close %s >-< %s", conn.RemoteAddr(), s.listenAddr) - - for { - stream, err := session.AcceptStream() - if err != nil { - s.l.Errorf("accept stream err: %s", err) - break - } - select { - case s.connChan <- stream: - default: - stream.Close() - s.l.Infof("%s - %s: connection queue is full", conn.RemoteAddr(), conn.LocalAddr()) - } - } -} - -func (s *MTCPServer) Accept() (conn net.Conn, err error) { - select { - case conn = <-s.connChan: - case err = <-s.errChan: - } - return -} - -func (s *MTCPServer) ListenAndServe() error { - lis, err := net.Listen("tcp", s.listenAddr) - if err != nil { - return err - } - s.listener = lis - +func (s *MtcpServer) ListenAndServe() error { go func() { for { - c, err := lis.Accept() + c, err := s.lis.Accept() if err != nil { s.errChan <- err continue @@ -152,14 +95,13 @@ func (s *MTCPServer) ListenAndServe() error { return e } go func(c net.Conn) { - remote := s.raw.GetRemote() - if err := s.raw.HandleTCPConn(c, remote); err != nil { - s.l.Errorf("HandleTCPConn meet error from:%s to:%s err:%s", c.RemoteAddr(), remote.Address, err) + if err := s.RelayTCPConn(c, s.relayer.TCPHandShake); err != nil { + s.l.Errorf("RelayTCPConn error: %s", err.Error()) } }(conn) } } -func (s *MTCPServer) Close() error { - return s.listener.Close() +func (s *MtcpServer) Close() error { + return s.lis.Close() } diff --git a/echo/internal/transporter/ws.go b/echo/internal/transporter/ws.go index 0b4608ed84..402b11850c 100644 --- a/echo/internal/transporter/ws.go +++ b/echo/internal/transporter/ws.go @@ -8,27 +8,35 @@ import ( "github.com/gobwas/ws" "github.com/labstack/echo/v4" - "go.uber.org/zap" - "github.com/Ehco1996/ehco/internal/conn" "github.com/Ehco1996/ehco/internal/constant" "github.com/Ehco1996/ehco/internal/metrics" "github.com/Ehco1996/ehco/internal/web" "github.com/Ehco1996/ehco/pkg/lb" ) +var ( + _ RelayClient = &WsClient{} + _ RelayServer = &WsServer{} +) + type WsClient struct { - *RawClient + *baseTransporter + + dialer *ws.Dialer } -func newWsClient(raw *RawClient) *WsClient { - return &WsClient{RawClient: raw} +func newWsClient(base *baseTransporter) (*WsClient, error) { + s := &WsClient{ + baseTransporter: base, + dialer: &ws.Dialer{Timeout: constant.DialTimeOut}, + } + return s, nil } -func (s *WsClient) dialRemote(remote *lb.Node) (net.Conn, error) { +func (s *WsClient) TCPHandShake(remote *lb.Node) (net.Conn, error) { t1 := time.Now() - d := ws.Dialer{Timeout: constant.DialTimeOut} - wsc, _, _, err := d.Dial(context.TODO(), remote.Address+"/handshake/") + wsc, _, _, err := s.dialer.Dial(context.TODO(), remote.Address+"/handshake/") if err != nil { return nil, err } @@ -38,57 +46,51 @@ func (s *WsClient) dialRemote(remote *lb.Node) (net.Conn, error) { return wsc, nil } -func (s *WsClient) HandleTCPConn(c net.Conn, remote *lb.Node) error { - clonedRemote := remote.Clone() - wsc, err := s.dialRemote(clonedRemote) - if err != nil { - return err - } - s.l.Infof("HandleTCPConn from %s to %s", c.LocalAddr(), remote.Address) - relayConn := conn.NewRelayConn( - s.relayLabel, c, wsc, - conn.WithHandshakeDuration(clonedRemote.HandShakeDuration)) - s.cmgr.AddConnection(relayConn) - defer s.cmgr.RemoveConnection(relayConn) - return relayConn.Transport(remote.Label) -} +type WsServer struct { + *baseTransporter -type WSServer struct { - raw *RawClient e *echo.Echo httpServer *http.Server - l *zap.SugaredLogger + relayer RelayClient } -func NewWSServer(listenAddr string, raw *RawClient, l *zap.SugaredLogger) *WSServer { - s := &WSServer{ - l: l, - raw: raw, - httpServer: &http.Server{Addr: listenAddr, ReadHeaderTimeout: 30 * time.Second}, +func newWsServer(base *baseTransporter) (*WsServer, error) { + localTCPAddr, err := base.GetTCPListenAddr() + if err != nil { + return nil, err + } + s := &WsServer{ + baseTransporter: base, + httpServer: &http.Server{ + Addr: localTCPAddr.String(), ReadHeaderTimeout: 30 * time.Second, + }, } e := web.NewEchoServer() e.GET("/", echo.WrapHandler(web.MakeIndexF())) e.GET("/handshake/", echo.WrapHandler(http.HandlerFunc(s.HandleRequest))) s.e = e - s.httpServer.Handler = e - return s + relayer, err := NewRelayClient(base.cfg.TransportType, base) + if err != nil { + return nil, err + } + s.relayer = relayer + return s, nil } -func (s *WSServer) ListenAndServe() error { +func (s *WsServer) ListenAndServe() error { return s.e.StartServer(s.httpServer) } -func (s *WSServer) Close() error { +func (s *WsServer) Close() error { return s.e.Close() } -func (s *WSServer) HandleRequest(w http.ResponseWriter, req *http.Request) { +func (s *WsServer) HandleRequest(w http.ResponseWriter, req *http.Request) { wsc, _, _, err := ws.UpgradeHTTP(req, w) if err != nil { return } - remote := s.raw.GetRemote() - if err := s.raw.HandleTCPConn(wsc, remote); err != nil { - s.l.Errorf("HandleTCPConn meet error from:%s to:%s err:%s", wsc.RemoteAddr(), remote.Address, err) + if err := s.RelayTCPConn(wsc, s.relayer.TCPHandShake); err != nil { + s.l.Errorf("RelayTCPConn error: %s", err.Error()) } } diff --git a/echo/internal/transporter/wss.go b/echo/internal/transporter/wss.go index a8915ae93c..19c5eb9610 100644 --- a/echo/internal/transporter/wss.go +++ b/echo/internal/transporter/wss.go @@ -1,64 +1,38 @@ -// nolint: errcheck package transporter import ( - "context" - "net" - "time" - - "github.com/gobwas/ws" - "go.uber.org/zap" - - "github.com/Ehco1996/ehco/internal/conn" - "github.com/Ehco1996/ehco/internal/metrics" mytls "github.com/Ehco1996/ehco/internal/tls" - "github.com/Ehco1996/ehco/pkg/lb" ) -type WSSClient struct { - WsClient +var ( + _ RelayClient = &WssClient{} + _ RelayServer = &WssServer{} +) + +type WssClient struct { + *WsClient } -func newWSSClient(raw *RawClient) *WSSClient { - return &WSSClient{*newWsClient(raw)} -} - -func (s *WSSClient) dialRemote(remote *lb.Node) (net.Conn, error) { - t1 := time.Now() - d := ws.Dialer{TLSConfig: mytls.DefaultTLSConfig} - wssc, _, _, err := d.Dial(context.TODO(), remote.Address+"/handshake/") +func newWssClient(base *baseTransporter) (*WssClient, error) { + wc, err := newWsClient(base) if err != nil { - println("wss called", err.Error()) return nil, err } - latency := time.Since(t1) - metrics.HandShakeDuration.WithLabelValues(remote.Label).Observe(float64(latency.Milliseconds())) - remote.HandShakeDuration = latency - return wssc, nil + // insert tls config + wc.dialer.TLSConfig = mytls.DefaultTLSConfig + return &WssClient{WsClient: wc}, nil } -func (s *WSSClient) HandleTCPConn(c net.Conn, remote *lb.Node) error { - clonedRemote := remote.Clone() - wssc, err := s.dialRemote(clonedRemote) +type WssServer struct { + *WsServer +} + +func newWssServer(base *baseTransporter) (*WssServer, error) { + wsServer, err := newWsServer(base) if err != nil { - return err + return nil, err } - s.l.Infof("HandleTCPConn from %s to %s", c.RemoteAddr(), remote.Address) - - relayConn := conn.NewRelayConn(s.relayLabel, c, wssc, conn.WithHandshakeDuration(clonedRemote.HandShakeDuration)) - s.cmgr.AddConnection(relayConn) - defer s.cmgr.RemoveConnection(relayConn) - return relayConn.Transport(remote.Label) -} - -type WSSServer struct{ WSServer } - -func NewWSSServer(listenAddr string, raw *RawClient, l *zap.SugaredLogger) *WSSServer { - wsServer := NewWSServer(listenAddr, raw, l) - return &WSSServer{WSServer: *wsServer} -} - -func (s *WSSServer) ListenAndServe() error { - s.httpServer.TLSConfig = mytls.DefaultTLSConfig - return s.WSServer.ListenAndServe() + // insert tls config + wsServer.httpServer.TLSConfig = mytls.DefaultTLSConfig + return &WssServer{WsServer: wsServer}, nil } diff --git a/echo/internal/transporter/wss_mux.go b/echo/internal/transporter/wss_mux.go index d180c5f2f6..844a1cbca4 100644 --- a/echo/internal/transporter/wss_mux.go +++ b/echo/internal/transporter/wss_mux.go @@ -1,9 +1,7 @@ -// nolint: errcheck package transporter import ( "context" - "crypto/tls" "net" "net/http" "time" @@ -11,31 +9,34 @@ import ( "github.com/gobwas/ws" "github.com/labstack/echo/v4" "github.com/xtaci/smux" - "go.uber.org/zap" - "github.com/Ehco1996/ehco/internal/conn" - "github.com/Ehco1996/ehco/internal/constant" "github.com/Ehco1996/ehco/internal/metrics" - mytls "github.com/Ehco1996/ehco/internal/tls" - "github.com/Ehco1996/ehco/internal/web" "github.com/Ehco1996/ehco/pkg/lb" ) -type MWSSClient struct { - *RawClient - dialer *ws.Dialer - mtp *smuxTransporter +var ( + _ RelayClient = &MwssClient{} + _ RelayServer = &MwssServer{} + _ muxServer = &MwssServer{} +) + +type MwssClient struct { + *WssClient + + muxTP *smuxTransporter } -func newMWSSClient(raw *RawClient) *MWSSClient { - dialer := &ws.Dialer{TLSConfig: mytls.DefaultTLSConfig, Timeout: constant.DialTimeOut} - c := &MWSSClient{dialer: dialer, RawClient: raw} - mtp := NewSmuxTransporter(raw.l.Named("mwss"), c.initNewSession) - c.mtp = mtp - return c +func newMwssClient(base *baseTransporter) (*MwssClient, error) { + wc, err := newWssClient(base) + if err != nil { + return nil, err + } + c := &MwssClient{WssClient: wc} + c.muxTP = NewSmuxTransporter(c.l.Named("mwss"), c.initNewSession) + return c, nil } -func (c *MWSSClient) initNewSession(ctx context.Context, addr string) (*smux.Session, error) { +func (c *MwssClient) initNewSession(ctx context.Context, addr string) (*smux.Session, error) { rc, _, _, err := c.dialer.Dial(ctx, addr) if err != nil { return nil, err @@ -51,68 +52,39 @@ func (c *MWSSClient) initNewSession(ctx context.Context, addr string) (*smux.Ses return session, nil } -func (s *MWSSClient) dialRemote(remote *lb.Node) (net.Conn, error) { +func (s *MwssClient) TCPHandShake(remote *lb.Node) (net.Conn, error) { t1 := time.Now() - mwssc, err := s.mtp.Dial(context.TODO(), remote.Address+"/handshake/") + mwssc, err := s.muxTP.Dial(context.TODO(), remote.Address+"/handshake/") if err != nil { return nil, err } - latency := time.Since(t1) metrics.HandShakeDuration.WithLabelValues(remote.Label).Observe(float64(latency.Milliseconds())) remote.HandShakeDuration = latency return mwssc, nil } -func (s *MWSSClient) HandleTCPConn(c net.Conn, remote *lb.Node) error { - clonedRemote := remote.Clone() - mwsc, err := s.dialRemote(clonedRemote) +type MwssServer struct { + *WssServer + *muxServerImpl +} + +func newMwssServer(base *baseTransporter) (*MwssServer, error) { + wssServer, err := newWssServer(base) if err != nil { - return err + return nil, err } - s.l.Infof("HandleTCPConn from:%s to:%s", c.LocalAddr(), remote.Address) - relayConn := conn.NewRelayConn(s.relayLabel, c, mwsc, conn.WithHandshakeDuration(clonedRemote.HandShakeDuration)) - s.cmgr.AddConnection(relayConn) - defer s.cmgr.RemoveConnection(relayConn) - return relayConn.Transport(remote.Label) + s := &MwssServer{ + WssServer: wssServer, + muxServerImpl: newMuxServer(base.cfg.Listen, base.l.Named("mwss")), + } + s.e.GET("/handshake/", echo.WrapHandler(http.HandlerFunc(s.HandleRequest))) + return s, nil } -type MWSSServer struct { - raw *RawClient - httpServer *http.Server - l *zap.SugaredLogger - - connChan chan net.Conn - errChan chan error -} - -func NewMWSSServer(listenAddr string, raw *RawClient, l *zap.SugaredLogger) *MWSSServer { - s := &MWSSServer{ - raw: raw, - l: l, - errChan: make(chan error, 1), - connChan: make(chan net.Conn, 1024), - } - - e := web.NewEchoServer() - e.GET("/", echo.WrapHandler(web.MakeIndexF())) - e.GET("/handshake/", echo.WrapHandler(http.HandlerFunc(s.HandleRequest))) - s.httpServer = &http.Server{ - Addr: listenAddr, - Handler: e, - TLSConfig: mytls.DefaultTLSConfig, - ReadHeaderTimeout: 30 * time.Second, - } - return s -} - -func (s *MWSSServer) ListenAndServe() error { - lis, err := net.Listen("tcp", s.httpServer.Addr) - if err != nil { - return err - } +func (s *MwssServer) ListenAndServe() error { go func() { - s.errChan <- s.httpServer.Serve(tls.NewListener(lis, s.httpServer.TLSConfig)) + s.errChan <- s.e.StartServer(s.httpServer) }() for { @@ -121,15 +93,14 @@ func (s *MWSSServer) ListenAndServe() error { return e } go func(c net.Conn) { - remote := s.raw.GetRemote() - if err := s.raw.HandleTCPConn(c, remote); err != nil { - s.l.Errorf("HandleTCPConn meet error from:%s to:%s err:%s", c.RemoteAddr(), remote.Address, err) + if err := s.RelayTCPConn(c, s.relayer.TCPHandShake); err != nil { + s.l.Errorf("RelayTCPConn error: %s", err.Error()) } }(conn) } } -func (s *MWSSServer) HandleRequest(w http.ResponseWriter, r *http.Request) { +func (s *MwssServer) HandleRequest(w http.ResponseWriter, r *http.Request) { conn, _, _, err := ws.UpgradeHTTP(r, w) if err != nil { s.l.Error(err) @@ -138,44 +109,6 @@ func (s *MWSSServer) HandleRequest(w http.ResponseWriter, r *http.Request) { s.mux(conn) } -func (s *MWSSServer) mux(conn net.Conn) { - defer conn.Close() - - cfg := smux.DefaultConfig() - cfg.KeepAliveDisabled = true - session, err := smux.Server(conn, cfg) - if err != nil { - s.l.Debugf("server err %s - %s : %s", conn.RemoteAddr(), s.httpServer.Addr, err) - return - } - defer session.Close() - - s.l.Debugf("session init %s %s", conn.RemoteAddr(), s.httpServer.Addr) - defer s.l.Debugf("session close %s >-< %s", conn.RemoteAddr(), s.httpServer.Addr) - - for { - stream, err := session.AcceptStream() - if err != nil { - s.l.Errorf("accept stream err: %s", err) - break - } - select { - case s.connChan <- stream: - default: - stream.Close() - s.l.Infof("%s - %s: connection queue is full", conn.RemoteAddr(), conn.LocalAddr()) - } - } -} - -func (s *MWSSServer) Accept() (conn net.Conn, err error) { - select { - case conn = <-s.connChan: - case err = <-s.errChan: - } - return -} - -func (s *MWSSServer) Close() error { - return s.httpServer.Close() +func (s *MwssServer) Close() error { + return s.e.Close() } diff --git a/echo/internal/transporter/buffer.go b/echo/pkg/buffer/buffer.go similarity index 98% rename from echo/internal/transporter/buffer.go rename to echo/pkg/buffer/buffer.go index 8edd7ebef5..8ae9d3ebe0 100644 --- a/echo/internal/transporter/buffer.go +++ b/echo/pkg/buffer/buffer.go @@ -1,4 +1,4 @@ -package transporter +package buffer import ( "github.com/Ehco1996/ehco/internal/constant" diff --git a/echo/pkg/sub/clash_types.go b/echo/pkg/sub/clash_types.go index d36e6822d7..f40f9e488a 100644 --- a/echo/pkg/sub/clash_types.go +++ b/echo/pkg/sub/clash_types.go @@ -133,8 +133,8 @@ func (p *Proxies) ToRelayConfig(listenHost string, listenPort string, newName st remoteAddr := net.JoinHostPort(p.Server, p.Port) r := &relay_cfg.Config{ Label: newName, - ListenType: constant.Listen_RAW, - TransportType: constant.Transport_RAW, + ListenType: constant.RelayTypeRaw, + TransportType: constant.RelayTypeRaw, Listen: net.JoinHostPort(listenHost, listenPort), TCPRemotes: []string{remoteAddr}, } diff --git a/echo/test/relay_test.go b/echo/test/relay_test.go index a01410c4b8..d271f0678f 100644 --- a/echo/test/relay_test.go +++ b/echo/test/relay_test.go @@ -56,66 +56,66 @@ func init() { // raw cfg { Listen: RAW_LISTEN, - ListenType: constant.Listen_RAW, + ListenType: constant.RelayTypeRaw, TCPRemotes: []string{ECHO_SERVER}, UDPRemotes: []string{ECHO_SERVER}, - TransportType: constant.Transport_RAW, + TransportType: constant.RelayTypeRaw, }, // ws { Listen: WS_LISTEN, - ListenType: constant.Listen_RAW, + ListenType: constant.RelayTypeRaw, TCPRemotes: []string{WS_REMOTE}, - TransportType: constant.Transport_WS, + TransportType: constant.RelayTypeWS, }, { Listen: WS_SERVER, - ListenType: constant.Listen_WS, + ListenType: constant.RelayTypeWS, TCPRemotes: []string{ECHO_SERVER}, - TransportType: constant.Transport_RAW, + TransportType: constant.RelayTypeRaw, }, // wss { Listen: WSS_LISTEN, - ListenType: constant.Listen_RAW, + ListenType: constant.RelayTypeRaw, TCPRemotes: []string{WSS_REMOTE}, - TransportType: constant.Transport_WSS, + TransportType: constant.RelayTypeWSS, }, { Listen: WSS_SERVER, - ListenType: constant.Listen_WSS, + ListenType: constant.RelayTypeWSS, TCPRemotes: []string{ECHO_SERVER}, - TransportType: constant.Transport_RAW, + TransportType: constant.RelayTypeRaw, }, // mwss { Listen: MWSS_LISTEN, - ListenType: constant.Listen_RAW, + ListenType: constant.RelayTypeRaw, TCPRemotes: []string{MWSS_REMOTE}, - TransportType: constant.Transport_MWSS, + TransportType: constant.RelayTypeMWSS, }, { Listen: MWSS_SERVER, - ListenType: constant.Listen_MWSS, + ListenType: constant.RelayTypeMWSS, TCPRemotes: []string{ECHO_SERVER}, - TransportType: constant.Transport_RAW, + TransportType: constant.RelayTypeRaw, }, // mtcp { Listen: MTCP_LISTEN, - ListenType: constant.Listen_RAW, + ListenType: constant.RelayTypeRaw, TCPRemotes: []string{MTCP_REMOTE}, - TransportType: constant.Transport_MTCP, + TransportType: constant.RelayTypeMTCP, }, { Listen: MTCP_SERVER, - ListenType: constant.Listen_MTCP, + ListenType: constant.RelayTypeMTCP, TCPRemotes: []string{ECHO_SERVER}, - TransportType: constant.Transport_RAW, + TransportType: constant.RelayTypeRaw, }, }, } diff --git a/mihomo/adapter/provider/parser.go b/mihomo/adapter/provider/parser.go index bbaf29be31..1094668d46 100644 --- a/mihomo/adapter/provider/parser.go +++ b/mihomo/adapter/provider/parser.go @@ -88,16 +88,13 @@ func ParseProxyProvider(name string, mapping map[string]any) (types.ProxyProvide path := C.Path.Resolve(schema.Path) vehicle = resource.NewFileVehicle(path) case "http": - var path string + path := C.Path.GetPathByHash("proxies", schema.URL) if schema.Path != "" { path = C.Path.Resolve(schema.Path) if !features.CMFA && !C.Path.IsSafePath(path) { return nil, fmt.Errorf("%w: %s", errSubPath, path) } - } else { - path = C.Path.GetPathByHash("proxies", schema.URL) } - vehicle = resource.NewHTTPVehicle(schema.URL, path, schema.Proxy, schema.Header) default: return nil, fmt.Errorf("%w: %s", errVehicleType, schema.Type) diff --git a/mihomo/adapter/provider/provider.go b/mihomo/adapter/provider/provider.go index aa5b823350..2715a30972 100644 --- a/mihomo/adapter/provider/provider.go +++ b/mihomo/adapter/provider/provider.go @@ -125,7 +125,7 @@ func (pp *proxySetProvider) getSubscriptionInfo() { ctx, cancel := context.WithTimeout(context.Background(), time.Second*90) defer cancel() resp, err := mihomoHttp.HttpRequest(ctx, pp.Vehicle().(*resource.HTTPVehicle).Url(), - http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil, "") + http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil) if err != nil { return } @@ -134,7 +134,7 @@ func (pp *proxySetProvider) getSubscriptionInfo() { userInfoStr := strings.TrimSpace(resp.Header.Get("subscription-userinfo")) if userInfoStr == "" { resp2, err := mihomoHttp.HttpRequest(ctx, pp.Vehicle().(*resource.HTTPVehicle).Url(), - http.MethodGet, http.Header{"User-Agent": {"Quantumultx"}}, nil, "") + http.MethodGet, http.Header{"User-Agent": {"Quantumultx"}}, nil) if err != nil { return } diff --git a/mihomo/component/geodata/init.go b/mihomo/component/geodata/init.go index 64022f013b..834567a447 100644 --- a/mihomo/component/geodata/init.go +++ b/mihomo/component/geodata/init.go @@ -47,7 +47,7 @@ func InitGeoSite() error { func downloadGeoSite(path string) (err error) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*90) defer cancel() - resp, err := mihomoHttp.HttpRequest(ctx, C.GeoSiteUrl, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil, "") + resp, err := mihomoHttp.HttpRequest(ctx, C.GeoSiteUrl, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil) if err != nil { return } @@ -66,7 +66,7 @@ func downloadGeoSite(path string) (err error) { func downloadGeoIP(path string) (err error) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*90) defer cancel() - resp, err := mihomoHttp.HttpRequest(ctx, C.GeoIpUrl, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil, "") + resp, err := mihomoHttp.HttpRequest(ctx, C.GeoIpUrl, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil) if err != nil { return } diff --git a/mihomo/component/http/http.go b/mihomo/component/http/http.go index ef37e328b8..21d65d2e96 100644 --- a/mihomo/component/http/http.go +++ b/mihomo/component/http/http.go @@ -16,7 +16,11 @@ import ( "github.com/metacubex/mihomo/listener/inner" ) -func HttpRequest(ctx context.Context, url, method string, header map[string][]string, body io.Reader, specialProxy string) (*http.Response, error) { +func HttpRequest(ctx context.Context, url, method string, header map[string][]string, body io.Reader) (*http.Response, error) { + return HttpRequestWithProxy(ctx, url, method, header, body, "") +} + +func HttpRequestWithProxy(ctx context.Context, url, method string, header map[string][]string, body io.Reader, specialProxy string) (*http.Response, error) { method = strings.ToUpper(method) urlRes, err := URL.Parse(url) if err != nil { diff --git a/mihomo/component/mmdb/mmdb.go b/mihomo/component/mmdb/mmdb.go index 120739fc15..81156bc62d 100644 --- a/mihomo/component/mmdb/mmdb.go +++ b/mihomo/component/mmdb/mmdb.go @@ -82,7 +82,7 @@ func IPInstance() IPReader { func DownloadMMDB(path string) (err error) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*90) defer cancel() - resp, err := mihomoHttp.HttpRequest(ctx, C.MmdbUrl, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil, "") + resp, err := mihomoHttp.HttpRequest(ctx, C.MmdbUrl, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil) if err != nil { return } @@ -115,7 +115,7 @@ func ASNInstance() ASNReader { func DownloadASN(path string) (err error) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*90) defer cancel() - resp, err := mihomoHttp.HttpRequest(ctx, C.ASNUrl, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil, "") + resp, err := mihomoHttp.HttpRequest(ctx, C.ASNUrl, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil) if err != nil { return } diff --git a/mihomo/component/resource/vehicle.go b/mihomo/component/resource/vehicle.go index 2f71c2e6c6..2d71be9455 100644 --- a/mihomo/component/resource/vehicle.go +++ b/mihomo/component/resource/vehicle.go @@ -54,7 +54,7 @@ func (h *HTTPVehicle) Path() string { func (h *HTTPVehicle) Read() ([]byte, error) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*20) defer cancel() - resp, err := mihomoHttp.HttpRequest(ctx, h.url, http.MethodGet, h.header, nil, h.proxy) + resp, err := mihomoHttp.HttpRequestWithProxy(ctx, h.url, http.MethodGet, h.header, nil, h.proxy) if err != nil { return nil, err } diff --git a/mihomo/config/utils.go b/mihomo/config/utils.go index 596199ca18..66bf3441f2 100644 --- a/mihomo/config/utils.go +++ b/mihomo/config/utils.go @@ -20,7 +20,7 @@ import ( func downloadForBytes(url string) ([]byte, error) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*90) defer cancel() - resp, err := mihomoHttp.HttpRequest(ctx, url, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil, "") + resp, err := mihomoHttp.HttpRequest(ctx, url, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil) if err != nil { return nil, err } diff --git a/mihomo/hub/updater/updater.go b/mihomo/hub/updater/updater.go index e0d3a39ca1..02ff07ba26 100644 --- a/mihomo/hub/updater/updater.go +++ b/mihomo/hub/updater/updater.go @@ -234,7 +234,7 @@ const MaxPackageFileSize = 32 * 1024 * 1024 func downloadPackageFile() (err error) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*90) defer cancel() - resp, err := mihomoHttp.HttpRequest(ctx, packageURL, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil, "") + resp, err := mihomoHttp.HttpRequest(ctx, packageURL, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil) if err != nil { return fmt.Errorf("http request failed: %w", err) } @@ -415,7 +415,7 @@ func copyFile(src, dst string) error { func getLatestVersion() (version string, err error) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() - resp, err := mihomoHttp.HttpRequest(ctx, versionURL, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil, "") + resp, err := mihomoHttp.HttpRequest(ctx, versionURL, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil) if err != nil { return "", fmt.Errorf("get Latest Version fail: %w", err) } diff --git a/mihomo/rules/provider/parse.go b/mihomo/rules/provider/parse.go index e7920adf6c..a20da28d5c 100644 --- a/mihomo/rules/provider/parse.go +++ b/mihomo/rules/provider/parse.go @@ -62,15 +62,12 @@ func ParseRuleProvider(name string, mapping map[string]interface{}, parse func(t path := C.Path.Resolve(schema.Path) vehicle = resource.NewFileVehicle(path) case "http": - var path string + path := C.Path.GetPathByHash("rules", schema.URL) if schema.Path != "" { path = C.Path.Resolve(schema.Path) if !features.CMFA && !C.Path.IsSafePath(path) { return nil, fmt.Errorf("%w: %s", errSubPath, path) } - } else { - path = C.Path.GetPathByHash("rules", schema.URL) - } vehicle = resource.NewHTTPVehicle(schema.URL, path, schema.Proxy, nil) default: diff --git a/openwrt-packages/luci-app-store/Makefile b/openwrt-packages/luci-app-store/Makefile index 8cb1502640..319b2651eb 100644 --- a/openwrt-packages/luci-app-store/Makefile +++ b/openwrt-packages/luci-app-store/Makefile @@ -11,7 +11,7 @@ LUCI_DEPENDS:=+curl +opkg +luci-base +tar +coreutils +coreutils-stat +libuci-lua LUCI_EXTRA_DEPENDS:=luci-lib-taskd (>=1.0.17) LUCI_PKGARCH:=all -PKG_VERSION:=0.1.14-2 +PKG_VERSION:=0.1.14-3 # PKG_RELEASE MUST be empty for luci.mk PKG_RELEASE:= diff --git a/openwrt-packages/luci-app-store/root/bin/is-opkg b/openwrt-packages/luci-app-store/root/bin/is-opkg index 01698cc2bc..5bc97ae65d 100755 --- a/openwrt-packages/luci-app-store/root/bin/is-opkg +++ b/openwrt-packages/luci-app-store/root/bin/is-opkg @@ -58,11 +58,13 @@ update() { return 1 fi - fcurl -o ${OPKG_CONF_DIR}/meta.conf "${FEEDS_SERVER}/all/meta.conf" && \ - fcurl -o ${OPKG_CONF_DIR}/all.conf "${FEEDS_SERVER}/all/isfeeds.conf" && \ - fcurl -o ${OPKG_CONF_DIR}/arch.conf "${FEEDS_SERVER}/${ARCH}/isfeeds.conf" || \ + echo "Fetch feed list for ${ARCH}" + fcurl --no-progress-meter -o ${OPKG_CONF_DIR}/meta.conf "${FEEDS_SERVER}/all/meta.conf" && \ + fcurl --no-progress-meter -o ${OPKG_CONF_DIR}/all.conf "${FEEDS_SERVER}/all/isfeeds.conf" && \ + fcurl --no-progress-meter -o ${OPKG_CONF_DIR}/arch.conf "${FEEDS_SERVER}/${ARCH}/isfeeds.conf" || \ return 1 + echo "Update feeds index" opkg -f ${IS_ROOT}/etc/opkg_o.conf --offline-root ${IS_ROOT} update return 0 diff --git a/small/chinadns-ng/Makefile b/small/chinadns-ng/Makefile index 7bb5555289..e524022526 100644 --- a/small/chinadns-ng/Makefile +++ b/small/chinadns-ng/Makefile @@ -12,8 +12,8 @@ ifeq ($(ARCH),aarch64) else ifeq ($(ARCH),arm) ARM_CPU_FEATURES:=$(word 2,$(subst +,$(space),$(call qstrip,$(CONFIG_CPU_TYPE)))) ifeq ($(ARM_CPU_FEATURES),) - PKG_ARCH:=chinadns-ng@arm-linux-musleabi@generic+v7a@fast+lto - PKG_HASH:=c2e0378e20f5fda376d4f52bfbe1a3dbc7dfe23879e8edc8ef7ab55694cfd676 + PKG_ARCH:=chinadns-ng@arm-linux-musleabi@generic+v6+soft_float@fast+lto + PKG_HASH:=f9e597bbc060d7c5e19c837a3c0b3118f91c986f29ee7f55a23ace321aca1731 else PKG_ARCH:=chinadns-ng@arm-linux-musleabihf@generic+v7a@fast+lto PKG_HASH:=d065c7d55b6c43b20dbb668d7bdafabe371c3360cc75bd0279cc2d7a83e342e9 diff --git a/small/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua b/small/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua index d2b87a7fa0..44b29af29a 100644 --- a/small/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua +++ b/small/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua @@ -525,7 +525,9 @@ o.default = "3" o.rmempty = true o = s:option(Value, "timeout", translate("Timeout for establishing a connection to server(second)")) +o.description = translate("Default value 0 indicatesno heartbeat.") o:depends("type", "tuic") +o:depends({type = "v2ray", v2ray_protocol = "wireguard"}) o.datatype = "uinteger" o.default = "8" o.rmempty = true @@ -831,11 +833,22 @@ o:depends("transport", "kcp") o.rmempty = true -- [[ WireGuard 部分 ]]-- +o = s:option(Flag, "kernelmode", translate("Enabled Kernel virtual NIC TUN(optional)")) +o.description = translate("Virtual NIC TUN of Linux kernel can be used only when system supports and have root permission. If used, IPv6 routing table 1023 is occupied.") +o:depends({type = "v2ray", v2ray_protocol = "wireguard"}) +o.default = "0" +o.rmempty = true + o = s:option(DynamicList, "local_addresses", translate("Local addresses")) o.datatype = "cidr" o:depends({type = "v2ray", v2ray_protocol = "wireguard"}) o.rmempty = true +o = s:option(DynamicList, "reserved", translate("Reserved bytes(optional)")) +o.description = translate("Wireguard reserved bytes.") +o:depends({type = "v2ray", v2ray_protocol = "wireguard"}) +o.rmempty = true + o = s:option(Value, "private_key", translate("Private key")) o:depends({type = "v2ray", v2ray_protocol = "wireguard"}) o.password = true @@ -850,6 +863,13 @@ o:depends({type = "v2ray", v2ray_protocol = "wireguard"}) o.password = true o.rmempty = true +o = s:option(DynamicList, "allowedips", translate("allowedIPs(optional)")) +o.description = translate("Wireguard allows only traffic from specific source IP.") +o.datatype = "cidr" +o:depends({type = "v2ray", v2ray_protocol = "wireguard"}) +o.default = "0.0.0.0/0" +o.rmempty = true + -- [[ TLS ]]-- o = s:option(Flag, "tls", translate("TLS")) o.rmempty = true diff --git a/small/luci-app-ssr-plus/po/zh-cn/ssr-plus.po b/small/luci-app-ssr-plus/po/zh-cn/ssr-plus.po index d343d4f28a..632b5c9e44 100644 --- a/small/luci-app-ssr-plus/po/zh-cn/ssr-plus.po +++ b/small/luci-app-ssr-plus/po/zh-cn/ssr-plus.po @@ -942,9 +942,21 @@ msgstr "写入缓冲区大小" msgid "Congestion" msgstr "拥塞控制" +msgid "Enabled Kernel virtual NIC TUN(optional)" +msgstr "启用内核的虚拟网卡 TUN(可选)" + +msgid "Virtual NIC TUN of Linux kernel can be used only when system supports and have root permission. If used, IPv6 routing table 1023 is occupied." +msgstr "需要系统支持且有 root 权限才能使用 Linux 内核的虚拟网卡 TUN,使用后会占用 IPv6 的 1023 号路由表。" + msgid "Local addresses" msgstr "本地地址" +msgid "Reserved bytes(optional)" +msgstr "保留字节(可选)" + +msgid "Wireguard reserved bytes." +msgstr "Wireguard 保留字节。" + msgid "Private key" msgstr "私钥" @@ -954,6 +966,15 @@ msgstr "节点公钥" msgid "Pre-shared key" msgstr "预共享密钥" +msgid "Default value 0 indicatesno heartbeat." +msgstr "默认为 0 表示无心跳。" + +msgid "allowedIPs(optional)" +msgstr "allowedIPs(可选)" + +msgid "Wireguard allows only traffic from specific source IP." +msgstr "Wireguard 仅允许特定源 IP 的流量。" + msgid "Network interface to use" msgstr "使用的网络接口" diff --git a/small/luci-app-ssr-plus/root/usr/share/shadowsocksr/gen_config.lua b/small/luci-app-ssr-plus/root/usr/share/shadowsocksr/gen_config.lua index cbb911ebed..b07949e5f1 100755 --- a/small/luci-app-ssr-plus/root/usr/share/shadowsocksr/gen_config.lua +++ b/small/luci-app-ssr-plus/root/usr/share/shadowsocksr/gen_config.lua @@ -72,9 +72,13 @@ function wireguard() { publicKey = server.peer_pubkey, preSharedKey = server.preshared_key, - endpoint = server.server .. ":" .. server.server_port + endpoint = server.server .. ":" .. server.server_port, + keepAlive = tonumber(server.heartbeat), + allowedIPs = (server.allowedips) or nil, } }, + kernelMode = (server.kernelmode == "1") and true or false, + reserved = {server.reserved} or nil, mtu = tonumber(server.mtu) } end @@ -172,7 +176,7 @@ local Xray = { protocol = server.v2ray_protocol, settings = outbound_settings, -- 底层传输配置 - streamSettings = { + streamSettings = (server.v2ray_protocol ~= "wireguard") and { network = server.transport or "tcp", security = (server.tls == '1') and "tls" or (server.reality == '1') and "reality" or nil, tlsSettings = (server.tls == '1') and { @@ -258,14 +262,14 @@ local Xray = { tcpNoDelay = (server.mptcp == "1") and true or false, -- MPTCP tcpcongestion = server.custom_tcpcongestion -- 连接服务器节点的 TCP 拥塞控制算法 } - }, - mux = { + } or nil, + mux = (server.v2ray_protocol ~= "wireguard") and { -- mux enabled = (server.mux == "1") and true or false, -- Mux concurrency = tonumber(server.concurrency), -- TCP 最大并发连接数 xudpConcurrency = tonumber(server.xudpConcurrency), -- UDP 最大并发连接数 xudpProxyUDP443 = server.xudpProxyUDP443 -- 对被代理的 UDP/443 流量处理方式 - } + } or nil } } local cipher = "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:AES128-SHA:AES256-SHA:DES-CBC3-SHA" @@ -322,7 +326,7 @@ local ss = { } local hysteria = { server = (server.server_port and (server.port_range and (server.server .. ":" .. server.server_port .. "," .. server.port_range) or server.server .. ":" .. server.server_port) or (server.port_range and server.server .. ":" .. server.port_range or server.server .. ":443")), - bandwidth = { + bandwidth = (server.uplink_capacity or server.downlink_capacity) and { up = tonumber(server.uplink_capacity) and tonumber(server.uplink_capacity) .. " mbps" or nil, down = tonumber(server.downlink_capacity) and tonumber(server.downlink_capacity) .. " mbps" or nil }, @@ -336,12 +340,11 @@ local hysteria = { hopInterval = (server.port_range and (tonumber(server.hopinterval) .. "s") or nil) } or nil) } or nil, - --[[ tcpTProxy = (proto:find("tcp") and local_port ~= "0") and { - listen = "0.0.0.0:" .. tonumber(local_port) -} or nil, -]] + listen = "0.0.0.0:" .. tonumber(local_port) + } or nil, +]]-- tcpRedirect = (proto:find("tcp") and local_port ~= "0") and { listen = "0.0.0.0:" .. tonumber(local_port) } or nil, @@ -359,7 +362,7 @@ local hysteria = { maxConnReceiveWindow = (server.maxconnreceivewindow and server.maxconnreceivewindow or nil), maxIdleTimeout = (tonumber(server.maxidletimeout) and tonumber(server.maxidletimeout) .. "s" or nil), keepAlivePeriod = (tonumber(server.keepaliveperiod) and tonumber(server.keepaliveperiod) .. "s" or nil), - disable_mtu_discovery = (server.disablepathmtudiscovery == "1") and true or false + disablePathMTUDiscovery = (server.disablepathmtudiscovery == "1") and true or false } or nil, auth = server.hy2_auth, tls = (server.tls_host) and { @@ -394,7 +397,7 @@ local chain_sslocal = { mode = (proto:find("tcp,udp") and "tcp_and_udp") or proto .. "_only", protocol = "redir", tcp_redir = "redirect", - --tcp_redir = "tproxy", + --tcp_redir = "tproxy", udp_redir = "tproxy" }, socks_port ~= "0" and { diff --git a/suyu/.ci/scripts/windows/docker.sh b/suyu/.ci/scripts/windows/docker.sh index 70eadda41e..73e000324c 100755 --- a/suyu/.ci/scripts/windows/docker.sh +++ b/suyu/.ci/scripts/windows/docker.sh @@ -1,6 +1,7 @@ #!/bin/bash -ex # SPDX-FileCopyrightText: 2019 yuzu Emulator Project +# SPDX-FileCopyrightText: 2024 suyu Emulator Project # SPDX-License-Identifier: GPL-2.0-or-later set -e @@ -9,12 +10,15 @@ set -e ccache -sv +rm -rf build mkdir -p build && cd build -cmake .. \ +/usr/bin/x86_64-w64-mingw32-cmake .. \ -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_TOOLCHAIN_FILE="${PWD}/../CMakeModules/MinGWCross.cmake" \ -DDISPLAY_VERSION="$1" \ - -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON \ + -DDYNARMIC_USE_PRECOMPILED_HEADERS=OFF \ + -DSUYU_USE_PRECOMPILED_HEADERS=OFF \ + -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=OFF \ + -DUSE_DISCORD_PRESENCE=ON \ -DENABLE_QT_TRANSLATION=ON \ -DUSE_CCACHE=ON \ -DSUYU_USE_BUNDLED_SDL2=OFF \ diff --git a/suyu/.forgejo/workflows/verify.yml b/suyu/.forgejo/workflows/verify.yml index f89166dbde..1292ddcfa9 100644 --- a/suyu/.forgejo/workflows/verify.yml +++ b/suyu/.forgejo/workflows/verify.yml @@ -63,8 +63,8 @@ jobs: image: linux-fresh - type: linux image: linux-fresh - # - type: windows - # image: linux-mingw + - type: windows + image: linux-mingw container: fijxu/build-environments:${{ matrix.image }} # User 1001 doesn't exists on the images. # options: -u 1001 diff --git a/xray-core/app/proxyman/outbound/handler.go b/xray-core/app/proxyman/outbound/handler.go index 41713d0b8c..792ac24971 100644 --- a/xray-core/app/proxyman/outbound/handler.go +++ b/xray-core/app/proxyman/outbound/handler.go @@ -2,12 +2,8 @@ package outbound import ( "context" + "crypto/rand" "errors" - "io" - "math/rand" - gonet "net" - "os" - "github.com/xtls/xray-core/app/proxyman" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/buf" @@ -25,6 +21,10 @@ import ( "github.com/xtls/xray-core/transport/internet/stat" "github.com/xtls/xray-core/transport/internet/tls" "github.com/xtls/xray-core/transport/pipe" + "io" + "math/big" + gonet "net" + "os" ) func getStatCounter(v *core.Instance, tag string) (stats.Counter, stats.Counter) { @@ -319,16 +319,21 @@ func (h *Handler) Close() error { return nil } -// Return random IPv6 in a CIDR block + func ParseRandomIPv6(address net.Address, prefix string) net.Address { - addr := address.IP().String() - _, network, _ := gonet.ParseCIDR(addr + "/" + prefix) + _, network, _ := gonet.ParseCIDR(address.IP().String() + "/" + prefix) - ipv6 := network.IP.To16() - prefixLen, _ := network.Mask.Size() - for i := prefixLen / 8; i < 16; i++ { - ipv6[i] = byte(rand.Intn(256)) - } + maskSize, totalBits := network.Mask.Size() + subnetSize := big.NewInt(1).Lsh(big.NewInt(1), uint(totalBits-maskSize)) - return net.ParseAddress(gonet.IP(ipv6).String()) + // random + randomBigInt, _ := rand.Int(rand.Reader, subnetSize) + + startIPBigInt := big.NewInt(0).SetBytes(network.IP.To16()) + randomIPBigInt := big.NewInt(0).Add(startIPBigInt, randomBigInt) + + randomIPBytes := randomIPBigInt.Bytes() + randomIPBytes = append(make([]byte, 16-len(randomIPBytes)), randomIPBytes...) + + return net.ParseAddress(gonet.IP(randomIPBytes).String()) } diff --git a/xray-core/go.mod b/xray-core/go.mod index 92fad3c3b4..bb3a6beb9d 100644 --- a/xray-core/go.mod +++ b/xray-core/go.mod @@ -20,16 +20,16 @@ require ( github.com/vishvananda/netlink v1.2.1-beta.2.0.20230316163032-ced5aaba43e3 github.com/xtls/reality v0.0.0-20231112171332-de1173cf2b19 go4.org/netipx v0.0.0-20231129151722-fdeea329fbba - golang.org/x/crypto v0.21.0 - golang.org/x/net v0.23.0 - golang.org/x/sync v0.6.0 - golang.org/x/sys v0.18.0 + golang.org/x/crypto v0.22.0 + golang.org/x/net v0.24.0 + golang.org/x/sync v0.7.0 + golang.org/x/sys v0.19.0 golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 google.golang.org/grpc v1.63.0 google.golang.org/protobuf v1.33.0 gvisor.dev/gvisor v0.0.0-20231104011432-48a6d7d5bd0b h12.io/socks v1.0.3 - lukechampine.com/blake3 v1.2.1 + lukechampine.com/blake3 v1.2.2 ) require ( diff --git a/xray-core/go.sum b/xray-core/go.sum index b9a4797b92..f0450753b7 100644 --- a/xray-core/go.sum +++ b/xray-core/go.sum @@ -179,8 +179,8 @@ golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ= golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= @@ -201,8 +201,8 @@ golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -214,8 +214,8 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= -golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -228,8 +228,8 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220804214406-8e32c043e418/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -299,7 +299,7 @@ h12.io/socks v1.0.3/go.mod h1:AIhxy1jOId/XCz9BO+EIgNL2rQiPTBNnOfnVnQ+3Eck= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI= -lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= +lukechampine.com/blake3 v1.2.2 h1:wEAbSg0IVU4ih44CVlpMqMZMpzr5hf/6aqodLlevd/w= +lukechampine.com/blake3 v1.2.2/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= diff --git a/xray-core/proxy/freedom/freedom.go b/xray-core/proxy/freedom/freedom.go index 558536462f..0176929cc4 100644 --- a/xray-core/proxy/freedom/freedom.go +++ b/xray-core/proxy/freedom/freedom.go @@ -373,6 +373,9 @@ func (f *FragmentWriter) Write(b []byte) (int, error) { return f.writer.Write(b) } recordLen := 5 + ((int(b[3]) << 8) | int(b[4])) + if len(b) < recordLen { // maybe already fragmented somehow + return f.writer.Write(b) + } data := b[5:recordLen] buf := make([]byte, 1024) for from := 0; ; { diff --git a/yass/.github/workflows/releases-linux-binary.yml b/yass/.github/workflows/releases-linux-binary.yml index 23d2c1289e..8ab1b92a2e 100644 --- a/yass/.github/workflows/releases-linux-binary.yml +++ b/yass/.github/workflows/releases-linux-binary.yml @@ -57,10 +57,13 @@ jobs: qemu_suffix: mipsel - arch: mips64el qemu_suffix: mips64el + - arch: loongarch64 + qemu_suffix: loongarch64 + - arch: riscv64 + qemu_suffix: riscv64 + - arch: riscv32 + qemu_suffix: riscv32 runs-on: ubuntu-20.04 - env: - ARCH: ${{ matrix.arch }} - SDK_ROOT: ${{ github.workspace }}/debian_bullseye_${{ matrix.arch }}-sysroot steps: - uses: actions/checkout@v4 - name: Checkout with shallow submodules @@ -73,6 +76,18 @@ jobs: run: | cd third_party/libc++abi patch -p1 < v8-6.7.17-fix-gcc-unwind-header.patch + - name: Set SDK_ROOT + if: ${{ matrix.arch != 'loongarch64' && matrix.arch != 'riscv64' && matrix.arch != 'riscv32' }} + run: | + echo "SDK_ROOT=${{ github.workspace }}/debian_bullseye_${{ matrix.arch }}-sysroot" >> $GITHUB_ENV + - name: Set SDK_ROOT for loongarch64 + if: ${{ matrix.arch == 'loongarch64' }} + run: | + echo "SDK_ROOT=${{ github.workspace }}/debian_bullseye_${{ matrix.arch }}-sysroot/target" >> $GITHUB_ENV + - name: Set SDK_ROOT for riscv64 and riscv32 + if: ${{ matrix.arch == 'riscv64' || matrix.arch == 'riscv32' }} + run: | + echo "SDK_ROOT=${{ github.workspace }}/debian_bullseye_${{ matrix.arch }}-sysroot/sysroot" >> $GITHUB_ENV - name: Cache clang id: clang-cache uses: actions/cache@v4 @@ -101,8 +116,8 @@ jobs: uses: actions/cache@v4 with: path: | - ${{ env.SDK_ROOT }} - key: ${{ runner.os }}-sysroot-${{ matrix.arch }}-${{ hashFiles('scripts/sysroots.json') }}-v1 + ${{ github.workspace }}/debian_bullseye_${{ matrix.arch }}-sysroot + key: ${{ runner.os }}-sysroot-${{ matrix.arch }}-${{ hashFiles('scripts/sysroots.json') }}-v2 - name: Build build tool run: | cd tools @@ -117,9 +132,27 @@ jobs: run: | wget http://ftp.us.debian.org/debian/pool/main/q/qemu/qemu-user-static_8.2.2+ds-2_amd64.deb - name: "Install dependency: sysroot" - if: ${{ steps.sysroot-cache.outputs.cache-hit != 'true' }} + if: ${{ steps.sysroot-cache.outputs.cache-hit != 'true' && matrix.arch != 'loongarch64' && matrix.arch != 'riscv64' && matrix.arch != 'riscv32' }} run: | - ./scripts/install-sysroot.py --arch ${{ env.ARCH }} + ./scripts/install-sysroot.py --arch ${{ matrix.arch }} + - name: "Install dependency: sysroot (loongarch64)" + if: ${{ steps.sysroot-cache.outputs.cache-hit != 'true' && matrix.arch == 'loongarch64' }} + run: | + mkdir -p debian_bullseye_loongarch64-sysroot + cd debian_bullseye_loongarch64-sysroot + curl -L https://github.com/loongson/build-tools/releases/download/2023.08.08/CLFS-loongarch64-8.1-x86_64-cross-tools-gcc-glibc.tar.xz | tar --strip-components=1 --xz -xf - + cp -fv ./loongarch64-unknown-linux-gnu/lib/libgcc_s.so target/lib64/ + cp -fv ./loongarch64-unknown-linux-gnu/lib/libgcc_s.so.1 target/lib64/ + rm -rf bin include libexec loongarch64-unknown-linux-gnu share + cd .. + - name: "Install dependency: sysroot (riscv64 and riscv32)" + if: ${{ steps.sysroot-cache.outputs.cache-hit != 'true' && (matrix.arch == 'riscv64' || matrix.arch == 'riscv32') }} + run: | + mkdir -p debian_bullseye_${{ matrix.arch }}-sysroot + cd debian_bullseye_${{ matrix.arch }}-sysroot + curl -L https://github.com/riscv-collab/riscv-gnu-toolchain/releases/download/2023.07.07/${{ matrix.arch }}-glibc-ubuntu-20.04-gcc-nightly-2023.07.07-nightly.tar.gz | tar --strip-components=1 --gz -xf - + rm -rf bin include libexec ${{ matrix.arch }}-unknown-linux-gnu share + cd .. - name: Change ubuntu mirror run: | sudo sed -i 's/azure.archive.ubuntu.com/azure.archive.ubuntu.com/g' /etc/apt/sources.list @@ -134,11 +167,14 @@ jobs: sudo apt install qemu-user sudo apt remove -y qemu-user-binfmt sudo dpkg -i qemu-user-static_*.deb - - name: Build TGZ packages + - name: Build TGZ packages (CLI and Server) run: | - ./tools/build --variant gui --arch ${{ matrix.arch }} --system linux --sysroot ${{ env.SDK_ROOT }} -build-benchmark -build-test -nc ./tools/build --variant cli --arch ${{ matrix.arch }} --system linux --sysroot ${{ env.SDK_ROOT }} -build-benchmark -build-test -nc ./tools/build --variant server --arch ${{ matrix.arch }} --system linux --sysroot ${{ env.SDK_ROOT }} -build-benchmark -build-test -nc + - name: Build TGZ packages (GUI) + if: ${{ matrix.arch != 'loongarch64' && matrix.arch != 'riscv64' && matrix.arch != 'riscv32' }} + run: | + ./tools/build --variant gui --arch ${{ matrix.arch }} --system linux --sysroot ${{ env.SDK_ROOT }} -build-benchmark -build-test -nc - name: Run tests (i386 and amd64) if: ${{ matrix.arch == 'i386' || matrix.arch == 'amd64' }} run: | diff --git a/yass/CMakeLists.txt b/yass/CMakeLists.txt index 51f8d286d6..a4e4a58ef7 100644 --- a/yass/CMakeLists.txt +++ b/yass/CMakeLists.txt @@ -310,6 +310,9 @@ else() if (CMAKE_C_COMPILER_TARGET MATCHES "^riscv64-.*") set(OS_RISCV64 TRUE) endif() + if (CMAKE_C_COMPILER_TARGET MATCHES "^riscv32-.*") + set(OS_RISCV32 TRUE) + endif() # Fix MINGW (native mingw)'s CMAKE_SYSTEM_PROCESSOR if (MINGW) if (OS_X86) @@ -529,6 +532,14 @@ if (UNIX AND CMAKE_CROSSCOMPILING AND CMAKE_SYSROOT AND COMPILER_CLANG AND NOT C # fix up for loongarch sysroot if (CMAKE_SYSTEM_PROCESSOR STREQUAL "loongarch64") file(GLOB _GCC_SYSROOT "${CMAKE_SYSROOT}/../../lib/gcc/*/*.*.*") + # required by loongarch64 sdk https://github.com/loongson/build-tools/releases + if (NOT _GCC_SYSROOT) + file(GLOB _GCC_SYSROOT "${CMAKE_SYSROOT}/../lib/gcc/*/*.*.*") + endif() + endif() + # fix up for riscv64 and riscv32 sysroot + if (CMAKE_SYSTEM_PROCESSOR STREQUAL "riscv64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "riscv32") + file(GLOB _GCC_SYSROOT "${CMAKE_SYSROOT}/../lib/gcc/*/*.*.*") endif() if (_GCC_SYSROOT) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --gcc-install-dir=${_GCC_SYSROOT}") @@ -2044,8 +2055,17 @@ elseif (GUI) set(GUI off) endif() + # fix up for loongarch gtk support + # see https://github.com/microcai/gentoo-zh/pull/4486 + if (CMAKE_SYSTEM_PROCESSOR STREQUAL "loongarch64") + message(STATUS "Blacklist gtk4 for loongarch64 target") + set(BLACKLIST_GTK4 ON) + else() + set(BLACKLIST_GTK4 OFF) + endif() + pkg_check_modules(GTK4 gtk4) - if (GTK4_FOUND) + if (GTK4_FOUND AND NOT BLACKLIST_GTK4) set(GUI_FLAVOUR "gtk4") set(GUI_OTHER_FLAGS -Wno-deprecated-declarations) @@ -2280,6 +2300,10 @@ if (USE_TCMALLOC) message(WARNING "tcmalloc: loongarch64 is not supported, disabling...") set(USE_TCMALLOC OFF) endif() + if (CMAKE_SYSTEM_PROCESSOR STREQUAL "riscv32") + message(WARNING "tcmalloc: riscv32 is not supported, disabling...") + set(USE_TCMALLOC OFF) + endif() endif() # tunings: diff --git a/yass/src/core/debug.cpp b/yass/src/core/debug.cpp index ae0a74f8e1..c5e0b6b217 100644 --- a/yass/src/core/debug.cpp +++ b/yass/src/core/debug.cpp @@ -4,6 +4,7 @@ #include "core/debug.hpp" #include "core/check_op.hpp" +#include #include #include #include @@ -38,7 +39,7 @@ bool WaitForDebugger(int wait_seconds, bool silent) { BreakDebugger(); return true; } - // FIXME PlatformThread::Sleep(Milliseconds(100)); + absl::SleepFor(absl::Milliseconds(100)); } return false; } @@ -287,7 +288,7 @@ void DebugBreak() { #else volatile int go = 0; while (!go) - PlatformThread::Sleep(Milliseconds(100)); + absl::SleepFor(absl::Milliseconds(100)); #endif } } diff --git a/yass/src/core/rand_util_posix.cpp b/yass/src/core/rand_util_posix.cpp index 4c65c75e0c..82c1df3a14 100644 --- a/yass/src/core/rand_util_posix.cpp +++ b/yass/src/core/rand_util_posix.cpp @@ -72,7 +72,8 @@ class URandomFd { // (https://chromium-review.googlesource.com/c/chromium/src/+/1545096) and land // it or some form of it. void RandBytes(void* output, size_t output_length) { -#ifdef __linux__ +// FIXME currently lss doesn't like riscv32 +#if defined(__linux__) && !defined(ARCH_CPU_RISCV32) // We have to call `getrandom` via Linux Syscall Support, rather than through // the libc wrapper, because we might not have an up-to-date libc (e.g. on // some bots). diff --git a/yass/third_party/boringssl/src/crypto/fipsmodule/rand/getrandom_fillin.h b/yass/third_party/boringssl/src/crypto/fipsmodule/rand/getrandom_fillin.h index 5f03768be7..73d267cb85 100644 --- a/yass/third_party/boringssl/src/crypto/fipsmodule/rand/getrandom_fillin.h +++ b/yass/third_party/boringssl/src/crypto/fipsmodule/rand/getrandom_fillin.h @@ -32,6 +32,8 @@ #define EXPECTED_NR_getrandom 384 #elif defined(OPENSSL_RISCV64) #define EXPECTED_NR_getrandom 278 +#elif defined(OPENSSL_RISCV) +#define EXPECTED_NR_getrandom 278 #elif defined(OPENSSL_LOONGARCH64) #define EXPECTED_NR_getrandom 278 #endif diff --git a/yass/third_party/boringssl/src/include/openssl/target.h b/yass/third_party/boringssl/src/include/openssl/target.h index c38114df3b..638190dfd6 100644 --- a/yass/third_party/boringssl/src/include/openssl/target.h +++ b/yass/third_party/boringssl/src/include/openssl/target.h @@ -45,12 +45,13 @@ #define OPENSSL_RISCV64 #elif defined(__riscv) && __SIZEOF_POINTER__ == 4 #define OPENSSL_32_BIT -#elif defined(__loongarch__) && __loongarch_grlen == 64 +#define OPENSSL_RISCV +#elif defined(__loongarch__) && !defined(__loongarch_lp64) +#define OPENSSL_32_BIT +#define OPENSSL_LOONGARCH +#elif defined(__loongarch__) && defined(__loongarch_lp64) #define OPENSSL_64_BIT #define OPENSSL_LOONGARCH64 -#elif defined(__loongarch__) && __loongarch_grlen == 32 -#define OPENSSL_32_BIT -#define OPENSSL_LOONGARCH32 #elif defined(__pnacl__) #define OPENSSL_32_BIT #define OPENSSL_PNACL diff --git a/yass/third_party/googleurl-override/build/build_config.h b/yass/third_party/googleurl-override/build/build_config.h index fd0651fd39..a151d61372 100644 --- a/yass/third_party/googleurl-override/build/build_config.h +++ b/yass/third_party/googleurl-override/build/build_config.h @@ -358,6 +358,11 @@ #define ARCH_CPU_RISCV64 1 #define ARCH_CPU_64_BITS 1 #define ARCH_CPU_LITTLE_ENDIAN 1 +#elif defined(__riscv) && (__riscv_xlen == 32) +#define ARCH_CPU_RISCV_FAMILY 1 +#define ARCH_CPU_RISCV32 1 +#define ARCH_CPU_32_BITS 1 +#define ARCH_CPU_LITTLE_ENDIAN 1 #else #error Please add support for your architecture in build/build_config.h #endif diff --git a/yass/third_party/quiche/CMakeLists.txt b/yass/third_party/quiche/CMakeLists.txt index 55956ce604..45c8994ea8 100644 --- a/yass/third_party/quiche/CMakeLists.txt +++ b/yass/third_party/quiche/CMakeLists.txt @@ -730,7 +730,6 @@ target_include_directories(quiche PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/overrides ${CMAKE_CURRENT_BINARY_DIR}/src ${CMAKE_CURRENT_BINARY_DIR} - ${CMAKE_CURRENT_SOURCE_DIR}/../googleurl ${CMAKE_CURRENT_SOURCE_DIR}/.. ${CMAKE_CURRENT_SOURCE_DIR}/../../src ${CMAKE_CURRENT_SOURCE_DIR}/../../ diff --git a/yass/tools/build.go b/yass/tools/build.go index 149746e08c..7fe5c86404 100644 --- a/yass/tools/build.go +++ b/yass/tools/build.go @@ -471,6 +471,10 @@ func getGNUTargetTypeAndArch(arch string, subsystem string) (string, string) { return "mips64el-linux-gnuabi64", "mips64el" } else if arch == "loongarch64" { return "loongarch64-linux-gnu", "loongarch64" + } else if arch == "riscv64" { + return "riscv64-linux-gnu", "riscv64" + } else if arch == "riscv32" { + return "riscv32-linux-gnu", "riscv32" } glog.Fatalf("Invalid arch: %s", arch) return "", "" diff --git a/youtube-dl/youtube_dl/postprocessor/ffmpeg.py b/youtube-dl/youtube_dl/postprocessor/ffmpeg.py index e5ffdf3788..214825aa97 100644 --- a/youtube-dl/youtube_dl/postprocessor/ffmpeg.py +++ b/youtube-dl/youtube_dl/postprocessor/ffmpeg.py @@ -74,8 +74,11 @@ class FFmpegPostProcessor(PostProcessor): return FFmpegPostProcessor(downloader)._versions def _determine_executables(self): - programs = ['avprobe', 'avconv', 'ffmpeg', 'ffprobe'] + # ordered to match prefer_ffmpeg! + convs = ['ffmpeg', 'avconv'] + probes = ['ffprobe', 'avprobe'] prefer_ffmpeg = True + programs = convs + probes def get_ffmpeg_version(path): ver = get_exe_version(path, args=['-version']) @@ -127,10 +130,13 @@ class FFmpegPostProcessor(PostProcessor): (p, get_ffmpeg_version(self._paths[p])) for p in programs) if x[1] is not None) - for p in ('ffmpeg', 'avconv')[::-1 if prefer_ffmpeg is False else 1]: - if self._versions.get(p): - self.basename = self.probe_basename = p - break + basenames = [None, None] + for i, progs in enumerate((convs, probes)): + for p in progs[::-1 if prefer_ffmpeg is False else 1]: + if self._versions.get(p): + basenames[i] = p + break + self.basename, self.probe_basename = basenames @property def available(self): diff --git a/yt-dlp/yt_dlp/cookies.py b/yt-dlp/yt_dlp/cookies.py index 85d6dd1823..7b8d215f03 100644 --- a/yt-dlp/yt_dlp/cookies.py +++ b/yt-dlp/yt_dlp/cookies.py @@ -194,7 +194,11 @@ def _firefox_browser_dirs(): yield os.path.expanduser('~/Library/Application Support/Firefox/Profiles') else: - yield from map(os.path.expanduser, ('~/.mozilla/firefox', '~/snap/firefox/common/.mozilla/firefox')) + yield from map(os.path.expanduser, ( + '~/.mozilla/firefox', + '~/snap/firefox/common/.mozilla/firefox', + '~/.var/app/org.mozilla.firefox/.mozilla/firefox', + )) def _firefox_cookie_dbs(roots): diff --git a/yt-dlp/yt_dlp/extractor/nhk.py b/yt-dlp/yt_dlp/extractor/nhk.py index 7cf5b246b1..8bb017a732 100644 --- a/yt-dlp/yt_dlp/extractor/nhk.py +++ b/yt-dlp/yt_dlp/extractor/nhk.py @@ -8,6 +8,7 @@ from ..utils import ( int_or_none, join_nonempty, parse_duration, + remove_end, traverse_obj, try_call, unescapeHTML, @@ -19,8 +20,7 @@ from ..utils import ( class NhkBaseIE(InfoExtractor): _API_URL_TEMPLATE = 'https://nwapi.nhk.jp/nhkworld/%sod%slist/v7b/%s/%s/%s/all%s.json' - _BASE_URL_REGEX = r'https?://www3\.nhk\.or\.jp/nhkworld/(?P[a-z]{2})/ondemand' - _TYPE_REGEX = r'/(?Pvideo|audio)/' + _BASE_URL_REGEX = r'https?://www3\.nhk\.or\.jp/nhkworld/(?P[a-z]{2})/' def _call_api(self, m_id, lang, is_video, is_episode, is_clip): return self._download_json( @@ -83,7 +83,7 @@ class NhkBaseIE(InfoExtractor): def _extract_episode_info(self, url, episode=None): fetch_episode = episode is None lang, m_type, episode_id = NhkVodIE._match_valid_url(url).group('lang', 'type', 'id') - is_video = m_type == 'video' + is_video = m_type != 'audio' if is_video: episode_id = episode_id[:4] + '-' + episode_id[4:] @@ -138,9 +138,10 @@ class NhkBaseIE(InfoExtractor): else: if fetch_episode: - audio_path = episode['audio']['audio'] + # From https://www3.nhk.or.jp/nhkworld/common/player/radio/inline/rod.html + audio_path = remove_end(episode['audio']['audio'], '.m4a') info['formats'] = self._extract_m3u8_formats( - 'https://nhkworld-vh.akamaihd.net/i%s/master.m3u8' % audio_path, + f'{urljoin("https://vod-stream.nhk.jp", audio_path)}/index.m3u8', episode_id, 'm4a', entry_protocol='m3u8_native', m3u8_id='hls', fatal=False) for f in info['formats']: @@ -155,9 +156,11 @@ class NhkBaseIE(InfoExtractor): class NhkVodIE(NhkBaseIE): - # the 7-character IDs can have alphabetic chars too: assume [a-z] rather than just [a-f], eg - _VALID_URL = [rf'{NhkBaseIE._BASE_URL_REGEX}/(?Pvideo)/(?P[0-9a-z]+)', - rf'{NhkBaseIE._BASE_URL_REGEX}/(?Paudio)/(?P[^/?#]+?-\d{{8}}-[0-9a-z]+)'] + _VALID_URL = [ + rf'{NhkBaseIE._BASE_URL_REGEX}shows/(?:(?Pvideo)/)?(?P\d{{4}}[\da-z]\d+)/?(?:$|[?#])', + rf'{NhkBaseIE._BASE_URL_REGEX}(?:ondemand|shows)/(?Paudio)/(?P[^/?#]+?-\d{{8}}-[\da-z]+)', + rf'{NhkBaseIE._BASE_URL_REGEX}ondemand/(?Pvideo)/(?P\d{{4}}[\da-z]\d+)', # deprecated + ] # Content available only for a limited period of time. Visit # https://www3.nhk.or.jp/nhkworld/en/ondemand/ for working samples. _TESTS = [{ @@ -167,17 +170,16 @@ class NhkVodIE(NhkBaseIE): 'ext': 'mp4', 'title': 'Japan Railway Journal - The Tohoku Shinkansen: Full Speed Ahead', 'description': 'md5:49f7c5b206e03868a2fdf0d0814b92f6', - 'thumbnail': 'md5:51bcef4a21936e7fea1ff4e06353f463', + 'thumbnail': r're:https://.+/.+\.jpg', 'episode': 'The Tohoku Shinkansen: Full Speed Ahead', 'series': 'Japan Railway Journal', - 'modified_timestamp': 1694243656, + 'modified_timestamp': 1707217907, 'timestamp': 1681428600, 'release_timestamp': 1693883728, 'duration': 1679, 'upload_date': '20230413', - 'modified_date': '20230909', + 'modified_date': '20240206', 'release_date': '20230905', - }, }, { # video clip @@ -188,15 +190,15 @@ class NhkVodIE(NhkBaseIE): 'ext': 'mp4', 'title': 'Dining with the Chef - Chef Saito\'s Family recipe: MENCHI-KATSU', 'description': 'md5:5aee4a9f9d81c26281862382103b0ea5', - 'thumbnail': 'md5:d6a4d9b6e9be90aaadda0bcce89631ed', + 'thumbnail': r're:https://.+/.+\.jpg', 'series': 'Dining with the Chef', 'episode': 'Chef Saito\'s Family recipe: MENCHI-KATSU', 'duration': 148, 'upload_date': '20190816', 'release_date': '20230902', 'release_timestamp': 1693619292, - 'modified_timestamp': 1694168033, - 'modified_date': '20230908', + 'modified_timestamp': 1707217907, + 'modified_date': '20240206', 'timestamp': 1565997540, }, }, { @@ -208,7 +210,7 @@ class NhkVodIE(NhkBaseIE): 'title': 'Living in Japan - Tips for Travelers to Japan / Ramen Vending Machines', 'series': 'Living in Japan', 'description': 'md5:0a0e2077d8f07a03071e990a6f51bfab', - 'thumbnail': 'md5:960622fb6e06054a4a1a0c97ea752545', + 'thumbnail': r're:https://.+/.+\.jpg', 'episode': 'Tips for Travelers to Japan / Ramen Vending Machines' }, }, { @@ -245,7 +247,7 @@ class NhkVodIE(NhkBaseIE): 'title': 'おはよう日本(7時台) - 10月8日放送', 'series': 'おはよう日本(7時台)', 'episode': '10月8日放送', - 'thumbnail': 'md5:d733b1c8e965ab68fb02b2d347d0e9b4', + 'thumbnail': r're:https://.+/.+\.jpg', 'description': 'md5:9c1d6cbeadb827b955b20e99ab920ff0', }, 'skip': 'expires 2023-10-15', @@ -255,17 +257,100 @@ class NhkVodIE(NhkBaseIE): 'info_dict': { 'id': 'nw_vod_v_en_3004_952_20230723091000_01_1690074552', 'ext': 'mp4', - 'title': 'Barakan Discovers AMAMI OSHIMA: Isson\'s Treasure Island', + 'title': 'Barakan Discovers - AMAMI OSHIMA: Isson\'s Treasure Isla', 'description': 'md5:5db620c46a0698451cc59add8816b797', - 'thumbnail': 'md5:67d9ff28009ba379bfa85ad1aaa0e2bd', + 'thumbnail': r're:https://.+/.+\.jpg', 'release_date': '20230905', 'timestamp': 1690103400, 'duration': 2939, 'release_timestamp': 1693898699, - 'modified_timestamp': 1698057495, - 'modified_date': '20231023', 'upload_date': '20230723', + 'modified_timestamp': 1707217907, + 'modified_date': '20240206', + 'episode': 'AMAMI OSHIMA: Isson\'s Treasure Isla', + 'series': 'Barakan Discovers', }, + }, { + # /ondemand/video/ url with alphabetical character in 5th position of id + 'url': 'https://www3.nhk.or.jp/nhkworld/en/ondemand/video/9999a07/', + 'info_dict': { + 'id': 'nw_c_en_9999-a07', + 'ext': 'mp4', + 'episode': 'Mini-Dramas on SDGs: Ep 1 Close the Gender Gap [Director\'s Cut]', + 'series': 'Mini-Dramas on SDGs', + 'modified_date': '20240206', + 'title': 'Mini-Dramas on SDGs - Mini-Dramas on SDGs: Ep 1 Close the Gender Gap [Director\'s Cut]', + 'description': 'md5:3f9dcb4db22fceb675d90448a040d3f6', + 'timestamp': 1621962360, + 'duration': 189, + 'release_date': '20230903', + 'modified_timestamp': 1707217907, + 'upload_date': '20210525', + 'thumbnail': r're:https://.+/.+\.jpg', + 'release_timestamp': 1693713487, + }, + }, { + 'url': 'https://www3.nhk.or.jp/nhkworld/en/ondemand/video/9999d17/', + 'info_dict': { + 'id': 'nw_c_en_9999-d17', + 'ext': 'mp4', + 'title': 'Flowers of snow blossom - The 72 Pentads of Yamato', + 'description': 'Today’s focus: Snow', + 'release_timestamp': 1693792402, + 'release_date': '20230904', + 'upload_date': '20220128', + 'timestamp': 1643370960, + 'thumbnail': r're:https://.+/.+\.jpg', + 'duration': 136, + 'series': '', + 'modified_date': '20240206', + 'modified_timestamp': 1707217907, + }, + }, { + # new /shows/ url format + 'url': 'https://www3.nhk.or.jp/nhkworld/en/shows/2032307/', + 'info_dict': { + 'id': 'nw_vod_v_en_2032_307_20240321113000_01_1710990282', + 'ext': 'mp4', + 'title': 'Japanology Plus - 20th Anniversary Special Part 1', + 'description': 'md5:817d41fc8e54339ad2a916161ea24faf', + 'episode': '20th Anniversary Special Part 1', + 'series': 'Japanology Plus', + 'thumbnail': r're:https://.+/.+\.jpg', + 'duration': 1680, + 'timestamp': 1711020600, + 'upload_date': '20240321', + 'release_timestamp': 1711022683, + 'release_date': '20240321', + 'modified_timestamp': 1711031012, + 'modified_date': '20240321', + }, + }, { + 'url': 'https://www3.nhk.or.jp/nhkworld/en/shows/3020025/', + 'info_dict': { + 'id': 'nw_vod_v_en_3020_025_20230325144000_01_1679723944', + 'ext': 'mp4', + 'title': '100 Ideas to Save the World - Working Styles Evolve', + 'description': 'md5:9e6c7778eaaf4f7b4af83569649f84d9', + 'episode': 'Working Styles Evolve', + 'series': '100 Ideas to Save the World', + 'thumbnail': r're:https://.+/.+\.jpg', + 'duration': 899, + 'upload_date': '20230325', + 'timestamp': 1679755200, + 'release_date': '20230905', + 'release_timestamp': 1693880540, + 'modified_date': '20240206', + 'modified_timestamp': 1707217907, + }, + }, { + # new /shows/audio/ url format + 'url': 'https://www3.nhk.or.jp/nhkworld/en/shows/audio/livinginjapan-20231001-1/', + 'only_matching': True, + }, { + # valid url even if can't be found in wild; support needed for clip entries extraction + 'url': 'https://www3.nhk.or.jp/nhkworld/en/shows/9999o80/', + 'only_matching': True, }] def _real_extract(self, url): @@ -273,18 +358,21 @@ class NhkVodIE(NhkBaseIE): class NhkVodProgramIE(NhkBaseIE): - _VALID_URL = rf'{NhkBaseIE._BASE_URL_REGEX}/program{NhkBaseIE._TYPE_REGEX}(?P\w+)(?:.+?\btype=(?Pclip|(?:radio|tv)Episode))?' + _VALID_URL = rf'''(?x) + {NhkBaseIE._BASE_URL_REGEX}(?:shows|tv)/ + (?:(?Paudio)/programs/)?(?P\w+)/? + (?:\?(?:[^#]+&)?type=(?Pclip|(?:radio|tv)Episode))?''' _TESTS = [{ # video program episodes - 'url': 'https://www3.nhk.or.jp/nhkworld/en/ondemand/program/video/sumo', + 'url': 'https://www3.nhk.or.jp/nhkworld/en/shows/sumo/', 'info_dict': { 'id': 'sumo', 'title': 'GRAND SUMO Highlights', 'description': 'md5:fc20d02dc6ce85e4b72e0273aa52fdbf', }, - 'playlist_mincount': 0, + 'playlist_mincount': 1, }, { - 'url': 'https://www3.nhk.or.jp/nhkworld/en/ondemand/program/video/japanrailway', + 'url': 'https://www3.nhk.or.jp/nhkworld/en/shows/japanrailway/', 'info_dict': { 'id': 'japanrailway', 'title': 'Japan Railway Journal', @@ -293,40 +381,68 @@ class NhkVodProgramIE(NhkBaseIE): 'playlist_mincount': 12, }, { # video program clips - 'url': 'https://www3.nhk.or.jp/nhkworld/en/ondemand/program/video/japanrailway/?type=clip', + 'url': 'https://www3.nhk.or.jp/nhkworld/en/shows/japanrailway/?type=clip', 'info_dict': { 'id': 'japanrailway', 'title': 'Japan Railway Journal', 'description': 'md5:ea39d93af7d05835baadf10d1aae0e3f', }, - 'playlist_mincount': 5, - }, { - 'url': 'https://www3.nhk.or.jp/nhkworld/en/ondemand/program/video/10yearshayaomiyazaki/', - 'only_matching': True, + 'playlist_mincount': 12, }, { # audio program - 'url': 'https://www3.nhk.or.jp/nhkworld/en/ondemand/program/audio/listener/', + 'url': 'https://www3.nhk.or.jp/nhkworld/en/shows/audio/programs/livinginjapan/', + 'info_dict': { + 'id': 'livinginjapan', + 'title': 'Living in Japan', + 'description': 'md5:665bb36ec2a12c5a7f598ee713fc2b54', + }, + 'playlist_mincount': 12, + }, { + # /tv/ program url + 'url': 'https://www3.nhk.or.jp/nhkworld/en/tv/designtalksplus/', + 'info_dict': { + 'id': 'designtalksplus', + 'title': 'DESIGN TALKS plus', + 'description': 'md5:47b3b3a9f10d4ac7b33b53b70a7d2837', + }, + 'playlist_mincount': 20, + }, { + 'url': 'https://www3.nhk.or.jp/nhkworld/en/shows/10yearshayaomiyazaki/', 'only_matching': True, }] + @classmethod + def suitable(cls, url): + return False if NhkVodIE.suitable(url) else super().suitable(url) + + def _extract_meta_from_class_elements(self, class_values, html): + for class_value in class_values: + if value := clean_html(get_element_by_class(class_value, html)): + return value + def _real_extract(self, url): lang, m_type, program_id, episode_type = self._match_valid_url(url).group('lang', 'type', 'id', 'episode_type') episodes = self._call_api( - program_id, lang, m_type == 'video', False, episode_type == 'clip') + program_id, lang, m_type != 'audio', False, episode_type == 'clip') - entries = [] - for episode in episodes: - episode_path = episode.get('url') - if not episode_path: - continue - entries.append(self._extract_episode_info( - urljoin(url, episode_path), episode)) + def entries(): + for episode in episodes: + if episode_path := episode.get('url'): + yield self._extract_episode_info(urljoin(url, episode_path), episode) html = self._download_webpage(url, program_id) - program_title = clean_html(get_element_by_class('p-programDetail__title', html)) - program_description = clean_html(get_element_by_class('p-programDetail__text', html)) + program_title = self._extract_meta_from_class_elements([ + 'p-programDetail__title', # /ondemand/program/ + 'pProgramHero__logoText', # /shows/ + 'tAudioProgramMain__title', # /shows/audio/programs/ + 'p-program-name'], html) # /tv/ + program_description = self._extract_meta_from_class_elements([ + 'p-programDetail__text', # /ondemand/program/ + 'pProgramHero__description', # /shows/ + 'tAudioProgramMain__info', # /shows/audio/programs/ + 'p-program-description'], html) # /tv/ - return self.playlist_result(entries, program_id, program_title, program_description) + return self.playlist_result(entries(), program_id, program_title, program_description) class NhkForSchoolBangumiIE(InfoExtractor): diff --git a/yt-dlp/yt_dlp/extractor/patreon.py b/yt-dlp/yt_dlp/extractor/patreon.py index d2ddb72cd4..d4f822f52d 100644 --- a/yt-dlp/yt_dlp/extractor/patreon.py +++ b/yt-dlp/yt_dlp/extractor/patreon.py @@ -92,7 +92,7 @@ class PatreonIE(PatreonBaseIE): 'thumbnail': 're:^https?://.*$', 'upload_date': '20150211', 'description': 'md5:8af6425f50bd46fbf29f3db0fc3a8364', - 'uploader_id': 'TraciJHines', + 'uploader_id': '@TraciHinesMusic', 'categories': ['Entertainment'], 'duration': 282, 'view_count': int, @@ -106,8 +106,10 @@ class PatreonIE(PatreonBaseIE): 'availability': 'public', 'channel_follower_count': int, 'playable_in_embed': True, - 'uploader_url': 'http://www.youtube.com/user/TraciJHines', + 'uploader_url': 'https://www.youtube.com/@TraciHinesMusic', 'comment_count': int, + 'channel_is_verified': True, + 'chapters': 'count:4', }, 'params': { 'noplaylist': True, @@ -176,6 +178,27 @@ class PatreonIE(PatreonBaseIE): 'uploader_url': 'https://www.patreon.com/thenormies', }, 'skip': 'Patron-only content', + }, { + # dead vimeo and embed URLs, need to extract post_file + 'url': 'https://www.patreon.com/posts/hunter-x-hunter-34007913', + 'info_dict': { + 'id': '34007913', + 'ext': 'mp4', + 'title': 'Hunter x Hunter | Kurapika DESTROYS Uvogin!!!', + 'like_count': int, + 'uploader': 'YaBoyRoshi', + 'timestamp': 1581636833, + 'channel_url': 'https://www.patreon.com/yaboyroshi', + 'thumbnail': r're:^https?://.*$', + 'tags': ['Hunter x Hunter'], + 'uploader_id': '14264111', + 'comment_count': int, + 'channel_follower_count': int, + 'description': 'Kurapika is a walking cheat code!', + 'upload_date': '20200213', + 'channel_id': '2147162', + 'uploader_url': 'https://www.patreon.com/yaboyroshi', + }, }] def _real_extract(self, url): @@ -250,20 +273,13 @@ class PatreonIE(PatreonBaseIE): v_url = url_or_none(compat_urllib_parse_unquote( self._search_regex(r'(https(?:%3A%2F%2F|://)player\.vimeo\.com.+app_id(?:=|%3D)+\d+)', embed_html, 'vimeo url', fatal=False))) if v_url: - return { - **info, - '_type': 'url_transparent', - 'url': VimeoIE._smuggle_referrer(v_url, 'https://patreon.com'), - 'ie_key': 'Vimeo', - } + v_url = VimeoIE._smuggle_referrer(v_url, 'https://patreon.com') + if self._request_webpage(v_url, video_id, 'Checking Vimeo embed URL', fatal=False, errnote=False): + return self.url_result(v_url, VimeoIE, url_transparent=True, **info) embed_url = try_get(attributes, lambda x: x['embed']['url']) - if embed_url: - return { - **info, - '_type': 'url', - 'url': embed_url, - } + if embed_url and self._request_webpage(embed_url, video_id, 'Checking embed URL', fatal=False, errnote=False): + return self.url_result(embed_url, **info) post_file = traverse_obj(attributes, 'post_file') if post_file: diff --git a/yt-dlp/yt_dlp/extractor/tiktok.py b/yt-dlp/yt_dlp/extractor/tiktok.py index 295e14932a..3f5261ad96 100644 --- a/yt-dlp/yt_dlp/extractor/tiktok.py +++ b/yt-dlp/yt_dlp/extractor/tiktok.py @@ -155,6 +155,7 @@ class TikTokBaseIE(InfoExtractor): 'locale': 'en', 'ac2': 'wifi5g', 'uoo': '1', + 'carrier_region': 'US', 'op_region': 'US', 'build_number': self._APP_INFO['app_version'], 'region': 'US', diff --git a/yt-dlp/yt_dlp/extractor/vk.py b/yt-dlp/yt_dlp/extractor/vk.py index e4a78c2977..7e3a3a9a98 100644 --- a/yt-dlp/yt_dlp/extractor/vk.py +++ b/yt-dlp/yt_dlp/extractor/vk.py @@ -707,6 +707,7 @@ class VKWallPostIE(VKBaseIE): class VKPlayBaseIE(InfoExtractor): + _BASE_URL_RE = r'https?://(?:vkplay\.live|live\.vkplay\.ru)/' _RESOLUTIONS = { 'tiny': '256x144', 'lowest': '426x240', @@ -765,7 +766,7 @@ class VKPlayBaseIE(InfoExtractor): class VKPlayIE(VKPlayBaseIE): - _VALID_URL = r'https?://vkplay\.live/(?P[^/#?]+)/record/(?P[a-f0-9-]+)' + _VALID_URL = rf'{VKPlayBaseIE._BASE_URL_RE}(?P[^/#?]+)/record/(?P[\da-f-]+)' _TESTS = [{ 'url': 'https://vkplay.live/zitsmann/record/f5e6e3b5-dc52-4d14-965d-0680dd2882da', 'info_dict': { @@ -776,13 +777,16 @@ class VKPlayIE(VKPlayBaseIE): 'uploader_id': '13159830', 'release_timestamp': 1683461378, 'release_date': '20230507', - 'thumbnail': r're:https://images.vkplay.live/public_video_stream/record/f5e6e3b5-dc52-4d14-965d-0680dd2882da/preview\?change_time=\d+', + 'thumbnail': r're:https://[^/]+/public_video_stream/record/f5e6e3b5-dc52-4d14-965d-0680dd2882da/preview', 'duration': 10608, 'view_count': int, 'like_count': int, 'categories': ['Atomic Heart'], }, 'params': {'skip_download': 'm3u8'}, + }, { + 'url': 'https://live.vkplay.ru/lebwa/record/33a4e4ce-e3ef-49db-bb14-f006cc6fabc9/records', + 'only_matching': True, }] def _real_extract(self, url): @@ -802,7 +806,7 @@ class VKPlayIE(VKPlayBaseIE): class VKPlayLiveIE(VKPlayBaseIE): - _VALID_URL = r'https?://vkplay\.live/(?P[^/#?]+)/?(?:[#?]|$)' + _VALID_URL = rf'{VKPlayBaseIE._BASE_URL_RE}(?P[^/#?]+)/?(?:[#?]|$)' _TESTS = [{ 'url': 'https://vkplay.live/bayda', 'info_dict': { @@ -813,7 +817,7 @@ class VKPlayLiveIE(VKPlayBaseIE): 'uploader_id': '12279401', 'release_timestamp': 1687209962, 'release_date': '20230619', - 'thumbnail': r're:https://images.vkplay.live/public_video_stream/12279401/preview\?change_time=\d+', + 'thumbnail': r're:https://[^/]+/public_video_stream/12279401/preview', 'view_count': int, 'concurrent_view_count': int, 'like_count': int, @@ -822,6 +826,9 @@ class VKPlayLiveIE(VKPlayBaseIE): }, 'skip': 'livestream', 'params': {'skip_download': True}, + }, { + 'url': 'https://live.vkplay.ru/lebwa', + 'only_matching': True, }] def _real_extract(self, url):