From ce0208fb9e2ae4f1eca2f1688b26a72de04e5f52 Mon Sep 17 00:00:00 2001 From: "github-action[bot]" Date: Sat, 13 Apr 2024 20:25:06 +0200 Subject: [PATCH] Update On Sat Apr 13 20:25:05 CEST 2024 --- .github/update.log | 1 + clash-meta/component/resolver/system.go | 4 + clash-nyanpasu/.prettierignore | 1 + clash-nyanpasu/frontend/interface/index.ts | 2 + .../frontend/interface/ipc/index.ts | 1 + .../frontend/interface/ipc/useNyanpasu.ts | 34 ++ .../frontend/interface/package.json | 10 + .../frontend/interface/service/index.ts | 2 + .../frontend/interface/service/tauri.ts | 12 + .../frontend/interface/service/types.ts | 46 ++ .../frontend/interface/tsconfig.json | 24 + clash-nyanpasu/frontend/nyanpasu/package.json | 9 +- .../frontend/nyanpasu/src/router.ts | 20 +- clash-nyanpasu/manifest/version.json | 6 +- clash-nyanpasu/package.json | 2 +- clash-nyanpasu/pnpm-lock.yaml | 202 +++++---- clash-verge-rev/src-tauri/src/core/service.rs | 2 +- clash-verge-rev/src-tauri/src/core/sysopt.rs | 5 +- echo/pkg/sub/clash.go | 43 +- hysteria/app/cmd/client.go | 46 +- hysteria/app/cmd/client_test.go | 13 + hysteria/app/cmd/client_test.yaml | 12 +- hysteria/app/go.mod | 4 +- hysteria/app/internal/proxymux/.mockery.yaml | 12 + .../proxymux/internal/mocks/mock_Conn.go | 427 ++++++++++++++++++ .../proxymux/internal/mocks/mock_Listener.go | 185 ++++++++ hysteria/app/internal/proxymux/manager.go | 72 +++ .../app/internal/proxymux/manager_test.go | 110 +++++ hysteria/app/internal/proxymux/mux.go | 320 +++++++++++++ hysteria/app/internal/proxymux/mux_test.go | 154 +++++++ .../sockopts/fd_control_unix_socket_test.py | 65 +++ hysteria/app/internal/sockopts/sockopts.go | 76 ++++ .../app/internal/sockopts/sockopts_linux.go | 96 ++++ .../internal/sockopts/sockopts_linux_test.go | 53 +++ hysteria/extras/transport/udphop/conn.go | 2 +- juicity/go.mod | 30 +- juicity/go.sum | 54 ++- juicity/server/server.go | 2 +- .../721-net-add-packet-mangeling.patch | 4 +- ...vert-driver-core-Set-fw_devlink-on-b.patch | 2 +- .../hack-6.1/930-usb-net-for-fm350.patch | 26 ++ ...-linux-kernel-to-support-shortcut-fe.patch | 2 +- .../hack-6.6/930-usb-net-for-fm350.patch | 26 ++ ...-linux-kernel-to-support-shortcut-fe.patch | 2 +- ...etfilter_match_bypass_default_checks.patch | 2 +- .../pending-6.1/655-increase_skb_pad.patch | 2 +- ...T-skip-GRO-for-foreign-MAC-addresses.patch | 4 +- ...-r8169-add-LED-configuration-from-OF.patch | 4 +- ...-r8169-add-LED-configuration-from-OF.patch | 4 +- mihomo/component/resolver/system.go | 4 + small/hysteria/Makefile | 4 +- v2raya/install/universal/v2raya.desktop | 2 +- .../V2rayNG/app/src/main/AndroidManifest.xml | 3 + .../main/kotlin/com/v2ray/ang/AppConfig.kt | 10 +- .../kotlin/com/v2ray/ang/ui/AboutActivity.kt | 46 ++ .../kotlin/com/v2ray/ang/ui/MainActivity.kt | 56 +-- .../com/v2ray/ang/ui/MainRecyclerAdapter.kt | 2 +- .../com/v2ray/ang/ui/UserAssetActivity.kt | 4 +- .../main/res/drawable-night/ic_about_24dp.xml | 12 + .../res/drawable-night/ic_feedback_24dp.xml | 13 +- .../main/res/drawable-night/ic_file_24dp.xml | 33 +- .../res/drawable-night/ic_logcat_24dp.xml | 18 +- .../res/drawable-night/ic_privacy_24dp.xml | 9 + .../res/drawable-night/ic_promotion_24dp.xml | 12 + .../res/drawable-night/ic_settings_24dp.xml | 13 +- .../drawable-night/ic_source_code_24dp.xml | 9 + .../drawable-night/ic_subscriptions_24dp.xml | 10 +- .../res/drawable-night/ic_telegram_24dp.xml | 9 + .../res/drawable-night/ic_whatshot_24dp.xml | 9 - .../src/main/res/drawable-xxhdpi/donate.png | Bin 3237 -> 0 bytes .../src/main/res/drawable/ic_about_24dp.xml | 12 + .../main/res/drawable/ic_feedback_24dp.xml | 13 +- .../src/main/res/drawable/ic_file_24dp.xml | 33 +- .../src/main/res/drawable/ic_logcat_24dp.xml | 20 +- .../src/main/res/drawable/ic_privacy_24dp.xml | 9 + .../main/res/drawable/ic_promotion_24dp.xml | 12 + .../main/res/drawable/ic_settings_24dp.xml | 15 +- .../main/res/drawable/ic_source_code_24dp.xml | 4 + .../res/drawable/ic_subscriptions_24dp.xml | 10 +- .../main/res/drawable/ic_telegram_24dp.xml | 9 + .../main/res/drawable/ic_whatshot_24dp.xml | 9 - .../src/main/res/layout/activity_about.xml | 143 ++++++ .../app/src/main/res/layout/activity_main.xml | 12 - .../app/src/main/res/menu/menu_drawer.xml | 12 +- .../app/src/main/res/values-ar/strings.xml | 3 + .../app/src/main/res/values-fa/strings.xml | 3 + .../app/src/main/res/values-ru/strings.xml | 13 +- .../app/src/main/res/values-vi/strings.xml | 3 + .../src/main/res/values-zh-rCN/strings.xml | 3 + .../src/main/res/values-zh-rTW/strings.xml | 3 + .../app/src/main/res/values/strings.xml | 3 + yass/src/config/config_impl_apple.mm | 48 +- yass/src/core/logging.cpp | 3 +- yass/src/core/utils.hpp | 29 ++ yass/src/core/utils_mac.mm | 4 +- yass/src/ios/YassAppDelegate.mm | 34 +- yass/src/ios/YassViewController.mm | 30 +- .../extensions/YassPacketTunnelProvider.mm | 18 +- yass/src/mac/YassAppDelegate.mm | 34 +- yass/src/mac/YassViewController.mm | 16 +- yass/src/mac/YassWindowController.mm | 8 +- yass/src/mac/utils_mac.mm | 54 +-- yass/src/net/asio_ssl.cpp | 16 +- yt-dlp/README.md | 3 + yt-dlp/yt_dlp/extractor/afreecatv.py | 66 ++- yt-dlp/yt_dlp/extractor/jiosaavn.py | 106 +++-- 106 files changed, 2750 insertions(+), 578 deletions(-) create mode 100644 clash-nyanpasu/frontend/interface/index.ts create mode 100644 clash-nyanpasu/frontend/interface/ipc/index.ts create mode 100644 clash-nyanpasu/frontend/interface/ipc/useNyanpasu.ts create mode 100644 clash-nyanpasu/frontend/interface/package.json create mode 100644 clash-nyanpasu/frontend/interface/service/index.ts create mode 100644 clash-nyanpasu/frontend/interface/service/tauri.ts create mode 100644 clash-nyanpasu/frontend/interface/service/types.ts create mode 100644 clash-nyanpasu/frontend/interface/tsconfig.json create mode 100644 hysteria/app/internal/proxymux/.mockery.yaml create mode 100644 hysteria/app/internal/proxymux/internal/mocks/mock_Conn.go create mode 100644 hysteria/app/internal/proxymux/internal/mocks/mock_Listener.go create mode 100644 hysteria/app/internal/proxymux/manager.go create mode 100644 hysteria/app/internal/proxymux/manager_test.go create mode 100644 hysteria/app/internal/proxymux/mux.go create mode 100644 hysteria/app/internal/proxymux/mux_test.go create mode 100644 hysteria/app/internal/sockopts/fd_control_unix_socket_test.py create mode 100644 hysteria/app/internal/sockopts/sockopts.go create mode 100644 hysteria/app/internal/sockopts/sockopts_linux.go create mode 100644 hysteria/app/internal/sockopts/sockopts_linux_test.go create mode 100644 lede/target/linux/generic/hack-6.1/930-usb-net-for-fm350.patch create mode 100644 lede/target/linux/generic/hack-6.6/930-usb-net-for-fm350.patch create mode 100644 v2rayng/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/AboutActivity.kt create mode 100644 v2rayng/V2rayNG/app/src/main/res/drawable-night/ic_about_24dp.xml create mode 100644 v2rayng/V2rayNG/app/src/main/res/drawable-night/ic_privacy_24dp.xml create mode 100644 v2rayng/V2rayNG/app/src/main/res/drawable-night/ic_promotion_24dp.xml create mode 100644 v2rayng/V2rayNG/app/src/main/res/drawable-night/ic_source_code_24dp.xml create mode 100644 v2rayng/V2rayNG/app/src/main/res/drawable-night/ic_telegram_24dp.xml delete mode 100644 v2rayng/V2rayNG/app/src/main/res/drawable-night/ic_whatshot_24dp.xml delete mode 100644 v2rayng/V2rayNG/app/src/main/res/drawable-xxhdpi/donate.png create mode 100644 v2rayng/V2rayNG/app/src/main/res/drawable/ic_about_24dp.xml create mode 100644 v2rayng/V2rayNG/app/src/main/res/drawable/ic_privacy_24dp.xml create mode 100644 v2rayng/V2rayNG/app/src/main/res/drawable/ic_promotion_24dp.xml create mode 100644 v2rayng/V2rayNG/app/src/main/res/drawable/ic_source_code_24dp.xml create mode 100644 v2rayng/V2rayNG/app/src/main/res/drawable/ic_telegram_24dp.xml delete mode 100644 v2rayng/V2rayNG/app/src/main/res/drawable/ic_whatshot_24dp.xml create mode 100644 v2rayng/V2rayNG/app/src/main/res/layout/activity_about.xml diff --git a/.github/update.log b/.github/update.log index c4afaa121a..c7d8fda0b8 100644 --- a/.github/update.log +++ b/.github/update.log @@ -615,3 +615,4 @@ Update On Tue Apr 9 20:25:08 CEST 2024 Update On Wed Apr 10 20:31:24 CEST 2024 Update On Thu Apr 11 20:26:19 CEST 2024 Update On Fri Apr 12 20:24:06 CEST 2024 +Update On Sat Apr 13 20:24:55 CEST 2024 diff --git a/clash-meta/component/resolver/system.go b/clash-meta/component/resolver/system.go index 4ed990a365..a134bb05ea 100644 --- a/clash-meta/component/resolver/system.go +++ b/clash-meta/component/resolver/system.go @@ -7,6 +7,10 @@ var blacklist struct { Mutex sync.Mutex } +func init() { + blacklist.Map = make(map[string]struct{}) +} + func AddSystemDnsBlacklist(names ...string) { blacklist.Mutex.Lock() defer blacklist.Mutex.Unlock() diff --git a/clash-nyanpasu/.prettierignore b/clash-nyanpasu/.prettierignore index dade694ee7..c3f863e3b9 100644 --- a/clash-nyanpasu/.prettierignore +++ b/clash-nyanpasu/.prettierignore @@ -6,3 +6,4 @@ dist/ pnpm-lock.yaml *.lock *.wxs +frontend/nyanpasu/src/router.ts diff --git a/clash-nyanpasu/frontend/interface/index.ts b/clash-nyanpasu/frontend/interface/index.ts new file mode 100644 index 0000000000..3933946e65 --- /dev/null +++ b/clash-nyanpasu/frontend/interface/index.ts @@ -0,0 +1,2 @@ +export * from "./ipc"; +export * from "./service"; diff --git a/clash-nyanpasu/frontend/interface/ipc/index.ts b/clash-nyanpasu/frontend/interface/ipc/index.ts new file mode 100644 index 0000000000..f122a8dc71 --- /dev/null +++ b/clash-nyanpasu/frontend/interface/ipc/index.ts @@ -0,0 +1 @@ +export * from "./useNyanpasu"; diff --git a/clash-nyanpasu/frontend/interface/ipc/useNyanpasu.ts b/clash-nyanpasu/frontend/interface/ipc/useNyanpasu.ts new file mode 100644 index 0000000000..e35cb43ff9 --- /dev/null +++ b/clash-nyanpasu/frontend/interface/ipc/useNyanpasu.ts @@ -0,0 +1,34 @@ +import useSWR from "swr"; +import { nyanpasuConfig, VergeConfig } from "@/service"; + +export const useNyanpasu = (options?: { + onUpdate?: (data?: VergeConfig) => void; + onError?: (error: any) => void; +}) => { + const { data, error, mutate } = useSWR("nynpasuConfig", () => + nyanpasuConfig.get(), + ); + + const setNyanpasuConfig = async (payload: Partial) => { + try { + await nyanpasuConfig.set(payload); + + const result = await mutate(); + + if (options?.onUpdate) { + options?.onUpdate(result); + } + } catch (error) { + if (options?.onError) { + options?.onError(error); + } + } + }; + + return { + nyanpasuConfig: data, + isLoading: !data && !error, + isError: error, + setNyanpasuConfig, + }; +}; diff --git a/clash-nyanpasu/frontend/interface/package.json b/clash-nyanpasu/frontend/interface/package.json new file mode 100644 index 0000000000..f0249134cd --- /dev/null +++ b/clash-nyanpasu/frontend/interface/package.json @@ -0,0 +1,10 @@ +{ + "name": "@nyanpasu/interface", + "version": "0.1.0", + "main": "index.ts", + "module": "index.ts", + "dependencies": { + "@tauri-apps/api": "1.5.3", + "swr": "2.2.5" + } +} diff --git a/clash-nyanpasu/frontend/interface/service/index.ts b/clash-nyanpasu/frontend/interface/service/index.ts new file mode 100644 index 0000000000..c758311d3e --- /dev/null +++ b/clash-nyanpasu/frontend/interface/service/index.ts @@ -0,0 +1,2 @@ +export * from "./types"; +export * from "./tauri"; diff --git a/clash-nyanpasu/frontend/interface/service/tauri.ts b/clash-nyanpasu/frontend/interface/service/tauri.ts new file mode 100644 index 0000000000..8359bee451 --- /dev/null +++ b/clash-nyanpasu/frontend/interface/service/tauri.ts @@ -0,0 +1,12 @@ +import { invoke } from "@tauri-apps/api/tauri"; +import { VergeConfig } from "./types"; + +export const nyanpasuConfig = { + get: async () => { + return await invoke("get_verge_config"); + }, + + set: async (payload: VergeConfig) => { + return await invoke("patch_verge_config", { payload }); + }, +}; diff --git a/clash-nyanpasu/frontend/interface/service/types.ts b/clash-nyanpasu/frontend/interface/service/types.ts new file mode 100644 index 0000000000..3595d0a336 --- /dev/null +++ b/clash-nyanpasu/frontend/interface/service/types.ts @@ -0,0 +1,46 @@ +export interface VergeConfig { + app_log_level?: "trace" | "debug" | "info" | "warn" | "error" | string; + language?: string; + clash_core?: "mihomo" | "mihomo-alpha" | "clash-rs" | "clash"; + theme_mode?: "light" | "dark" | "system"; + theme_blur?: boolean; + traffic_graph?: boolean; + enable_memory_usage?: boolean; + page_transition_animation?: string; + disable_auto_check_update?: boolean; + enable_tun_mode?: boolean; + enable_auto_launch?: boolean; + enable_service_mode?: boolean; + enable_silent_start?: boolean; + enable_system_proxy?: boolean; + enable_random_port?: boolean; + verge_mixed_port?: number; + enable_proxy_guard?: boolean; + proxy_guard_duration?: number; + system_proxy_bypass?: string; + web_ui_list?: string[]; + hotkeys?: string[]; + theme_setting?: { + primary_color?: string; + secondary_color?: string; + primary_text?: string; + secondary_text?: string; + info_color?: string; + error_color?: string; + warning_color?: string; + success_color?: string; + font_family?: string; + css_injection?: string; + page_transition_duration?: number; + }; + max_log_files?: number; + auto_close_connection?: boolean; + default_latency_test?: string; + enable_clash_fields?: boolean; + enable_builtin_enhanced?: boolean; + proxy_layout_column?: number; + clash_tray_selector?: boolean; + clash_strategy?: { + external_controller_port_strategy: "fixed" | "random" | "allow_fallback"; + }; +} diff --git a/clash-nyanpasu/frontend/interface/tsconfig.json b/clash-nyanpasu/frontend/interface/tsconfig.json new file mode 100644 index 0000000000..c83485f348 --- /dev/null +++ b/clash-nyanpasu/frontend/interface/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "target": "ESNext", + "useDefineForClassFields": true, + "lib": ["DOM", "DOM.Iterable", "ESNext"], + "allowJs": false, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "module": "ESNext", + "moduleResolution": "Node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "preserve", + "paths": { + "@/*": ["./*"], + }, + }, + "include": ["./"], +} diff --git a/clash-nyanpasu/frontend/nyanpasu/package.json b/clash-nyanpasu/frontend/nyanpasu/package.json index 9f6523b477..ed31c5dcc3 100644 --- a/clash-nyanpasu/frontend/nyanpasu/package.json +++ b/clash-nyanpasu/frontend/nyanpasu/package.json @@ -14,12 +14,13 @@ "@dnd-kit/utilities": "3.2.2", "@emotion/react": "11.11.4", "@emotion/styled": "11.11.5", - "@generouted/react-router": "1.18.8", + "@generouted/react-router": "1.19.2", "@juggle/resize-observer": "3.4.0", "@mui/icons-material": "5.15.15", "@mui/lab": "5.0.0-alpha.170", "@mui/material": "5.15.15", - "@mui/x-data-grid": "7.1.1", + "@mui/x-data-grid": "7.2.0", + "@nyanpasu/interface": "workspace:^", "@tauri-apps/api": "1.5.3", "ahooks": "3.7.11", "axios": "1.6.8", @@ -42,13 +43,13 @@ }, "devDependencies": { "@types/js-cookie": "3.0.6", - "@types/react": "18.2.77", + "@types/react": "18.2.78", "@types/react-dom": "18.2.25", "@types/react-transition-group": "4.4.10", "@typescript-eslint/eslint-plugin": "7.6.0", "@typescript-eslint/parser": "7.6.0", "@vitejs/plugin-react": "4.2.1", - "sass": "1.74.1", + "sass": "1.75.0", "shiki": "1.3.0", "vite": "5.2.8", "vite-plugin-monaco-editor": "1.1.0", diff --git a/clash-nyanpasu/frontend/nyanpasu/src/router.ts b/clash-nyanpasu/frontend/nyanpasu/src/router.ts index 56f4dff4a6..d5e7461ebb 100644 --- a/clash-nyanpasu/frontend/nyanpasu/src/router.ts +++ b/clash-nyanpasu/frontend/nyanpasu/src/router.ts @@ -1,7 +1,7 @@ // Generouted, changes to this file will be overriden /* eslint-disable */ -import { components, hooks, utils } from "@generouted/react-router/client"; +import { components, hooks, utils } from '@generouted/react-router/client' export type Path = | `/connections` @@ -10,16 +10,14 @@ export type Path = | `/providers` | `/proxies` | `/rules` - | `/settings`; + | `/settings` -export type Params = {}; +export type Params = { + +} -export type ModalPath = never; +export type ModalPath = never -export const { Link, Navigate } = components(); -export const { useModals, useNavigate, useParams } = hooks< - Path, - Params, - ModalPath ->(); -export const { redirect } = utils(); +export const { Link, Navigate } = components() +export const { useModals, useNavigate, useParams } = hooks() +export const { redirect } = utils() diff --git a/clash-nyanpasu/manifest/version.json b/clash-nyanpasu/manifest/version.json index 499659ffbb..50ef505de5 100644 --- a/clash-nyanpasu/manifest/version.json +++ b/clash-nyanpasu/manifest/version.json @@ -2,8 +2,8 @@ "manifest_version": 1, "latest": { "mihomo": "v1.18.3", - "mihomo_alpha": "alpha-e3b69b8", - "clash_rs": "v0.1.15", + "mihomo_alpha": "alpha-6f0fe85", + "clash_rs": "v0.1.16", "clash_premium": "2023-09-05-gdcc8d87" }, "arch_template": { @@ -36,5 +36,5 @@ "darwin-x64": "clash-darwin-amd64-n{}.gz" } }, - "updated_at": "2024-04-11T22:19:31.146Z" + "updated_at": "2024-04-12T22:18:27.892Z" } diff --git a/clash-nyanpasu/package.json b/clash-nyanpasu/package.json index 52845dd9f6..45539b6445 100644 --- a/clash-nyanpasu/package.json +++ b/clash-nyanpasu/package.json @@ -5,7 +5,7 @@ "type": "module", "repository": "https://github.com/LibNyanpasu/clash-nyanpasu.git", "engines": { - "node": ">=20.0.0" + "node": "21.7.3" }, "scripts": { "dev": "tauri dev -c ./backend/tauri/tauri.conf.json", diff --git a/clash-nyanpasu/pnpm-lock.yaml b/clash-nyanpasu/pnpm-lock.yaml index 985780d6e7..e684de9880 100644 --- a/clash-nyanpasu/pnpm-lock.yaml +++ b/clash-nyanpasu/pnpm-lock.yaml @@ -130,6 +130,15 @@ importers: specifier: 5.4.5 version: 5.4.5 + frontend/interface: + dependencies: + '@tauri-apps/api': + specifier: 1.5.3 + version: 1.5.3 + swr: + specifier: 2.2.5 + version: 2.2.5(react@18.2.0) + frontend/nyanpasu: dependencies: '@dnd-kit/core': @@ -143,28 +152,31 @@ importers: version: 3.2.2(react@18.2.0) '@emotion/react': specifier: 11.11.4 - version: 11.11.4(@types/react@18.2.77)(react@18.2.0) + version: 11.11.4(@types/react@18.2.78)(react@18.2.0) '@emotion/styled': specifier: 11.11.5 - version: 11.11.5(@emotion/react@11.11.4)(@types/react@18.2.77)(react@18.2.0) + version: 11.11.5(@emotion/react@11.11.4)(@types/react@18.2.78)(react@18.2.0) '@generouted/react-router': - specifier: 1.18.8 - version: 1.18.8(react-router-dom@6.22.3)(react@18.2.0)(vite@5.2.8) + specifier: 1.19.2 + version: 1.19.2(react-router-dom@6.22.3)(react@18.2.0)(vite@5.2.8) '@juggle/resize-observer': specifier: 3.4.0 version: 3.4.0 '@mui/icons-material': specifier: 5.15.15 - version: 5.15.15(@mui/material@5.15.15)(@types/react@18.2.77)(react@18.2.0) + version: 5.15.15(@mui/material@5.15.15)(@types/react@18.2.78)(react@18.2.0) '@mui/lab': specifier: 5.0.0-alpha.170 - version: 5.0.0-alpha.170(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(@mui/material@5.15.15)(@types/react@18.2.77)(react-dom@18.2.0)(react@18.2.0) + version: 5.0.0-alpha.170(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(@mui/material@5.15.15)(@types/react@18.2.78)(react-dom@18.2.0)(react@18.2.0) '@mui/material': specifier: 5.15.15 - version: 5.15.15(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(@types/react@18.2.77)(react-dom@18.2.0)(react@18.2.0) + version: 5.15.15(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(@types/react@18.2.78)(react-dom@18.2.0)(react@18.2.0) '@mui/x-data-grid': - specifier: 7.1.1 - version: 7.1.1(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(@mui/material@5.15.15)(@types/react@18.2.77)(react-dom@18.2.0)(react@18.2.0) + specifier: 7.2.0 + version: 7.2.0(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(@mui/material@5.15.15)(@types/react@18.2.78)(react-dom@18.2.0)(react@18.2.0) + '@nyanpasu/interface': + specifier: workspace:^ + version: link:../interface '@tauri-apps/api': specifier: 1.5.3 version: 1.5.3 @@ -188,7 +200,7 @@ importers: version: 0.47.0 mui-color-input: specifier: 2.0.3 - version: 2.0.3(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(@mui/material@5.15.15)(@types/react@18.2.77)(react-dom@18.2.0)(react@18.2.0) + version: 2.0.3(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(@mui/material@5.15.15)(@types/react@18.2.78)(react-dom@18.2.0)(react@18.2.0) react: specifier: 18.2.0 version: 18.2.0 @@ -206,7 +218,7 @@ importers: version: 14.1.0(i18next@23.11.1)(react-dom@18.2.0)(react@18.2.0) react-markdown: specifier: 9.0.1 - version: 9.0.1(@types/react@18.2.77)(react@18.2.0) + version: 9.0.1(@types/react@18.2.78)(react@18.2.0) react-router-dom: specifier: 6.22.3 version: 6.22.3(react-dom@18.2.0)(react@18.2.0) @@ -227,8 +239,8 @@ importers: specifier: 3.0.6 version: 3.0.6 '@types/react': - specifier: 18.2.77 - version: 18.2.77 + specifier: 18.2.78 + version: 18.2.78 '@types/react-dom': specifier: 18.2.25 version: 18.2.25 @@ -245,20 +257,20 @@ importers: specifier: 4.2.1 version: 4.2.1(vite@5.2.8) sass: - specifier: 1.74.1 - version: 1.74.1 + specifier: 1.75.0 + version: 1.75.0 shiki: specifier: 1.3.0 version: 1.3.0 vite: specifier: 5.2.8 - version: 5.2.8(@types/node@20.12.7)(sass@1.74.1) + version: 5.2.8(@types/node@20.12.7)(sass@1.75.0) vite-plugin-monaco-editor: specifier: npm:vite-plugin-monaco-editor-new@1.1.3 version: /vite-plugin-monaco-editor-new@1.1.3(monaco-editor@0.47.0) vite-plugin-sass-dts: specifier: 1.3.17 - version: 1.3.17(postcss@8.4.38)(prettier@3.2.5)(sass@1.74.1)(vite@5.2.8) + version: 1.3.17(postcss@8.4.38)(prettier@3.2.5)(sass@1.75.0)(vite@5.2.8) vite-plugin-svgr: specifier: 4.2.0 version: 4.2.0(typescript@5.4.5)(vite@5.2.8) @@ -839,7 +851,7 @@ packages: resolution: {integrity: sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==} dev: false - /@emotion/react@11.11.4(@types/react@18.2.77)(react@18.2.0): + /@emotion/react@11.11.4(@types/react@18.2.78)(react@18.2.0): resolution: {integrity: sha512-t8AjMlF0gHpvvxk5mAtCqR4vmxiGHCeJBaQO6gncUSdklELOgtwjerNY2yuJNfwnc6vi16U/+uMF+afIawJ9iw==} peerDependencies: '@types/react': '*' @@ -855,7 +867,7 @@ packages: '@emotion/use-insertion-effect-with-fallbacks': 1.0.1(react@18.2.0) '@emotion/utils': 1.2.1 '@emotion/weak-memoize': 0.3.1 - '@types/react': 18.2.77 + '@types/react': 18.2.78 hoist-non-react-statics: 3.3.2 react: 18.2.0 dev: false @@ -874,7 +886,7 @@ packages: resolution: {integrity: sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==} dev: false - /@emotion/styled@11.11.5(@emotion/react@11.11.4)(@types/react@18.2.77)(react@18.2.0): + /@emotion/styled@11.11.5(@emotion/react@11.11.4)(@types/react@18.2.78)(react@18.2.0): resolution: {integrity: sha512-/ZjjnaNKvuMPxcIiUkf/9SHoG4Q196DRl1w82hQ3WCsjo1IUR8uaGWrC6a87CrYAW0Kb/pK7hk8BnLgLRi9KoQ==} peerDependencies: '@emotion/react': ^11.0.0-rc.0 @@ -887,11 +899,11 @@ packages: '@babel/runtime': 7.24.1 '@emotion/babel-plugin': 11.11.0 '@emotion/is-prop-valid': 1.2.2 - '@emotion/react': 11.11.4(@types/react@18.2.77)(react@18.2.0) + '@emotion/react': 11.11.4(@types/react@18.2.78)(react@18.2.0) '@emotion/serialize': 1.1.4 '@emotion/use-insertion-effect-with-fallbacks': 1.0.1(react@18.2.0) '@emotion/utils': 1.2.1 - '@types/react': 18.2.77 + '@types/react': 18.2.78 react: 18.2.0 dev: false @@ -1376,18 +1388,18 @@ packages: resolution: {integrity: sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==} dev: false - /@generouted/react-router@1.18.8(react-router-dom@6.22.3)(react@18.2.0)(vite@5.2.8): - resolution: {integrity: sha512-LN1mIF1Akj7t3mvTeDlBZCa8a0wFPooLHDKam9YcG/UcgOEMTn+Y63tV5AGdcM33xtOYeE+pTX+ud+GR3yNe0g==} + /@generouted/react-router@1.19.2(react-router-dom@6.22.3)(react@18.2.0)(vite@5.2.8): + resolution: {integrity: sha512-glHSqsrXZ7t524PgmQOD+fuxFVIuFOyAsYkPI+lbuskGm7Us/FknrkuxiuS4b5wPpHapDnOCChqLdumwvRxBng==} peerDependencies: react: '>=18' react-router-dom: '>=6' vite: '>=4' dependencies: fast-glob: 3.3.2 - generouted: 1.18.8(vite@5.2.8) + generouted: 1.19.2(vite@5.2.8) react: 18.2.0 react-router-dom: 6.22.3(react-dom@18.2.0)(react@18.2.0) - vite: 5.2.8(@types/node@20.12.7)(sass@1.74.1) + vite: 5.2.8(@types/node@20.12.7)(sass@1.75.0) dev: false /@humanwhocodes/config-array@0.11.14: @@ -1456,7 +1468,7 @@ packages: resolution: {integrity: sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==} dev: false - /@mui/base@5.0.0-beta.40(@types/react@18.2.77)(react-dom@18.2.0)(react@18.2.0): + /@mui/base@5.0.0-beta.40(@types/react@18.2.78)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-I/lGHztkCzvwlXpjD2+SNmvNQvB4227xBXhISPjEaJUXGImOQ9f3D2Yj/T3KasSI/h0MLWy74X0J6clhPmsRbQ==} engines: {node: '>=12.0.0'} peerDependencies: @@ -1469,10 +1481,10 @@ packages: dependencies: '@babel/runtime': 7.24.1 '@floating-ui/react-dom': 2.0.8(react-dom@18.2.0)(react@18.2.0) - '@mui/types': 7.2.14(@types/react@18.2.77) - '@mui/utils': 5.15.14(@types/react@18.2.77)(react@18.2.0) + '@mui/types': 7.2.14(@types/react@18.2.78) + '@mui/utils': 5.15.14(@types/react@18.2.78)(react@18.2.0) '@popperjs/core': 2.11.8 - '@types/react': 18.2.77 + '@types/react': 18.2.78 clsx: 2.1.0 prop-types: 15.8.1 react: 18.2.0 @@ -1483,7 +1495,7 @@ packages: resolution: {integrity: sha512-aXnw29OWQ6I5A47iuWEI6qSSUfH6G/aCsW9KmW3LiFqr7uXZBK4Ks+z8G+qeIub8k0T5CMqlT2q0L+ZJTMrqpg==} dev: false - /@mui/icons-material@5.15.15(@mui/material@5.15.15)(@types/react@18.2.77)(react@18.2.0): + /@mui/icons-material@5.15.15(@mui/material@5.15.15)(@types/react@18.2.78)(react@18.2.0): resolution: {integrity: sha512-kkeU/pe+hABcYDH6Uqy8RmIsr2S/y5bP2rp+Gat4CcRjCcVne6KudS1NrZQhUCRysrTDCAhcbcf9gt+/+pGO2g==} engines: {node: '>=12.0.0'} peerDependencies: @@ -1495,12 +1507,12 @@ packages: optional: true dependencies: '@babel/runtime': 7.24.1 - '@mui/material': 5.15.15(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(@types/react@18.2.77)(react-dom@18.2.0)(react@18.2.0) - '@types/react': 18.2.77 + '@mui/material': 5.15.15(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(@types/react@18.2.78)(react-dom@18.2.0)(react@18.2.0) + '@types/react': 18.2.78 react: 18.2.0 dev: false - /@mui/lab@5.0.0-alpha.170(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(@mui/material@5.15.15)(@types/react@18.2.77)(react-dom@18.2.0)(react@18.2.0): + /@mui/lab@5.0.0-alpha.170(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(@mui/material@5.15.15)(@types/react@18.2.78)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-0bDVECGmrNjd3+bLdcLiwYZ0O4HP5j5WSQm5DV6iA/Z9kr8O6AnvZ1bv9ImQbbX7Gj3pX4o43EKwCutj3EQxQg==} engines: {node: '>=12.0.0'} peerDependencies: @@ -1519,21 +1531,21 @@ packages: optional: true dependencies: '@babel/runtime': 7.24.1 - '@emotion/react': 11.11.4(@types/react@18.2.77)(react@18.2.0) - '@emotion/styled': 11.11.5(@emotion/react@11.11.4)(@types/react@18.2.77)(react@18.2.0) - '@mui/base': 5.0.0-beta.40(@types/react@18.2.77)(react-dom@18.2.0)(react@18.2.0) - '@mui/material': 5.15.15(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(@types/react@18.2.77)(react-dom@18.2.0)(react@18.2.0) - '@mui/system': 5.15.15(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(@types/react@18.2.77)(react@18.2.0) - '@mui/types': 7.2.14(@types/react@18.2.77) - '@mui/utils': 5.15.14(@types/react@18.2.77)(react@18.2.0) - '@types/react': 18.2.77 + '@emotion/react': 11.11.4(@types/react@18.2.78)(react@18.2.0) + '@emotion/styled': 11.11.5(@emotion/react@11.11.4)(@types/react@18.2.78)(react@18.2.0) + '@mui/base': 5.0.0-beta.40(@types/react@18.2.78)(react-dom@18.2.0)(react@18.2.0) + '@mui/material': 5.15.15(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(@types/react@18.2.78)(react-dom@18.2.0)(react@18.2.0) + '@mui/system': 5.15.15(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(@types/react@18.2.78)(react@18.2.0) + '@mui/types': 7.2.14(@types/react@18.2.78) + '@mui/utils': 5.15.14(@types/react@18.2.78)(react@18.2.0) + '@types/react': 18.2.78 clsx: 2.1.0 prop-types: 15.8.1 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@mui/material@5.15.15(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(@types/react@18.2.77)(react-dom@18.2.0)(react@18.2.0): + /@mui/material@5.15.15(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(@types/react@18.2.78)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-3zvWayJ+E1kzoIsvwyEvkTUKVKt1AjchFFns+JtluHCuvxgKcLSRJTADw37k0doaRtVAsyh8bz9Afqzv+KYrIA==} engines: {node: '>=12.0.0'} peerDependencies: @@ -1551,14 +1563,14 @@ packages: optional: true dependencies: '@babel/runtime': 7.24.1 - '@emotion/react': 11.11.4(@types/react@18.2.77)(react@18.2.0) - '@emotion/styled': 11.11.5(@emotion/react@11.11.4)(@types/react@18.2.77)(react@18.2.0) - '@mui/base': 5.0.0-beta.40(@types/react@18.2.77)(react-dom@18.2.0)(react@18.2.0) + '@emotion/react': 11.11.4(@types/react@18.2.78)(react@18.2.0) + '@emotion/styled': 11.11.5(@emotion/react@11.11.4)(@types/react@18.2.78)(react@18.2.0) + '@mui/base': 5.0.0-beta.40(@types/react@18.2.78)(react-dom@18.2.0)(react@18.2.0) '@mui/core-downloads-tracker': 5.15.15 - '@mui/system': 5.15.15(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(@types/react@18.2.77)(react@18.2.0) - '@mui/types': 7.2.14(@types/react@18.2.77) - '@mui/utils': 5.15.14(@types/react@18.2.77)(react@18.2.0) - '@types/react': 18.2.77 + '@mui/system': 5.15.15(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(@types/react@18.2.78)(react@18.2.0) + '@mui/types': 7.2.14(@types/react@18.2.78) + '@mui/utils': 5.15.14(@types/react@18.2.78)(react@18.2.0) + '@types/react': 18.2.78 '@types/react-transition-group': 4.4.10 clsx: 2.1.0 csstype: 3.1.3 @@ -1569,7 +1581,7 @@ packages: react-transition-group: 4.4.5(react-dom@18.2.0)(react@18.2.0) dev: false - /@mui/private-theming@5.15.14(@types/react@18.2.77)(react@18.2.0): + /@mui/private-theming@5.15.14(@types/react@18.2.78)(react@18.2.0): resolution: {integrity: sha512-UH0EiZckOWcxiXLX3Jbb0K7rC8mxTr9L9l6QhOZxYc4r8FHUkefltV9VDGLrzCaWh30SQiJvAEd7djX3XXY6Xw==} engines: {node: '>=12.0.0'} peerDependencies: @@ -1580,8 +1592,8 @@ packages: optional: true dependencies: '@babel/runtime': 7.24.1 - '@mui/utils': 5.15.14(@types/react@18.2.77)(react@18.2.0) - '@types/react': 18.2.77 + '@mui/utils': 5.15.14(@types/react@18.2.78)(react@18.2.0) + '@types/react': 18.2.78 prop-types: 15.8.1 react: 18.2.0 dev: false @@ -1601,14 +1613,14 @@ packages: dependencies: '@babel/runtime': 7.24.1 '@emotion/cache': 11.11.0 - '@emotion/react': 11.11.4(@types/react@18.2.77)(react@18.2.0) - '@emotion/styled': 11.11.5(@emotion/react@11.11.4)(@types/react@18.2.77)(react@18.2.0) + '@emotion/react': 11.11.4(@types/react@18.2.78)(react@18.2.0) + '@emotion/styled': 11.11.5(@emotion/react@11.11.4)(@types/react@18.2.78)(react@18.2.0) csstype: 3.1.3 prop-types: 15.8.1 react: 18.2.0 dev: false - /@mui/system@5.15.15(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(@types/react@18.2.77)(react@18.2.0): + /@mui/system@5.15.15(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(@types/react@18.2.78)(react@18.2.0): resolution: {integrity: sha512-aulox6N1dnu5PABsfxVGOZffDVmlxPOVgj56HrUnJE8MCSh8lOvvkd47cebIVQQYAjpwieXQXiDPj5pwM40jTQ==} engines: {node: '>=12.0.0'} peerDependencies: @@ -1625,20 +1637,20 @@ packages: optional: true dependencies: '@babel/runtime': 7.24.1 - '@emotion/react': 11.11.4(@types/react@18.2.77)(react@18.2.0) - '@emotion/styled': 11.11.5(@emotion/react@11.11.4)(@types/react@18.2.77)(react@18.2.0) - '@mui/private-theming': 5.15.14(@types/react@18.2.77)(react@18.2.0) + '@emotion/react': 11.11.4(@types/react@18.2.78)(react@18.2.0) + '@emotion/styled': 11.11.5(@emotion/react@11.11.4)(@types/react@18.2.78)(react@18.2.0) + '@mui/private-theming': 5.15.14(@types/react@18.2.78)(react@18.2.0) '@mui/styled-engine': 5.15.14(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.2.0) - '@mui/types': 7.2.14(@types/react@18.2.77) - '@mui/utils': 5.15.14(@types/react@18.2.77)(react@18.2.0) - '@types/react': 18.2.77 + '@mui/types': 7.2.14(@types/react@18.2.78) + '@mui/utils': 5.15.14(@types/react@18.2.78)(react@18.2.0) + '@types/react': 18.2.78 clsx: 2.1.0 csstype: 3.1.3 prop-types: 15.8.1 react: 18.2.0 dev: false - /@mui/types@7.2.14(@types/react@18.2.77): + /@mui/types@7.2.14(@types/react@18.2.78): resolution: {integrity: sha512-MZsBZ4q4HfzBsywtXgM1Ksj6HDThtiwmOKUXH1pKYISI9gAVXCNHNpo7TlGoGrBaYWZTdNoirIN7JsQcQUjmQQ==} peerDependencies: '@types/react': ^17.0.0 || ^18.0.0 @@ -1646,10 +1658,10 @@ packages: '@types/react': optional: true dependencies: - '@types/react': 18.2.77 + '@types/react': 18.2.78 dev: false - /@mui/utils@5.15.14(@types/react@18.2.77)(react@18.2.0): + /@mui/utils@5.15.14(@types/react@18.2.78)(react@18.2.0): resolution: {integrity: sha512-0lF/7Hh/ezDv5X7Pry6enMsbYyGKjADzvHyo3Qrc/SSlTsQ1VkbDMbH0m2t3OR5iIVLwMoxwM7yGd+6FCMtTFA==} engines: {node: '>=12.0.0'} peerDependencies: @@ -1661,14 +1673,14 @@ packages: dependencies: '@babel/runtime': 7.24.1 '@types/prop-types': 15.7.11 - '@types/react': 18.2.77 + '@types/react': 18.2.78 prop-types: 15.8.1 react: 18.2.0 react-is: 18.2.0 dev: false - /@mui/x-data-grid@7.1.1(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(@mui/material@5.15.15)(@types/react@18.2.77)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-hNvz927lkAznFdy45QPE7mIZVyQhlqveHmTK9+SD0N1us4sSTij90uUJ/roTNDod0VA9f5GqWmNz+5h8ihpz6Q==} + /@mui/x-data-grid@7.2.0(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(@mui/material@5.15.15)(@types/react@18.2.78)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-WKmFo0eKhj3W7Fv8u5n2XP4UcdzuJ+mEYALiMUDAYsah/hPBH9mA1miXn9DjXF3i3dxgzrTjdJemTgTJxAQZKg==} engines: {node: '>=14.0.0'} peerDependencies: '@mui/material': ^5.15.14 @@ -1676,9 +1688,9 @@ packages: react-dom: ^17.0.0 || ^18.0.0 dependencies: '@babel/runtime': 7.24.1 - '@mui/material': 5.15.15(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(@types/react@18.2.77)(react-dom@18.2.0)(react@18.2.0) - '@mui/system': 5.15.15(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(@types/react@18.2.77)(react@18.2.0) - '@mui/utils': 5.15.14(@types/react@18.2.77)(react@18.2.0) + '@mui/material': 5.15.15(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(@types/react@18.2.78)(react-dom@18.2.0)(react@18.2.0) + '@mui/system': 5.15.15(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(@types/react@18.2.78)(react@18.2.0) + '@mui/utils': 5.15.14(@types/react@18.2.78)(react@18.2.0) clsx: 2.1.0 prop-types: 15.8.1 react: 18.2.0 @@ -2303,16 +2315,16 @@ packages: /@types/react-dom@18.2.25: resolution: {integrity: sha512-o/V48vf4MQh7juIKZU2QGDfli6p1+OOi5oXx36Hffpc9adsHeXjVp8rHuPkjd8VT8sOJ2Zp05HR7CdpGTIUFUA==} dependencies: - '@types/react': 18.2.77 + '@types/react': 18.2.78 dev: true /@types/react-transition-group@4.4.10: resolution: {integrity: sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==} dependencies: - '@types/react': 18.2.77 + '@types/react': 18.2.78 - /@types/react@18.2.77: - resolution: {integrity: sha512-CUT9KUUF+HytDM7WiXKLF9qUSg4tGImwy4FXTlfEDPEkkNUzJ7rVFolYweJ9fS1ljoIaP7M7Rdjc5eUm/Yu5AA==} + /@types/react@18.2.78: + resolution: {integrity: sha512-qOwdPnnitQY4xKlKayt42q5W5UQrSHjgoXNVEtxeqdITJ99k4VXJOP3vt8Rkm9HmgJpH50UNU+rlqfkfWOqp0A==} dependencies: '@types/prop-types': 15.7.11 csstype: 3.1.3 @@ -2475,7 +2487,7 @@ packages: '@babel/plugin-transform-react-jsx-source': 7.23.3(@babel/core@7.23.6) '@types/babel__core': 7.20.5 react-refresh: 0.14.0 - vite: 5.2.8(@types/node@20.12.7)(sass@1.74.1) + vite: 5.2.8(@types/node@20.12.7)(sass@1.75.0) transitivePeerDependencies: - supports-color dev: true @@ -4145,12 +4157,12 @@ packages: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} dev: true - /generouted@1.18.8(vite@5.2.8): - resolution: {integrity: sha512-iaKHLtR/ziZEJT+0n0+6RMTRFSkPsnF7GvnDvM5xYOjKvFjFZO/u5HRQpKIlBhNC0IGu6JSdHlcu9EJcLOQXvA==} + /generouted@1.19.2(vite@5.2.8): + resolution: {integrity: sha512-kldS/yjNkjytPlQ1m44Gm98NCV1gWmgKJXM+pdtcIEp+x7V2JJTKs86N7jAMH0omnBe9GrhVj5OqtuN8x+VjZA==} peerDependencies: vite: '>=3' dependencies: - vite: 5.2.8(@types/node@20.12.7)(sass@1.74.1) + vite: 5.2.8(@types/node@20.12.7)(sass@1.75.0) dev: false /gensync@1.0.0-beta.2: @@ -5461,7 +5473,7 @@ packages: /ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} - /mui-color-input@2.0.3(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(@mui/material@5.15.15)(@types/react@18.2.77)(react-dom@18.2.0)(react@18.2.0): + /mui-color-input@2.0.3(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(@mui/material@5.15.15)(@types/react@18.2.78)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-rAd040qQ0Y+8dk4gE8kkCiJ/vCgA0j4vv1quJ43BfORTFE3uHarHj0xY1Vo9CPbojtx1f5vW+CjckYPRIZPIRg==} peerDependencies: '@emotion/react': ^11.5.0 @@ -5475,10 +5487,10 @@ packages: optional: true dependencies: '@ctrl/tinycolor': 4.0.3 - '@emotion/react': 11.11.4(@types/react@18.2.77)(react@18.2.0) - '@emotion/styled': 11.11.5(@emotion/react@11.11.4)(@types/react@18.2.77)(react@18.2.0) - '@mui/material': 5.15.15(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(@types/react@18.2.77)(react-dom@18.2.0)(react@18.2.0) - '@types/react': 18.2.77 + '@emotion/react': 11.11.4(@types/react@18.2.78)(react@18.2.0) + '@emotion/styled': 11.11.5(@emotion/react@11.11.4)(@types/react@18.2.78)(react@18.2.0) + '@mui/material': 5.15.15(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(@types/react@18.2.78)(react-dom@18.2.0)(react@18.2.0) + '@types/react': 18.2.78 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false @@ -6007,14 +6019,14 @@ packages: resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} dev: false - /react-markdown@9.0.1(@types/react@18.2.77)(react@18.2.0): + /react-markdown@9.0.1(@types/react@18.2.78)(react@18.2.0): resolution: {integrity: sha512-186Gw/vF1uRkydbsOIkcGXw7aHq0sZOCRFFjGrr7b9+nVZg4UfA4enXCaxm4fUzecU38sWfrNDitGhshuU7rdg==} peerDependencies: '@types/react': '>=18' react: '>=18' dependencies: '@types/hast': 3.0.4 - '@types/react': 18.2.77 + '@types/react': 18.2.78 devlop: 1.1.0 hast-util-to-jsx-runtime: 2.3.0 html-url-attributes: 3.0.0 @@ -6320,8 +6332,8 @@ packages: engines: {node: '>= 0.10'} dev: false - /sass@1.74.1: - resolution: {integrity: sha512-w0Z9p/rWZWelb88ISOLyvqTWGmtmu2QJICqDBGyNnfG4OUnPX9BBjjYIXUpXCMOOg5MQWNpqzt876la1fsTvUA==} + /sass@1.75.0: + resolution: {integrity: sha512-ShMYi3WkrDWxExyxSZPst4/okE9ts46xZmJDSawJQrnte7M1V9fScVB+uNXOVKRBt0PggHOwoZcn8mYX4trnBw==} engines: {node: '>=14.0.0'} hasBin: true dependencies: @@ -7091,7 +7103,7 @@ packages: monaco-editor: 0.47.0 dev: true - /vite-plugin-sass-dts@1.3.17(postcss@8.4.38)(prettier@3.2.5)(sass@1.74.1)(vite@5.2.8): + /vite-plugin-sass-dts@1.3.17(postcss@8.4.38)(prettier@3.2.5)(sass@1.75.0)(vite@5.2.8): resolution: {integrity: sha512-1YOEaDblFafFUhqOWdCBkJaJjBA7XPcX+Y8pFKFbn4BuNO5M57snN2A4w1DkA8pF6gL/QSvSpQfswHBDDC2fjQ==} engines: {node: '>=18'} peerDependencies: @@ -7103,8 +7115,8 @@ packages: postcss: 8.4.38 postcss-js: 4.0.1(postcss@8.4.38) prettier: 3.2.5 - sass: 1.74.1 - vite: 5.2.8(@types/node@20.12.7)(sass@1.74.1) + sass: 1.75.0 + vite: 5.2.8(@types/node@20.12.7)(sass@1.75.0) dev: true /vite-plugin-svgr@4.2.0(typescript@5.4.5)(vite@5.2.8): @@ -7115,7 +7127,7 @@ packages: '@rollup/pluginutils': 5.0.5 '@svgr/core': 8.1.0(typescript@5.4.5) '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0) - vite: 5.2.8(@types/node@20.12.7)(sass@1.74.1) + vite: 5.2.8(@types/node@20.12.7)(sass@1.75.0) transitivePeerDependencies: - rollup - supports-color @@ -7133,13 +7145,13 @@ packages: debug: 4.3.4 globrex: 0.1.2 tsconfck: 3.0.3(typescript@5.4.5) - vite: 5.2.8(@types/node@20.12.7)(sass@1.74.1) + vite: 5.2.8(@types/node@20.12.7)(sass@1.75.0) transitivePeerDependencies: - supports-color - typescript dev: true - /vite@5.2.8(@types/node@20.12.7)(sass@1.74.1): + /vite@5.2.8(@types/node@20.12.7)(sass@1.75.0): resolution: {integrity: sha512-OyZR+c1CE8yeHw5V5t59aXsUPPVTHMDjEZz8MgguLL/Q7NblxhZUlTu9xSPqlsUO/y+X7dlU05jdhvyycD55DA==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true @@ -7171,7 +7183,7 @@ packages: esbuild: 0.20.2 postcss: 8.4.38 rollup: 4.13.0 - sass: 1.74.1 + sass: 1.75.0 optionalDependencies: fsevents: 2.3.3 diff --git a/clash-verge-rev/src-tauri/src/core/service.rs b/clash-verge-rev/src-tauri/src/core/service.rs index af529ea197..226b751ace 100644 --- a/clash-verge-rev/src-tauri/src/core/service.rs +++ b/clash-verge-rev/src-tauri/src/core/service.rs @@ -193,7 +193,7 @@ pub async fn uninstall_service() -> Result<()> { bail!("uninstaller not found"); } - let shell = uninstaller_path.to_string_lossy(); + let shell = uninstaller_path.to_string_lossy().replace(" ", "\\\\ "); let command = format!(r#"do shell script "{shell}" with administrator privileges"#); let status = StdCommand::new("osascript") diff --git a/clash-verge-rev/src-tauri/src/core/sysopt.rs b/clash-verge-rev/src-tauri/src/core/sysopt.rs index cb3e2dbe42..513f3b081c 100644 --- a/clash-verge-rev/src-tauri/src/core/sysopt.rs +++ b/clash-verge-rev/src-tauri/src/core/sysopt.rs @@ -3,9 +3,10 @@ use anyhow::{anyhow, Result}; use auto_launch::{AutoLaunch, AutoLaunchBuilder}; use once_cell::sync::OnceCell; use parking_lot::Mutex; +use std::env::current_exe; use std::sync::Arc; use sysproxy::Sysproxy; -use tauri::{async_runtime::Mutex as TokioMutex, utils::platform::current_exe}; +use tauri::async_runtime::Mutex as TokioMutex; pub struct Sysopt { /// current system proxy setting @@ -149,7 +150,7 @@ impl Sysopt { /// init the auto launch pub fn init_launch(&self) -> Result<()> { let app_exe = current_exe()?; - let app_exe = dunce::canonicalize(app_exe)?; + // let app_exe = dunce::canonicalize(app_exe)?; let app_name = app_exe .file_stem() .and_then(|f| f.to_str()) diff --git a/echo/pkg/sub/clash.go b/echo/pkg/sub/clash.go index 088171b186..e875203470 100644 --- a/echo/pkg/sub/clash.go +++ b/echo/pkg/sub/clash.go @@ -115,15 +115,38 @@ func (c *ClashSub) Refresh() error { // a new free port will be used as relay listen port for each group func (c *ClashSub) ToRelayConfigs(listenHost string) ([]*relay_cfg.Config, error) { relayConfigs := []*relay_cfg.Config{} + portSet := map[int]struct{}{} + + const minPort = 10000 + const maxPort = 65535 + nextPort := minPort + getNextAvailablePort := func() int { + for ; nextPort <= maxPort; nextPort++ { + if _, occupied := portSet[nextPort]; !occupied { + portSet[nextPort] = struct{}{} + defer func() { nextPort++ }() + return nextPort + } + } + return -1 + } + // generate relay config for each proxy for _, proxy := range *c.cCfg.Proxies { + proxyPort, err := strconv.Atoi(proxy.Port) + if err != nil { + return nil, err + } + if _, ok := portSet[proxyPort]; ok { + proxyPort = getNextAvailablePort() + } var newName string if strings.HasSuffix(proxy.Name, "-") { newName = fmt.Sprintf("%s%s", proxy.Name, c.Name) } else { newName = fmt.Sprintf("%s-%s", proxy.Name, c.Name) } - rc, err := proxy.ToRelayConfig(listenHost, proxy.Port, newName) + rc, err := proxy.ToRelayConfig(listenHost, strconv.Itoa(proxyPort), newName) if err != nil { return nil, err } @@ -133,7 +156,6 @@ func (c *ClashSub) ToRelayConfigs(listenHost string) ([]*relay_cfg.Config, error // generate relay config for each group groupProxy := c.cCfg.groupByLongestCommonPrefix() for groupName, proxies := range groupProxy { - // only use first proxy will be show in proxy provider, other will be merged into load balance in relay groupLeader := proxies[0].getOrCreateGroupLeader() var newName string if strings.HasSuffix(groupName, "-") { @@ -141,28 +163,17 @@ func (c *ClashSub) ToRelayConfigs(listenHost string) ([]*relay_cfg.Config, error } else { newName = fmt.Sprintf("%s-lb", groupName) } - - // group listen port is the max port in group + 1 - port := 0 - for _, proxy := range proxies { - pp, err := strconv.Atoi(proxy.Port) - if err != nil { - return nil, err - } - if pp > port { - port = pp - } + port := getNextAvailablePort() + if port == -1 { + return nil, fmt.Errorf("no available port") } - port++ rc, err := groupLeader.ToRelayConfig(listenHost, strconv.Itoa(port), newName) if err != nil { return nil, err } - // add other proxies address in group to relay config for _, proxy := range proxies[1:] { remote := net.JoinHostPort(proxy.rawServer, proxy.rawPort) - // skip duplicate remote, because the relay cfg for this leader will be cached when first init if strInArray(remote, rc.TCPRemotes) { continue } diff --git a/hysteria/app/cmd/client.go b/hysteria/app/cmd/client.go index c1d04bd786..b6745abf4d 100644 --- a/hysteria/app/cmd/client.go +++ b/hysteria/app/cmd/client.go @@ -21,7 +21,9 @@ import ( "github.com/apernet/hysteria/app/internal/forwarding" "github.com/apernet/hysteria/app/internal/http" + "github.com/apernet/hysteria/app/internal/proxymux" "github.com/apernet/hysteria/app/internal/redirect" + "github.com/apernet/hysteria/app/internal/sockopts" "github.com/apernet/hysteria/app/internal/socks5" "github.com/apernet/hysteria/app/internal/tproxy" "github.com/apernet/hysteria/app/internal/tun" @@ -99,13 +101,20 @@ type clientConfigTLS struct { } type clientConfigQUIC struct { - InitStreamReceiveWindow uint64 `mapstructure:"initStreamReceiveWindow"` - MaxStreamReceiveWindow uint64 `mapstructure:"maxStreamReceiveWindow"` - InitConnectionReceiveWindow uint64 `mapstructure:"initConnReceiveWindow"` - MaxConnectionReceiveWindow uint64 `mapstructure:"maxConnReceiveWindow"` - MaxIdleTimeout time.Duration `mapstructure:"maxIdleTimeout"` - KeepAlivePeriod time.Duration `mapstructure:"keepAlivePeriod"` - DisablePathMTUDiscovery bool `mapstructure:"disablePathMTUDiscovery"` + InitStreamReceiveWindow uint64 `mapstructure:"initStreamReceiveWindow"` + MaxStreamReceiveWindow uint64 `mapstructure:"maxStreamReceiveWindow"` + InitConnectionReceiveWindow uint64 `mapstructure:"initConnReceiveWindow"` + MaxConnectionReceiveWindow uint64 `mapstructure:"maxConnReceiveWindow"` + MaxIdleTimeout time.Duration `mapstructure:"maxIdleTimeout"` + KeepAlivePeriod time.Duration `mapstructure:"keepAlivePeriod"` + DisablePathMTUDiscovery bool `mapstructure:"disablePathMTUDiscovery"` + Sockopts clientConfigQUICSockopts `mapstructure:"sockopts"` +} + +type clientConfigQUICSockopts struct { + BindInterface *string `mapstructure:"bindInterface"` + FirewallMark *uint32 `mapstructure:"fwmark"` + FdControlUnixSocket *string `mapstructure:"fdControlUnixSocket"` } type clientConfigBandwidth struct { @@ -195,6 +204,21 @@ func (c *clientConfig) fillServerAddr(hyConfig *client.Config) error { // fillConnFactory must be called after fillServerAddr, as we have different logic // for ConnFactory depending on whether we have a port hopping address. func (c *clientConfig) fillConnFactory(hyConfig *client.Config) error { + so := &sockopts.SocketOptions{ + BindInterface: c.QUIC.Sockopts.BindInterface, + FirewallMark: c.QUIC.Sockopts.FirewallMark, + FdControlUnixSocket: c.QUIC.Sockopts.FdControlUnixSocket, + } + if err := so.CheckSupported(); err != nil { + var unsupportedErr *sockopts.UnsupportedError + if errors.As(err, &unsupportedErr) { + return configError{ + Field: "quic.sockopts." + unsupportedErr.Field, + Err: errors.New("unsupported on this platform"), + } + } + return configError{Field: "quic.sockopts", Err: err} + } // Inner PacketConn var newFunc func(addr net.Addr) (net.PacketConn, error) switch strings.ToLower(c.Transport.Type) { @@ -202,11 +226,11 @@ func (c *clientConfig) fillConnFactory(hyConfig *client.Config) error { if hyConfig.ServerAddr.Network() == "udphop" { hopAddr := hyConfig.ServerAddr.(*udphop.UDPHopAddr) newFunc = func(addr net.Addr) (net.PacketConn, error) { - return udphop.NewUDPHopPacketConn(hopAddr, c.Transport.UDP.HopInterval, nil) + return udphop.NewUDPHopPacketConn(hopAddr, c.Transport.UDP.HopInterval, so.ListenUDP) } } else { newFunc = func(addr net.Addr) (net.PacketConn, error) { - return net.ListenUDP("udp", nil) + return so.ListenUDP() } } default: @@ -531,7 +555,7 @@ func clientSOCKS5(config socks5Config, c client.Client) error { if config.Listen == "" { return configError{Field: "listen", Err: errors.New("listen address is empty")} } - l, err := correctnet.Listen("tcp", config.Listen) + l, err := proxymux.ListenSOCKS(config.Listen) if err != nil { return configError{Field: "listen", Err: err} } @@ -556,7 +580,7 @@ func clientHTTP(config httpConfig, c client.Client) error { if config.Listen == "" { return configError{Field: "listen", Err: errors.New("listen address is empty")} } - l, err := correctnet.Listen("tcp", config.Listen) + l, err := proxymux.ListenHTTP(config.Listen) if err != nil { return configError{Field: "listen", Err: err} } diff --git a/hysteria/app/cmd/client_test.go b/hysteria/app/cmd/client_test.go index c586949e9e..10b2d9911e 100644 --- a/hysteria/app/cmd/client_test.go +++ b/hysteria/app/cmd/client_test.go @@ -46,6 +46,11 @@ func TestClientConfig(t *testing.T) { MaxIdleTimeout: 10 * time.Second, KeepAlivePeriod: 4 * time.Second, DisablePathMTUDiscovery: true, + Sockopts: clientConfigQUICSockopts{ + BindInterface: stringRef("eth0"), + FirewallMark: uint32Ref(1234), + FdControlUnixSocket: stringRef("test.sock"), + }, }, Bandwidth: clientConfigBandwidth{ Up: "200 mbps", @@ -189,3 +194,11 @@ func TestClientConfigURI(t *testing.T) { }) } } + +func stringRef(s string) *string { + return &s +} + +func uint32Ref(i uint32) *uint32 { + return &i +} diff --git a/hysteria/app/cmd/client_test.yaml b/hysteria/app/cmd/client_test.yaml index 4f919df0b9..e8438f6c4c 100644 --- a/hysteria/app/cmd/client_test.yaml +++ b/hysteria/app/cmd/client_test.yaml @@ -26,6 +26,10 @@ quic: maxIdleTimeout: 10s keepAlivePeriod: 4s disablePathMTUDiscovery: true + sockopts: + bindInterface: eth0 + fwmark: 1234 + fdControlUnixSocket: test.sock bandwidth: up: 200 mbps @@ -75,7 +79,7 @@ tun: ipv6: 2001::ffff:ffff:ffff:fff1/126 route: strict: true - ipv4: [0.0.0.0/0] - ipv6: ["2000::/3"] - ipv4Exclude: [192.0.2.1/32] - ipv6Exclude: ["2001:db8::1/128"] + ipv4: [ 0.0.0.0/0 ] + ipv6: [ "2000::/3" ] + ipv4Exclude: [ 192.0.2.1/32 ] + ipv6Exclude: [ "2001:db8::1/128" ] diff --git a/hysteria/app/go.mod b/hysteria/app/go.mod index a5025c1de3..97c5bbb4c6 100644 --- a/hysteria/app/go.mod +++ b/hysteria/app/go.mod @@ -16,6 +16,8 @@ require ( github.com/stretchr/testify v1.8.4 github.com/txthinking/socks5 v0.0.0-20230325130024-4230056ae301 go.uber.org/zap v1.24.0 + golang.org/x/exp v0.0.0-20221205204356-47842c84f3db + golang.org/x/sys v0.17.0 ) require ( @@ -54,10 +56,8 @@ require ( go.uber.org/multierr v1.11.0 // indirect go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect golang.org/x/crypto v0.19.0 // indirect - golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect golang.org/x/mod v0.12.0 // indirect golang.org/x/net v0.21.0 // indirect - golang.org/x/sys v0.17.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/tools v0.11.1 // indirect google.golang.org/protobuf v1.33.0 // indirect diff --git a/hysteria/app/internal/proxymux/.mockery.yaml b/hysteria/app/internal/proxymux/.mockery.yaml new file mode 100644 index 0000000000..7d3fac08a5 --- /dev/null +++ b/hysteria/app/internal/proxymux/.mockery.yaml @@ -0,0 +1,12 @@ +with-expecter: true +dir: internal/mocks +outpkg: mocks +packages: + net: + interfaces: + Listener: + config: + mockname: MockListener + Conn: + config: + mockname: MockConn diff --git a/hysteria/app/internal/proxymux/internal/mocks/mock_Conn.go b/hysteria/app/internal/proxymux/internal/mocks/mock_Conn.go new file mode 100644 index 0000000000..0bcbe657d7 --- /dev/null +++ b/hysteria/app/internal/proxymux/internal/mocks/mock_Conn.go @@ -0,0 +1,427 @@ +// Code generated by mockery v2.42.2. DO NOT EDIT. + +package mocks + +import ( + net "net" + + mock "github.com/stretchr/testify/mock" + + time "time" +) + +// MockConn is an autogenerated mock type for the Conn type +type MockConn struct { + mock.Mock +} + +type MockConn_Expecter struct { + mock *mock.Mock +} + +func (_m *MockConn) EXPECT() *MockConn_Expecter { + return &MockConn_Expecter{mock: &_m.Mock} +} + +// Close provides a mock function with given fields: +func (_m *MockConn) Close() error { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Close") + } + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockConn_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close' +type MockConn_Close_Call struct { + *mock.Call +} + +// Close is a helper method to define mock.On call +func (_e *MockConn_Expecter) Close() *MockConn_Close_Call { + return &MockConn_Close_Call{Call: _e.mock.On("Close")} +} + +func (_c *MockConn_Close_Call) Run(run func()) *MockConn_Close_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MockConn_Close_Call) Return(_a0 error) *MockConn_Close_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockConn_Close_Call) RunAndReturn(run func() error) *MockConn_Close_Call { + _c.Call.Return(run) + return _c +} + +// LocalAddr provides a mock function with given fields: +func (_m *MockConn) LocalAddr() net.Addr { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for LocalAddr") + } + + var r0 net.Addr + if rf, ok := ret.Get(0).(func() net.Addr); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(net.Addr) + } + } + + return r0 +} + +// MockConn_LocalAddr_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LocalAddr' +type MockConn_LocalAddr_Call struct { + *mock.Call +} + +// LocalAddr is a helper method to define mock.On call +func (_e *MockConn_Expecter) LocalAddr() *MockConn_LocalAddr_Call { + return &MockConn_LocalAddr_Call{Call: _e.mock.On("LocalAddr")} +} + +func (_c *MockConn_LocalAddr_Call) Run(run func()) *MockConn_LocalAddr_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MockConn_LocalAddr_Call) Return(_a0 net.Addr) *MockConn_LocalAddr_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockConn_LocalAddr_Call) RunAndReturn(run func() net.Addr) *MockConn_LocalAddr_Call { + _c.Call.Return(run) + return _c +} + +// Read provides a mock function with given fields: b +func (_m *MockConn) Read(b []byte) (int, error) { + ret := _m.Called(b) + + if len(ret) == 0 { + panic("no return value specified for Read") + } + + var r0 int + var r1 error + if rf, ok := ret.Get(0).(func([]byte) (int, error)); ok { + return rf(b) + } + if rf, ok := ret.Get(0).(func([]byte) int); ok { + r0 = rf(b) + } else { + r0 = ret.Get(0).(int) + } + + if rf, ok := ret.Get(1).(func([]byte) error); ok { + r1 = rf(b) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockConn_Read_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Read' +type MockConn_Read_Call struct { + *mock.Call +} + +// Read is a helper method to define mock.On call +// - b []byte +func (_e *MockConn_Expecter) Read(b interface{}) *MockConn_Read_Call { + return &MockConn_Read_Call{Call: _e.mock.On("Read", b)} +} + +func (_c *MockConn_Read_Call) Run(run func(b []byte)) *MockConn_Read_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].([]byte)) + }) + return _c +} + +func (_c *MockConn_Read_Call) Return(n int, err error) *MockConn_Read_Call { + _c.Call.Return(n, err) + return _c +} + +func (_c *MockConn_Read_Call) RunAndReturn(run func([]byte) (int, error)) *MockConn_Read_Call { + _c.Call.Return(run) + return _c +} + +// RemoteAddr provides a mock function with given fields: +func (_m *MockConn) RemoteAddr() net.Addr { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for RemoteAddr") + } + + var r0 net.Addr + if rf, ok := ret.Get(0).(func() net.Addr); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(net.Addr) + } + } + + return r0 +} + +// MockConn_RemoteAddr_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RemoteAddr' +type MockConn_RemoteAddr_Call struct { + *mock.Call +} + +// RemoteAddr is a helper method to define mock.On call +func (_e *MockConn_Expecter) RemoteAddr() *MockConn_RemoteAddr_Call { + return &MockConn_RemoteAddr_Call{Call: _e.mock.On("RemoteAddr")} +} + +func (_c *MockConn_RemoteAddr_Call) Run(run func()) *MockConn_RemoteAddr_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MockConn_RemoteAddr_Call) Return(_a0 net.Addr) *MockConn_RemoteAddr_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockConn_RemoteAddr_Call) RunAndReturn(run func() net.Addr) *MockConn_RemoteAddr_Call { + _c.Call.Return(run) + return _c +} + +// SetDeadline provides a mock function with given fields: t +func (_m *MockConn) SetDeadline(t time.Time) error { + ret := _m.Called(t) + + if len(ret) == 0 { + panic("no return value specified for SetDeadline") + } + + var r0 error + if rf, ok := ret.Get(0).(func(time.Time) error); ok { + r0 = rf(t) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockConn_SetDeadline_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetDeadline' +type MockConn_SetDeadline_Call struct { + *mock.Call +} + +// SetDeadline is a helper method to define mock.On call +// - t time.Time +func (_e *MockConn_Expecter) SetDeadline(t interface{}) *MockConn_SetDeadline_Call { + return &MockConn_SetDeadline_Call{Call: _e.mock.On("SetDeadline", t)} +} + +func (_c *MockConn_SetDeadline_Call) Run(run func(t time.Time)) *MockConn_SetDeadline_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(time.Time)) + }) + return _c +} + +func (_c *MockConn_SetDeadline_Call) Return(_a0 error) *MockConn_SetDeadline_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockConn_SetDeadline_Call) RunAndReturn(run func(time.Time) error) *MockConn_SetDeadline_Call { + _c.Call.Return(run) + return _c +} + +// SetReadDeadline provides a mock function with given fields: t +func (_m *MockConn) SetReadDeadline(t time.Time) error { + ret := _m.Called(t) + + if len(ret) == 0 { + panic("no return value specified for SetReadDeadline") + } + + var r0 error + if rf, ok := ret.Get(0).(func(time.Time) error); ok { + r0 = rf(t) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockConn_SetReadDeadline_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetReadDeadline' +type MockConn_SetReadDeadline_Call struct { + *mock.Call +} + +// SetReadDeadline is a helper method to define mock.On call +// - t time.Time +func (_e *MockConn_Expecter) SetReadDeadline(t interface{}) *MockConn_SetReadDeadline_Call { + return &MockConn_SetReadDeadline_Call{Call: _e.mock.On("SetReadDeadline", t)} +} + +func (_c *MockConn_SetReadDeadline_Call) Run(run func(t time.Time)) *MockConn_SetReadDeadline_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(time.Time)) + }) + return _c +} + +func (_c *MockConn_SetReadDeadline_Call) Return(_a0 error) *MockConn_SetReadDeadline_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockConn_SetReadDeadline_Call) RunAndReturn(run func(time.Time) error) *MockConn_SetReadDeadline_Call { + _c.Call.Return(run) + return _c +} + +// SetWriteDeadline provides a mock function with given fields: t +func (_m *MockConn) SetWriteDeadline(t time.Time) error { + ret := _m.Called(t) + + if len(ret) == 0 { + panic("no return value specified for SetWriteDeadline") + } + + var r0 error + if rf, ok := ret.Get(0).(func(time.Time) error); ok { + r0 = rf(t) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockConn_SetWriteDeadline_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetWriteDeadline' +type MockConn_SetWriteDeadline_Call struct { + *mock.Call +} + +// SetWriteDeadline is a helper method to define mock.On call +// - t time.Time +func (_e *MockConn_Expecter) SetWriteDeadline(t interface{}) *MockConn_SetWriteDeadline_Call { + return &MockConn_SetWriteDeadline_Call{Call: _e.mock.On("SetWriteDeadline", t)} +} + +func (_c *MockConn_SetWriteDeadline_Call) Run(run func(t time.Time)) *MockConn_SetWriteDeadline_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(time.Time)) + }) + return _c +} + +func (_c *MockConn_SetWriteDeadline_Call) Return(_a0 error) *MockConn_SetWriteDeadline_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockConn_SetWriteDeadline_Call) RunAndReturn(run func(time.Time) error) *MockConn_SetWriteDeadline_Call { + _c.Call.Return(run) + return _c +} + +// Write provides a mock function with given fields: b +func (_m *MockConn) Write(b []byte) (int, error) { + ret := _m.Called(b) + + if len(ret) == 0 { + panic("no return value specified for Write") + } + + var r0 int + var r1 error + if rf, ok := ret.Get(0).(func([]byte) (int, error)); ok { + return rf(b) + } + if rf, ok := ret.Get(0).(func([]byte) int); ok { + r0 = rf(b) + } else { + r0 = ret.Get(0).(int) + } + + if rf, ok := ret.Get(1).(func([]byte) error); ok { + r1 = rf(b) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockConn_Write_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Write' +type MockConn_Write_Call struct { + *mock.Call +} + +// Write is a helper method to define mock.On call +// - b []byte +func (_e *MockConn_Expecter) Write(b interface{}) *MockConn_Write_Call { + return &MockConn_Write_Call{Call: _e.mock.On("Write", b)} +} + +func (_c *MockConn_Write_Call) Run(run func(b []byte)) *MockConn_Write_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].([]byte)) + }) + return _c +} + +func (_c *MockConn_Write_Call) Return(n int, err error) *MockConn_Write_Call { + _c.Call.Return(n, err) + return _c +} + +func (_c *MockConn_Write_Call) RunAndReturn(run func([]byte) (int, error)) *MockConn_Write_Call { + _c.Call.Return(run) + return _c +} + +// NewMockConn creates a new instance of MockConn. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewMockConn(t interface { + mock.TestingT + Cleanup(func()) +}) *MockConn { + mock := &MockConn{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/hysteria/app/internal/proxymux/internal/mocks/mock_Listener.go b/hysteria/app/internal/proxymux/internal/mocks/mock_Listener.go new file mode 100644 index 0000000000..e4ca2f46cd --- /dev/null +++ b/hysteria/app/internal/proxymux/internal/mocks/mock_Listener.go @@ -0,0 +1,185 @@ +// Code generated by mockery v2.42.2. DO NOT EDIT. + +package mocks + +import ( + net "net" + + mock "github.com/stretchr/testify/mock" +) + +// MockListener is an autogenerated mock type for the Listener type +type MockListener struct { + mock.Mock +} + +type MockListener_Expecter struct { + mock *mock.Mock +} + +func (_m *MockListener) EXPECT() *MockListener_Expecter { + return &MockListener_Expecter{mock: &_m.Mock} +} + +// Accept provides a mock function with given fields: +func (_m *MockListener) Accept() (net.Conn, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Accept") + } + + var r0 net.Conn + var r1 error + if rf, ok := ret.Get(0).(func() (net.Conn, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() net.Conn); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(net.Conn) + } + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockListener_Accept_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Accept' +type MockListener_Accept_Call struct { + *mock.Call +} + +// Accept is a helper method to define mock.On call +func (_e *MockListener_Expecter) Accept() *MockListener_Accept_Call { + return &MockListener_Accept_Call{Call: _e.mock.On("Accept")} +} + +func (_c *MockListener_Accept_Call) Run(run func()) *MockListener_Accept_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MockListener_Accept_Call) Return(_a0 net.Conn, _a1 error) *MockListener_Accept_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockListener_Accept_Call) RunAndReturn(run func() (net.Conn, error)) *MockListener_Accept_Call { + _c.Call.Return(run) + return _c +} + +// Addr provides a mock function with given fields: +func (_m *MockListener) Addr() net.Addr { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Addr") + } + + var r0 net.Addr + if rf, ok := ret.Get(0).(func() net.Addr); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(net.Addr) + } + } + + return r0 +} + +// MockListener_Addr_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Addr' +type MockListener_Addr_Call struct { + *mock.Call +} + +// Addr is a helper method to define mock.On call +func (_e *MockListener_Expecter) Addr() *MockListener_Addr_Call { + return &MockListener_Addr_Call{Call: _e.mock.On("Addr")} +} + +func (_c *MockListener_Addr_Call) Run(run func()) *MockListener_Addr_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MockListener_Addr_Call) Return(_a0 net.Addr) *MockListener_Addr_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockListener_Addr_Call) RunAndReturn(run func() net.Addr) *MockListener_Addr_Call { + _c.Call.Return(run) + return _c +} + +// Close provides a mock function with given fields: +func (_m *MockListener) Close() error { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Close") + } + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockListener_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close' +type MockListener_Close_Call struct { + *mock.Call +} + +// Close is a helper method to define mock.On call +func (_e *MockListener_Expecter) Close() *MockListener_Close_Call { + return &MockListener_Close_Call{Call: _e.mock.On("Close")} +} + +func (_c *MockListener_Close_Call) Run(run func()) *MockListener_Close_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MockListener_Close_Call) Return(_a0 error) *MockListener_Close_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockListener_Close_Call) RunAndReturn(run func() error) *MockListener_Close_Call { + _c.Call.Return(run) + return _c +} + +// NewMockListener creates a new instance of MockListener. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewMockListener(t interface { + mock.TestingT + Cleanup(func()) +}) *MockListener { + mock := &MockListener{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/hysteria/app/internal/proxymux/manager.go b/hysteria/app/internal/proxymux/manager.go new file mode 100644 index 0000000000..92df918ba0 --- /dev/null +++ b/hysteria/app/internal/proxymux/manager.go @@ -0,0 +1,72 @@ +package proxymux + +import ( + "net" + "sync" + + "github.com/apernet/hysteria/extras/correctnet" +) + +type muxManager struct { + listeners map[string]*muxListener + lock sync.Mutex +} + +var globalMuxManager *muxManager + +func init() { + globalMuxManager = &muxManager{ + listeners: make(map[string]*muxListener), + } +} + +func (m *muxManager) GetOrCreate(address string) (*muxListener, error) { + key, err := m.canonicalizeAddrPort(address) + if err != nil { + return nil, err + } + + m.lock.Lock() + defer m.lock.Unlock() + + if ml, ok := m.listeners[key]; ok { + return ml, nil + } + + listener, err := correctnet.Listen("tcp", key) + if err != nil { + return nil, err + } + + ml := newMuxListener(listener, func() { + m.lock.Lock() + defer m.lock.Unlock() + delete(m.listeners, key) + }) + m.listeners[key] = ml + return ml, nil +} + +func (m *muxManager) canonicalizeAddrPort(address string) (string, error) { + taddr, err := net.ResolveTCPAddr("tcp", address) + if err != nil { + return "", err + } + return taddr.String(), nil +} + +func ListenHTTP(address string) (net.Listener, error) { + ml, err := globalMuxManager.GetOrCreate(address) + if err != nil { + return nil, err + } + return ml.ListenHTTP() +} + +func ListenSOCKS(address string) (net.Listener, error) { + ml, err := globalMuxManager.GetOrCreate(address) + if err != nil { + return nil, err + } + return ml.ListenSOCKS() +} diff --git a/hysteria/app/internal/proxymux/manager_test.go b/hysteria/app/internal/proxymux/manager_test.go new file mode 100644 index 0000000000..c776058f1e --- /dev/null +++ b/hysteria/app/internal/proxymux/manager_test.go @@ -0,0 +1,110 @@ +package proxymux + +import ( + "net" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestListenSOCKS(t *testing.T) { + address := "127.2.39.129:11081" + + sl, err := ListenSOCKS(address) + if !assert.NoError(t, err) { + return + } + defer func() { + sl.Close() + }() + + hl, err := ListenHTTP(address) + if !assert.NoError(t, err) { + return + } + defer hl.Close() + + _, err = ListenSOCKS(address) + if !assert.ErrorIs(t, err, ErrProtocolInUse) { + return + } + sl.Close() + + sl, err = ListenSOCKS(address) + if !assert.NoError(t, err) { + return + } +} + +func TestListenHTTP(t *testing.T) { + address := "127.2.39.129:11082" + + hl, err := ListenHTTP(address) + if !assert.NoError(t, err) { + return + } + defer func() { + hl.Close() + }() + + sl, err := ListenSOCKS(address) + if !assert.NoError(t, err) { + return + } + defer sl.Close() + + _, err = ListenHTTP(address) + if !assert.ErrorIs(t, err, ErrProtocolInUse) { + return + } + hl.Close() + + hl, err = ListenHTTP(address) + if !assert.NoError(t, err) { + return + } +} + +func TestRelease(t *testing.T) { + address := "127.2.39.129:11083" + + hl, err := ListenHTTP(address) + if !assert.NoError(t, err) { + return + } + sl, err := ListenSOCKS(address) + if !assert.NoError(t, err) { + return + } + + if !assert.True(t, globalMuxManager.testAddressExists(address)) { + return + } + _, err = net.Listen("tcp", address) + if !assert.Error(t, err) { + return + } + + hl.Close() + sl.Close() + + // Wait for muxListener released + time.Sleep(time.Second) + if !assert.False(t, globalMuxManager.testAddressExists(address)) { + return + } + lis, err := net.Listen("tcp", address) + if !assert.NoError(t, err) { + return + } + defer lis.Close() +} + +func (m *muxManager) testAddressExists(address string) bool { + m.lock.Lock() + defer m.lock.Unlock() + + _, ok := m.listeners[address] + return ok +} diff --git a/hysteria/app/internal/proxymux/mux.go b/hysteria/app/internal/proxymux/mux.go new file mode 100644 index 0000000000..1f0b7b09dd --- /dev/null +++ b/hysteria/app/internal/proxymux/mux.go @@ -0,0 +1,320 @@ +package proxymux + +import ( + "errors" + "fmt" + "io" + "net" + "sync" +) + +func newMuxListener(listener net.Listener, deleteFunc func()) *muxListener { + l := &muxListener{ + base: listener, + acceptChan: make(chan net.Conn), + closeChan: make(chan struct{}), + deleteFunc: deleteFunc, + } + go l.acceptLoop() + go l.mainLoop() + return l +} + +type muxListener struct { + lock sync.Mutex + base net.Listener + acceptErr error + + acceptChan chan net.Conn + closeChan chan struct{} + + socksListener *subListener + httpListener *subListener + + deleteFunc func() +} + +func (l *muxListener) acceptLoop() { + defer close(l.acceptChan) + + for { + conn, err := l.base.Accept() + if err != nil { + l.lock.Lock() + l.acceptErr = err + l.lock.Unlock() + return + } + select { + case <-l.closeChan: + return + case l.acceptChan <- conn: + } + } +} + +func (l *muxListener) mainLoop() { + defer func() { + l.deleteFunc() + l.base.Close() + + close(l.closeChan) + + l.lock.Lock() + defer l.lock.Unlock() + + if sl := l.httpListener; sl != nil { + close(sl.acceptChan) + l.httpListener = nil + } + if sl := l.socksListener; sl != nil { + close(sl.acceptChan) + l.socksListener = nil + } + }() + + for { + var socksCloseChan, httpCloseChan chan struct{} + if l.httpListener != nil { + httpCloseChan = l.httpListener.closeChan + } + if l.socksListener != nil { + socksCloseChan = l.socksListener.closeChan + } + select { + case <-l.closeChan: + return + case conn, ok := <-l.acceptChan: + if !ok { + return + } + go l.dispatch(conn) + case <-socksCloseChan: + l.lock.Lock() + if socksCloseChan == l.socksListener.closeChan { + // not replaced by another ListenSOCKS() + l.socksListener = nil + } + l.lock.Unlock() + if l.checkIdle() { + return + } + case <-httpCloseChan: + l.lock.Lock() + if httpCloseChan == l.httpListener.closeChan { + // not replaced by another ListenHTTP() + l.httpListener = nil + } + l.lock.Unlock() + if l.checkIdle() { + return + } + } + } +} + +func (l *muxListener) dispatch(conn net.Conn) { + var b [1]byte + if _, err := io.ReadFull(conn, b[:]); err != nil { + conn.Close() + return + } + + l.lock.Lock() + var target *subListener + if b[0] == 5 { + target = l.socksListener + } else { + target = l.httpListener + } + l.lock.Unlock() + + if target == nil { + conn.Close() + return + } + + wconn := &connWithOneByte{Conn: conn, b: b[0]} + + select { + case <-target.closeChan: + case target.acceptChan <- wconn: + } +} + +func (l *muxListener) checkIdle() bool { + l.lock.Lock() + defer l.lock.Unlock() + + return l.httpListener == nil && l.socksListener == nil +} + +func (l *muxListener) getAndClearAcceptError() error { + l.lock.Lock() + defer l.lock.Unlock() + + if l.acceptErr == nil { + return nil + } + err := l.acceptErr + l.acceptErr = nil + return err +} + +func (l *muxListener) ListenHTTP() (net.Listener, error) { + l.lock.Lock() + defer l.lock.Unlock() + + if l.httpListener != nil { + subListenerPendingClosed := false + select { + case <-l.httpListener.closeChan: + subListenerPendingClosed = true + default: + } + if !subListenerPendingClosed { + return nil, OpErr{ + Addr: l.base.Addr(), + Protocol: "http", + Op: "bind-protocol", + Err: ErrProtocolInUse, + } + } + l.httpListener = nil + } + + select { + case <-l.closeChan: + return nil, net.ErrClosed + default: + } + + sl := newSubListener(l.getAndClearAcceptError, l.base.Addr) + l.httpListener = sl + return sl, nil +} + +func (l *muxListener) ListenSOCKS() (net.Listener, error) { + l.lock.Lock() + defer l.lock.Unlock() + + if l.socksListener != nil { + subListenerPendingClosed := false + select { + case <-l.socksListener.closeChan: + subListenerPendingClosed = true + default: + } + if !subListenerPendingClosed { + return nil, OpErr{ + Addr: l.base.Addr(), + Protocol: "socks", + Op: "bind-protocol", + Err: ErrProtocolInUse, + } + } + l.socksListener = nil + } + + select { + case <-l.closeChan: + return nil, net.ErrClosed + default: + } + + sl := newSubListener(l.getAndClearAcceptError, l.base.Addr) + l.socksListener = sl + return sl, nil +} + +func newSubListener(acceptErrorFunc func() error, addrFunc func() net.Addr) *subListener { + return &subListener{ + acceptChan: make(chan net.Conn), + acceptErrorFunc: acceptErrorFunc, + closeChan: make(chan struct{}), + addrFunc: addrFunc, + } +} + +type subListener struct { + // receive connections or closure from upstream + acceptChan chan net.Conn + // get an error of Accept() from upstream + acceptErrorFunc func() error + // notify upstream that we are closed + closeChan chan struct{} + + // Listener.Addr() implementation of base listener + addrFunc func() net.Addr +} + +func (l *subListener) Accept() (net.Conn, error) { + select { + case <-l.closeChan: + // closed by ourselves + return nil, net.ErrClosed + case conn, ok := <-l.acceptChan: + if !ok { + // closed by upstream + if acceptErr := l.acceptErrorFunc(); acceptErr != nil { + return nil, acceptErr + } + return nil, net.ErrClosed + } + return conn, nil + } +} + +func (l *subListener) Addr() net.Addr { + return l.addrFunc() +} + +// Close implements net.Listener.Close. +// Upstream should use close(l.acceptChan) instead. +func (l *subListener) Close() error { + select { + case <-l.closeChan: + return nil + default: + } + close(l.closeChan) + return nil +} + +// connWithOneByte is a net.Conn that returns b for the first read +// request, then forwards everything else to Conn. +type connWithOneByte struct { + net.Conn + + b byte + bRead bool +} + +func (c *connWithOneByte) Read(bs []byte) (int, error) { + if c.bRead { + return c.Conn.Read(bs) + } + if len(bs) == 0 { + return 0, nil + } + c.bRead = true + bs[0] = c.b + return 1, nil +} + +type OpErr struct { + Addr net.Addr + Protocol string + Op string + Err error +} + +func (m OpErr) Error() string { + return fmt.Sprintf("mux-listen: %s[%s]: %s: %v", m.Addr, m.Protocol, m.Op, m.Err) +} + +func (m OpErr) Unwrap() error { + return m.Err +} + +var ErrProtocolInUse = errors.New("protocol already in use") diff --git a/hysteria/app/internal/proxymux/mux_test.go b/hysteria/app/internal/proxymux/mux_test.go new file mode 100644 index 0000000000..46cbf95c9f --- /dev/null +++ b/hysteria/app/internal/proxymux/mux_test.go @@ -0,0 +1,154 @@ +package proxymux + +import ( + "bytes" + "net" + "sync" + "testing" + "time" + + "github.com/apernet/hysteria/app/internal/proxymux/internal/mocks" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +//go:generate mockery + +func testMockListener(t *testing.T, connChan <-chan net.Conn) net.Listener { + closedChan := make(chan struct{}) + mockListener := mocks.NewMockListener(t) + mockListener.EXPECT().Accept().RunAndReturn(func() (net.Conn, error) { + select { + case <-closedChan: + return nil, net.ErrClosed + case conn, ok := <-connChan: + if !ok { + panic("unexpected closed channel (connChan)") + } + return conn, nil + } + }) + mockListener.EXPECT().Close().RunAndReturn(func() error { + select { + case <-closedChan: + default: + close(closedChan) + } + return nil + }) + return mockListener +} + +func testMockConn(t *testing.T, b []byte) net.Conn { + buf := bytes.NewReader(b) + isClosed := false + mockConn := mocks.NewMockConn(t) + mockConn.EXPECT().Read(mock.Anything).RunAndReturn(func(b []byte) (int, error) { + if isClosed { + return 0, net.ErrClosed + } + return buf.Read(b) + }) + mockConn.EXPECT().Close().RunAndReturn(func() error { + isClosed = true + return nil + }) + return mockConn +} + +func TestMuxHTTP(t *testing.T) { + connChan := make(chan net.Conn) + mockListener := testMockListener(t, connChan) + mockConn := testMockConn(t, []byte("CONNECT example.com:443 HTTP/1.1\r\n\r\n")) + + mux := newMuxListener(mockListener, func() {}) + hl, err := mux.ListenHTTP() + if !assert.NoError(t, err) { + return + } + sl, err := mux.ListenSOCKS() + if !assert.NoError(t, err) { + return + } + + connChan <- mockConn + + var socksConn, httpConn net.Conn + var socksErr, httpErr error + + var wg sync.WaitGroup + wg.Add(2) + go func() { + socksConn, socksErr = sl.Accept() + wg.Done() + }() + go func() { + httpConn, httpErr = hl.Accept() + wg.Done() + }() + + time.Sleep(time.Second) + + sl.Close() + hl.Close() + + wg.Wait() + + assert.Nil(t, socksConn) + assert.ErrorIs(t, socksErr, net.ErrClosed) + assert.NotNil(t, httpConn) + httpConn.Close() + assert.NoError(t, httpErr) + + // Wait for muxListener released + <-mux.acceptChan +} + +func TestMuxSOCKS(t *testing.T) { + connChan := make(chan net.Conn) + mockListener := testMockListener(t, connChan) + mockConn := testMockConn(t, []byte{0x05, 0x02, 0x00, 0x01}) // SOCKS5 Connect Request: NOAUTH+GSSAPI + + mux := newMuxListener(mockListener, func() {}) + hl, err := mux.ListenHTTP() + if !assert.NoError(t, err) { + return + } + sl, err := mux.ListenSOCKS() + if !assert.NoError(t, err) { + return + } + + connChan <- mockConn + + var socksConn, httpConn net.Conn + var socksErr, httpErr error + + var wg sync.WaitGroup + wg.Add(2) + go func() { + socksConn, socksErr = sl.Accept() + wg.Done() + }() + go func() { + httpConn, httpErr = hl.Accept() + wg.Done() + }() + + time.Sleep(time.Second) + + sl.Close() + hl.Close() + + wg.Wait() + + assert.NotNil(t, socksConn) + socksConn.Close() + assert.NoError(t, socksErr) + assert.Nil(t, httpConn) + assert.ErrorIs(t, httpErr, net.ErrClosed) + + // Wait for muxListener released + <-mux.acceptChan +} diff --git a/hysteria/app/internal/sockopts/fd_control_unix_socket_test.py b/hysteria/app/internal/sockopts/fd_control_unix_socket_test.py new file mode 100644 index 0000000000..e47a6f64a1 --- /dev/null +++ b/hysteria/app/internal/sockopts/fd_control_unix_socket_test.py @@ -0,0 +1,65 @@ +import socket +import array +import os +import struct +import sys + + +def serve(path): + try: + os.unlink(path) + except OSError: + if os.path.exists(path): + raise + + server = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + server.bind(path) + server.listen() + print(f"Listening on {path}") + + try: + while True: + connection, client_address = server.accept() + print(f"Client connected") + + try: + # Receiving fd from client + fds = array.array("i") + msg, ancdata, flags, addr = connection.recvmsg(1, socket.CMSG_LEN(struct.calcsize('i'))) + for cmsg_level, cmsg_type, cmsg_data in ancdata: + if cmsg_level == socket.SOL_SOCKET and cmsg_type == socket.SCM_RIGHTS: + fds.frombytes(cmsg_data[:len(cmsg_data) - (len(cmsg_data) % fds.itemsize)]) + + fd = fds[0] + + # We make a call to setsockopt(2) here, so client can verify we have received the fd + # In the real scenario, the server would set things like SO_MARK, + # we use SO_RCVBUF as it doesn't require any special capabilities. + nbytes = struct.pack("i", 2500) + fdsocket = fd_to_socket(fd) + fdsocket.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, nbytes) + fdsocket.close() + + # The only protocol-like thing specified in the client implementation. + connection.send(b'\x01') + finally: + connection.close() + print("Connection closed") + + except KeyboardInterrupt: + print("Exit") + + finally: + server.close() + os.unlink(path) + + +def fd_to_socket(fd): + return socket.fromfd(fd, socket.AF_UNIX, socket.SOCK_STREAM) + + +if __name__ == "__main__": + if len(sys.argv) < 2: + raise ValueError("unix socket path is required") + + serve(sys.argv[1]) diff --git a/hysteria/app/internal/sockopts/sockopts.go b/hysteria/app/internal/sockopts/sockopts.go new file mode 100644 index 0000000000..14ee0c019f --- /dev/null +++ b/hysteria/app/internal/sockopts/sockopts.go @@ -0,0 +1,76 @@ +package sockopts + +import ( + "fmt" + "net" +) + +type SocketOptions struct { + BindInterface *string + FirewallMark *uint32 + FdControlUnixSocket *string +} + +// implemented in platform-specific files +var ( + bindInterfaceFunc func(c *net.UDPConn, device string) error + firewallMarkFunc func(c *net.UDPConn, fwmark uint32) error + fdControlUnixSocketFunc func(c *net.UDPConn, path string) error +) + +func (o *SocketOptions) CheckSupported() (err error) { + if o.BindInterface != nil && bindInterfaceFunc == nil { + return &UnsupportedError{"bindInterface"} + } + if o.FirewallMark != nil && firewallMarkFunc == nil { + return &UnsupportedError{"fwmark"} + } + if o.FdControlUnixSocket != nil && fdControlUnixSocketFunc == nil { + return &UnsupportedError{"fdControlUnixSocket"} + } + return nil +} + +type UnsupportedError struct { + Field string +} + +func (e *UnsupportedError) Error() string { + return fmt.Sprintf("%s is not supported on this platform", e.Field) +} + +func (o *SocketOptions) ListenUDP() (uconn net.PacketConn, err error) { + uconn, err = net.ListenUDP("udp", nil) + if err != nil { + return + } + err = o.applyToUDPConn(uconn.(*net.UDPConn)) + if err != nil { + uconn.Close() + uconn = nil + return + } + return +} + +func (o *SocketOptions) applyToUDPConn(c *net.UDPConn) error { + if o.BindInterface != nil && bindInterfaceFunc != nil { + err := bindInterfaceFunc(c, *o.BindInterface) + if err != nil { + return fmt.Errorf("failed to bind to interface: %w", err) + } + } + if o.FirewallMark != nil && firewallMarkFunc != nil { + err := firewallMarkFunc(c, *o.FirewallMark) + if err != nil { + return fmt.Errorf("failed to set fwmark: %w", err) + } + } + if o.FdControlUnixSocket != nil && fdControlUnixSocketFunc != nil { + err := fdControlUnixSocketFunc(c, *o.FdControlUnixSocket) + if err != nil { + return fmt.Errorf("failed to send fd to control unix socket: %w", err) + } + } + return nil +} diff --git a/hysteria/app/internal/sockopts/sockopts_linux.go b/hysteria/app/internal/sockopts/sockopts_linux.go new file mode 100644 index 0000000000..d1e5d23c88 --- /dev/null +++ b/hysteria/app/internal/sockopts/sockopts_linux.go @@ -0,0 +1,96 @@ +//go:build linux + +package sockopts + +import ( + "fmt" + "net" + "time" + + "golang.org/x/exp/constraints" + "golang.org/x/sys/unix" +) + +const ( + fdControlUnixTimeout = 3 * time.Second +) + +func init() { + bindInterfaceFunc = bindInterfaceImpl + firewallMarkFunc = firewallMarkImpl + fdControlUnixSocketFunc = fdControlUnixSocketImpl +} + +func controlUDPConn(c *net.UDPConn, cb func(fd int) error) (err error) { + rconn, err := c.SyscallConn() + if err != nil { + return + } + cerr := rconn.Control(func(fd uintptr) { + err = cb(int(fd)) + }) + if err != nil { + return + } + if cerr != nil { + err = fmt.Errorf("failed to control fd: %w", cerr) + return + } + return +} + +func bindInterfaceImpl(c *net.UDPConn, device string) error { + return controlUDPConn(c, func(fd int) error { + return unix.BindToDevice(fd, device) + }) +} + +func firewallMarkImpl(c *net.UDPConn, fwmark uint32) error { + return controlUDPConn(c, func(fd int) error { + return unix.SetsockoptInt(fd, unix.SOL_SOCKET, unix.SO_MARK, int(fwmark)) + }) +} + +func fdControlUnixSocketImpl(c *net.UDPConn, path string) error { + return controlUDPConn(c, func(fd int) error { + socketFd, err := unix.Socket(unix.AF_UNIX, unix.SOCK_STREAM, 0) + if err != nil { + return fmt.Errorf("failed to create unix socket: %w", err) + } + defer unix.Close(socketFd) + + var timeout unix.Timeval + timeUsec := fdControlUnixTimeout.Microseconds() + castAssignInteger(timeUsec/1e6, &timeout.Sec) + // Specifying the type explicitly is not necessary here, but it makes GoLand happy. + castAssignInteger[int64](timeUsec%1e6, &timeout.Usec) + + _ = unix.SetsockoptTimeval(socketFd, unix.SOL_SOCKET, unix.SO_RCVTIMEO, &timeout) + _ = unix.SetsockoptTimeval(socketFd, unix.SOL_SOCKET, unix.SO_SNDTIMEO, &timeout) + + err = unix.Connect(socketFd, &unix.SockaddrUnix{Name: path}) + if err != nil { + return fmt.Errorf("failed to connect: %w", err) + } + + err = unix.Sendmsg(socketFd, nil, unix.UnixRights(fd), nil, 0) + if err != nil { + return fmt.Errorf("failed to send: %w", err) + } + + dummy := []byte{1} + n, err := unix.Read(socketFd, dummy) + if err != nil { + return fmt.Errorf("failed to receive: %w", err) + } + if n != 1 { + return fmt.Errorf("socket closed unexpectedly") + } + + return nil + }) +} + +func castAssignInteger[F, T constraints.Integer](from F, to *T) { + *to = T(from) +} diff --git a/hysteria/app/internal/sockopts/sockopts_linux_test.go b/hysteria/app/internal/sockopts/sockopts_linux_test.go new file mode 100644 index 0000000000..66614a4c7a --- /dev/null +++ b/hysteria/app/internal/sockopts/sockopts_linux_test.go @@ -0,0 +1,53 @@ +//go:build linux + +package sockopts + +import ( + "net" + "os" + "os/exec" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "golang.org/x/sys/unix" +) + +func Test_fdControlUnixSocketImpl(t *testing.T) { + sockPath := "./fd_control_unix_socket_test.sock" + defer os.Remove(sockPath) + + // Run test server + cmd := exec.Command("python", "fd_control_unix_socket_test.py", sockPath) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + err := cmd.Start() + if !assert.NoError(t, err) { + return + } + defer cmd.Process.Kill() + + // Wait for the server to start + time.Sleep(1 * time.Second) + + so := SocketOptions{ + FdControlUnixSocket: &sockPath, + } + conn, err := so.ListenUDP() + if !assert.NoError(t, err) { + return + } + defer conn.Close() + + err = controlUDPConn(conn.(*net.UDPConn), func(fd int) (err error) { + rcvbuf, err := unix.GetsockoptInt(fd, unix.SOL_SOCKET, unix.SO_RCVBUF) + if err != nil { + return + } + // The test server called setsockopt(fd, SOL_SOCKET, SO_RCVBUF, 2500), + // and kernel will double this value for getsockopt(). + assert.Equal(t, 5000, rcvbuf) + return + }) + assert.NoError(t, err) +} diff --git a/hysteria/extras/transport/udphop/conn.go b/hysteria/extras/transport/udphop/conn.go index f20c583882..32cc31c068 100644 --- a/hysteria/extras/transport/udphop/conn.go +++ b/hysteria/extras/transport/udphop/conn.go @@ -44,7 +44,7 @@ type udpPacket struct { Err error } -type ListenUDPFunc func() (net.PacketConn, error) +type ListenUDPFunc = func() (net.PacketConn, error) func NewUDPHopPacketConn(addr *UDPHopAddr, hopInterval time.Duration, listenUDPFunc ListenUDPFunc) (net.PacketConn, error) { if hopInterval == 0 { diff --git a/juicity/go.mod b/juicity/go.mod index 927388968b..298880aaee 100644 --- a/juicity/go.mod +++ b/juicity/go.mod @@ -3,11 +3,11 @@ module github.com/juicity/juicity go 1.21.0 require ( - github.com/daeuniverse/outbound v0.0.0-20240101085641-7932e7df927d - github.com/daeuniverse/softwind v0.0.0-20231230065827-eed67f20d2c1 + github.com/daeuniverse/outbound v0.0.0-20240413032918-078e65ce9409 + github.com/daeuniverse/quic-go v0.0.0-20240413031024-943f218e0810 + github.com/daeuniverse/softwind v0.0.0-20240413031314-0049ada6ee9d github.com/google/uuid v1.3.0 github.com/miekg/dns v1.1.55 - github.com/mzz2017/quic-go v0.0.0-20231230054300-5221ce9164a3 github.com/nadoo/glider v0.16.3 github.com/rs/zerolog v1.30.0 github.com/sourcegraph/conc v0.3.0 @@ -16,41 +16,39 @@ require ( ) require ( - github.com/andybalholm/brotli v1.0.5 // indirect + github.com/andybalholm/brotli v1.0.6 // indirect + github.com/cloudflare/circl v1.3.7 // indirect github.com/dgryski/go-camellia v0.0.0-20191119043421-69a8a13fb23d // indirect github.com/dgryski/go-idea v0.0.0-20170306091226-d2fb45a411fb // indirect github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect github.com/dgryski/go-rc2 v0.0.0-20150621095337-8a9021637152 // indirect github.com/eknkc/basex v1.0.1 // indirect - github.com/gaukas/godicttls v0.0.4 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8 // indirect github.com/gorilla/websocket v1.5.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.16.7 // indirect + github.com/klauspost/compress v1.17.4 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.19 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/mzz2017/disk-bloom v1.0.1 // indirect github.com/onsi/ginkgo/v2 v2.11.0 // indirect - github.com/quic-go/qtls-go1-20 v0.4.1 // indirect - github.com/quic-go/quic-go v0.37.4 // indirect - github.com/refraction-networking/utls v1.4.3 // indirect + github.com/refraction-networking/utls v1.6.4 // indirect github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb // indirect github.com/spf13/pflag v1.0.5 // indirect gitlab.com/yawning/chacha20.git v0.0.0-20230427033715-7877545b1b37 // indirect go.uber.org/atomic v1.7.0 // indirect - go.uber.org/mock v0.3.0 // indirect + go.uber.org/mock v0.4.0 // indirect go.uber.org/multierr v1.9.0 // indirect - golang.org/x/crypto v0.12.0 // indirect + golang.org/x/crypto v0.18.0 // indirect golang.org/x/exp v0.0.0-20230728194245-b0cb94b80691 // indirect golang.org/x/mod v0.12.0 // indirect - golang.org/x/net v0.14.0 // indirect - golang.org/x/sys v0.11.0 // indirect - golang.org/x/text v0.12.0 // indirect + golang.org/x/net v0.20.0 // indirect + golang.org/x/sys v0.16.0 // indirect + golang.org/x/text v0.14.0 // indirect golang.org/x/tools v0.11.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230807174057-1744710a1577 // indirect google.golang.org/grpc v1.57.0 // indirect @@ -61,6 +59,6 @@ replace github.com/nadoo/glider => github.com/juicity/glider v0.0.0-202308051437 // replace github.com/daeuniverse/softwind => ../softwind -// replace github.com/mzz2017/quic-go => ../quic-go +// replace github.com/daeuniverse/quic-go => ../quic-go -// replace github.com/mzz2017/quic-go => github.com/mzz2017/quic-go v0.0.0-20230821141654-3dd2575ee6bc +// replace github.com/daeuniverse/quic-go => github.com/daeuniverse/quic-go v0.0.0-20230821141654-3dd2575ee6bc diff --git a/juicity/go.sum b/juicity/go.sum index aad59f2c2e..504d196377 100644 --- a/juicity/go.sum +++ b/juicity/go.sum @@ -1,11 +1,15 @@ -github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= -github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI= +github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= +github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/daeuniverse/outbound v0.0.0-20240101085641-7932e7df927d h1:hEZDwJvoTATxtNU8/kirJP9GK0tFxekXzT00cGXO0xg= -github.com/daeuniverse/outbound v0.0.0-20240101085641-7932e7df927d/go.mod h1:RlBqzRS0OfxDxmD1bgNfTmz9uzs8wQSmSG2vonMwSd0= -github.com/daeuniverse/softwind v0.0.0-20231230065827-eed67f20d2c1 h1:qh16GLF9TfntnLIwos49rj7Yj4EHICDe9ToesIjTm1c= -github.com/daeuniverse/softwind v0.0.0-20231230065827-eed67f20d2c1/go.mod h1:ly72DcZIxHlKbOEz1qaSCh99lr7ns8T5JLpfww8hXrI= +github.com/daeuniverse/outbound v0.0.0-20240413032918-078e65ce9409 h1:A08H+3cGMreCiAYGq965rP2XoKXgvPKoR9I09/lDW7s= +github.com/daeuniverse/outbound v0.0.0-20240413032918-078e65ce9409/go.mod h1:+incQmNwLG/JkkBymqMz/SnxRGWcSCSjy80CAjVj4v0= +github.com/daeuniverse/quic-go v0.0.0-20240413031024-943f218e0810 h1:YtEYouFaNrg9sV9vf3UabvKShKn6sD0QaCdOxCwaF3g= +github.com/daeuniverse/quic-go v0.0.0-20240413031024-943f218e0810/go.mod h1:61o2uZUGLrlv1i+oO2rx9sVX0vbf8cHzdSHt7h6lMnM= +github.com/daeuniverse/softwind v0.0.0-20240413031314-0049ada6ee9d h1:1Tj48+Vv5W4XIyG0X2JWzzRBo13ll6csThKzFGCbT1o= +github.com/daeuniverse/softwind v0.0.0-20240413031314-0049ada6ee9d/go.mod h1:xhBh5EiYniZRA0qRRy0PvjRd1TeHMXR8M6k0beD7sv4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -22,8 +26,6 @@ github.com/ebfe/rc2 v0.0.0-20131011165748-24b9757f5521 h1:fBHFH+Y/GPGFGo7LIrErQc github.com/ebfe/rc2 v0.0.0-20131011165748-24b9757f5521/go.mod h1:ucvhdsUCE3TH0LoLRb6ShHiJl8e39dGlx6A4g/ujlow= github.com/eknkc/basex v1.0.1 h1:TcyAkqh4oJXgV3WYyL4KEfCMk9W8oJCpmx1bo+jVgKY= github.com/eknkc/basex v1.0.1/go.mod h1:k/F/exNEHFdbs3ZHuasoP2E7zeWwZblG84Y7Z59vQRo= -github.com/gaukas/godicttls v0.0.4 h1:NlRaXb3J6hAnTmWdsEKb9bcSBD6BvcIjdGdeb0zfXbk= -github.com/gaukas/godicttls v0.0.4/go.mod h1:l6EenT4TLWgTdwslVb4sEMOCf7Bv0JAK67deKr9/NCI= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= @@ -48,8 +50,8 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/juicity/glider v0.0.0-20230805143717-947042416fa6 h1:BZYZwOK4WgiQIpbOM1EN4c4PCob6ssqqrDXGiTTcE9o= github.com/juicity/glider v0.0.0-20230805143717-947042416fa6/go.mod h1:CRvv3wTGZh3tpBYZnQf6ZFZitsRjXll1N6KrZEEeo+I= -github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= -github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= +github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= @@ -66,8 +68,6 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mzz2017/disk-bloom v1.0.1 h1:rEF9MiXd9qMW3ibRpqcerLXULoTgRlM21yqqJl1B90M= github.com/mzz2017/disk-bloom v1.0.1/go.mod h1:JLHETtUu44Z6iBmsqzkOtFlRvXSlKnxjwiBRDapizDI= -github.com/mzz2017/quic-go v0.0.0-20231230054300-5221ce9164a3 h1:V0C+4ZV0TzzKH05TAjtxAKODudB5dKyO6rayhtSO55I= -github.com/mzz2017/quic-go v0.0.0-20231230054300-5221ce9164a3/go.mod h1:RVldHw4emztg37XspqsQSOqptlBuYTCHD/brBTROQSw= github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU= github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM= github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc= @@ -75,12 +75,8 @@ github.com/onsi/gomega v1.27.8/go.mod h1:2J8vzI/s+2shY9XHRApDkdgPo1TKT7P2u6fXeJK github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs= -github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= -github.com/quic-go/quic-go v0.37.4 h1:ke8B73yMCWGq9MfrCCAw0Uzdm7GaViC3i39dsIdDlH4= -github.com/quic-go/quic-go v0.37.4/go.mod h1:YsbH1r4mSHPJcLF4k4zruUkLBqctEMBDR6VPvcYjIsU= -github.com/refraction-networking/utls v1.4.3 h1:BdWS3BSzCwWCFfMIXP3mjLAyQkdmog7diaD/OqFbAzM= -github.com/refraction-networking/utls v1.4.3/go.mod h1:4u9V/awOSBrRw6+federGmVJQfPtemEqLBXkML1b0bo= +github.com/refraction-networking/utls v1.6.4 h1:aeynTroaYn7y+mFtqv8D0bQ4bw0y9nJHneGxJ7lvRDM= +github.com/refraction-networking/utls v1.6.4/go.mod h1:2VL2xfiqgFAZtJKeUTlf+PSYFs3Eu7km0gCtXJ3m8zs= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.30.0 h1:SymVODrcRsaRaSInD9yQtKbtWqwsfoPcRff/oRXLj4c= github.com/rs/zerolog v1.30.0/go.mod h1:/tk+P47gFdPXq4QYjvCmT5/Gsug2nagsFWBWhAiSi1w= @@ -103,18 +99,18 @@ gitlab.com/yawning/chacha20.git v0.0.0-20230427033715-7877545b1b37 h1:ZrWBE3u/o9 gitlab.com/yawning/chacha20.git v0.0.0-20230427033715-7877545b1b37/go.mod h1:3x6b94nWCP/a2XB/joOPMiGYUBvqbLfeY/BkHLeDs6s= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo= -go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= +go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= +go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= -golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= -golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/exp v0.0.0-20230728194245-b0cb94b80691 h1:/yRP+0AN7mf5DkD3BAI6TOFnd51gEoDEb8o35jIFtgw= golang.org/x/exp v0.0.0-20230728194245-b0cb94b80691/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= -golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= +golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20190902133755-9109b7679e13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -122,10 +118,12 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= -golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.11.0 h1:EMCa6U9S2LtZXLAMoWiR/R8dAQFRqbAitmbJ2UKhoi8= golang.org/x/tools v0.11.0/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/juicity/server/server.go b/juicity/server/server.go index 4c1ea048b5..76531cfcab 100644 --- a/juicity/server/server.go +++ b/juicity/server/server.go @@ -18,6 +18,7 @@ import ( "github.com/juicity/juicity/pkg/log" "github.com/daeuniverse/outbound/dialer" + "github.com/daeuniverse/quic-go" "github.com/daeuniverse/softwind/ciphers" "github.com/daeuniverse/softwind/netproxy" "github.com/daeuniverse/softwind/pkg/fastrand" @@ -28,7 +29,6 @@ import ( "github.com/daeuniverse/softwind/protocol/tuic" "github.com/daeuniverse/softwind/protocol/tuic/common" "github.com/google/uuid" - "github.com/mzz2017/quic-go" ) const ( diff --git a/lede/target/linux/generic/hack-6.1/721-net-add-packet-mangeling.patch b/lede/target/linux/generic/hack-6.1/721-net-add-packet-mangeling.patch index 6d47bcdb48..a9c889e186 100644 --- a/lede/target/linux/generic/hack-6.1/721-net-add-packet-mangeling.patch +++ b/lede/target/linux/generic/hack-6.1/721-net-add-packet-mangeling.patch @@ -60,7 +60,7 @@ Signed-off-by: Felix Fietkau */ --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h -@@ -3040,6 +3040,10 @@ static inline int pskb_trim(struct sk_bu +@@ -3035,6 +3035,10 @@ static inline int pskb_trim(struct sk_bu return (len < skb->len) ? __pskb_trim(skb, len) : 0; } @@ -71,7 +71,7 @@ Signed-off-by: Felix Fietkau /** * pskb_trim_unique - remove end from a paged unique (not cloned) buffer * @skb: buffer to alter -@@ -3189,16 +3193,6 @@ static inline struct sk_buff *dev_alloc_ +@@ -3184,16 +3188,6 @@ static inline struct sk_buff *dev_alloc_ } diff --git a/lede/target/linux/generic/hack-6.1/930-Revert-Revert-Revert-driver-core-Set-fw_devlink-on-b.patch b/lede/target/linux/generic/hack-6.1/930-Revert-Revert-Revert-driver-core-Set-fw_devlink-on-b.patch index 04aaab7adf..f2ae028aa1 100644 --- a/lede/target/linux/generic/hack-6.1/930-Revert-Revert-Revert-driver-core-Set-fw_devlink-on-b.patch +++ b/lede/target/linux/generic/hack-6.1/930-Revert-Revert-Revert-driver-core-Set-fw_devlink-on-b.patch @@ -19,7 +19,7 @@ Signed-off-by: Rafał Miłecki --- a/drivers/base/core.c +++ b/drivers/base/core.c -@@ -1702,7 +1702,7 @@ static void device_links_purge(struct de +@@ -1717,7 +1717,7 @@ static void device_links_purge(struct de #define FW_DEVLINK_FLAGS_RPM (FW_DEVLINK_FLAGS_ON | \ DL_FLAG_PM_RUNTIME) diff --git a/lede/target/linux/generic/hack-6.1/930-usb-net-for-fm350.patch b/lede/target/linux/generic/hack-6.1/930-usb-net-for-fm350.patch new file mode 100644 index 0000000000..bf436eb2e7 --- /dev/null +++ b/lede/target/linux/generic/hack-6.1/930-usb-net-for-fm350.patch @@ -0,0 +1,26 @@ +--- a/drivers/usb/serial/option.c ++++ b/drivers/usb/serial/option.c +@@ -589,6 +589,7 @@ + + + static const struct usb_device_id option_ids[] = { ++ { USB_DEVICE(MEDIATEK_VENDOR_ID, 0x7127) }, + { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) }, + { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA) }, + { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_LIGHT) }, +@@ -2210,6 +2211,15 @@ + if (device_flags & NUMEP2 && iface_desc->bNumEndpoints != 2) + return -ENODEV; + ++ if(id->idVendor == MEDIATEK_VENDOR_ID && ++ (id->idProduct == cpu_to_le16(0x7127) && ++ ((serial->interface->cur_altsetting->desc.bInterfaceNumber <= 5) || ++ (serial->interface->cur_altsetting->desc.bInterfaceNumber >= 7)))) ++ { ++ printk(KERN_INFO "Discovery the interface for Fibocom & MEDIATEK."); ++ return -ENODEV; ++ } ++ + /* Store the device flags so we can use them during attach. */ + usb_set_serial_data(serial, (void *)device_flags); + diff --git a/lede/target/linux/generic/hack-6.1/953-net-patch-linux-kernel-to-support-shortcut-fe.patch b/lede/target/linux/generic/hack-6.1/953-net-patch-linux-kernel-to-support-shortcut-fe.patch index 985e737aaa..72e5d3c952 100644 --- a/lede/target/linux/generic/hack-6.1/953-net-patch-linux-kernel-to-support-shortcut-fe.patch +++ b/lede/target/linux/generic/hack-6.1/953-net-patch-linux-kernel-to-support-shortcut-fe.patch @@ -12,7 +12,7 @@ struct list_head *br_ip_list); --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h -@@ -995,6 +995,10 @@ struct sk_buff { +@@ -990,6 +990,10 @@ struct sk_buff { __u8 csum_not_inet:1; __u8 scm_io_uring:1; diff --git a/lede/target/linux/generic/hack-6.6/930-usb-net-for-fm350.patch b/lede/target/linux/generic/hack-6.6/930-usb-net-for-fm350.patch new file mode 100644 index 0000000000..bf436eb2e7 --- /dev/null +++ b/lede/target/linux/generic/hack-6.6/930-usb-net-for-fm350.patch @@ -0,0 +1,26 @@ +--- a/drivers/usb/serial/option.c ++++ b/drivers/usb/serial/option.c +@@ -589,6 +589,7 @@ + + + static const struct usb_device_id option_ids[] = { ++ { USB_DEVICE(MEDIATEK_VENDOR_ID, 0x7127) }, + { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) }, + { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA) }, + { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_LIGHT) }, +@@ -2210,6 +2211,15 @@ + if (device_flags & NUMEP2 && iface_desc->bNumEndpoints != 2) + return -ENODEV; + ++ if(id->idVendor == MEDIATEK_VENDOR_ID && ++ (id->idProduct == cpu_to_le16(0x7127) && ++ ((serial->interface->cur_altsetting->desc.bInterfaceNumber <= 5) || ++ (serial->interface->cur_altsetting->desc.bInterfaceNumber >= 7)))) ++ { ++ printk(KERN_INFO "Discovery the interface for Fibocom & MEDIATEK."); ++ return -ENODEV; ++ } ++ + /* Store the device flags so we can use them during attach. */ + usb_set_serial_data(serial, (void *)device_flags); + diff --git a/lede/target/linux/generic/hack-6.6/953-net-patch-linux-kernel-to-support-shortcut-fe.patch b/lede/target/linux/generic/hack-6.6/953-net-patch-linux-kernel-to-support-shortcut-fe.patch index f6abdbf591..f13eed9984 100644 --- a/lede/target/linux/generic/hack-6.6/953-net-patch-linux-kernel-to-support-shortcut-fe.patch +++ b/lede/target/linux/generic/hack-6.6/953-net-patch-linux-kernel-to-support-shortcut-fe.patch @@ -12,7 +12,7 @@ struct list_head *br_ip_list); --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h -@@ -991,6 +991,10 @@ struct sk_buff { +@@ -986,6 +986,10 @@ struct sk_buff { __u8 csum_not_inet:1; #endif diff --git a/lede/target/linux/generic/pending-6.1/610-netfilter_match_bypass_default_checks.patch b/lede/target/linux/generic/pending-6.1/610-netfilter_match_bypass_default_checks.patch index 7e34ef3713..56d62ab8e2 100644 --- a/lede/target/linux/generic/pending-6.1/610-netfilter_match_bypass_default_checks.patch +++ b/lede/target/linux/generic/pending-6.1/610-netfilter_match_bypass_default_checks.patch @@ -91,7 +91,7 @@ Signed-off-by: Felix Fietkau for (i = sizeof(struct ipt_entry); i < e->target_offset; i += m->u.match_size) { -@@ -1223,12 +1260,15 @@ compat_copy_entry_to_user(struct ipt_ent +@@ -1225,12 +1262,15 @@ compat_copy_entry_to_user(struct ipt_ent compat_uint_t origsize; const struct xt_entry_match *ematch; int ret = 0; diff --git a/lede/target/linux/generic/pending-6.1/655-increase_skb_pad.patch b/lede/target/linux/generic/pending-6.1/655-increase_skb_pad.patch index 0c47bc9d2c..3e5c2337e9 100644 --- a/lede/target/linux/generic/pending-6.1/655-increase_skb_pad.patch +++ b/lede/target/linux/generic/pending-6.1/655-increase_skb_pad.patch @@ -9,7 +9,7 @@ Signed-off-by: Felix Fietkau --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h -@@ -3006,7 +3006,7 @@ static inline int pskb_network_may_pull( +@@ -3001,7 +3001,7 @@ static inline int pskb_network_may_pull( * NET_IP_ALIGN(2) + ethernet_header(14) + IP_header(20/40) + ports(8) */ #ifndef NET_SKB_PAD diff --git a/lede/target/linux/generic/pending-6.1/680-NET-skip-GRO-for-foreign-MAC-addresses.patch b/lede/target/linux/generic/pending-6.1/680-NET-skip-GRO-for-foreign-MAC-addresses.patch index e316da555b..4b053fe875 100644 --- a/lede/target/linux/generic/pending-6.1/680-NET-skip-GRO-for-foreign-MAC-addresses.patch +++ b/lede/target/linux/generic/pending-6.1/680-NET-skip-GRO-for-foreign-MAC-addresses.patch @@ -22,7 +22,7 @@ Signed-off-by: Felix Fietkau #endif --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h -@@ -972,6 +972,7 @@ struct sk_buff { +@@ -967,6 +967,7 @@ struct sk_buff { #ifdef CONFIG_IPV6_NDISC_NODETYPE __u8 ndisc_nodetype:2; #endif @@ -32,7 +32,7 @@ Signed-off-by: Felix Fietkau __u8 inner_protocol_type:1; --- a/net/core/gro.c +++ b/net/core/gro.c -@@ -491,6 +491,9 @@ static enum gro_result dev_gro_receive(s +@@ -492,6 +492,9 @@ static enum gro_result dev_gro_receive(s int same_flow; int grow; diff --git a/lede/target/linux/rockchip/patches-6.1/101-net-realtek-r8169-add-LED-configuration-from-OF.patch b/lede/target/linux/rockchip/patches-6.1/101-net-realtek-r8169-add-LED-configuration-from-OF.patch index 1ae4663e3d..972f46cf17 100644 --- a/lede/target/linux/rockchip/patches-6.1/101-net-realtek-r8169-add-LED-configuration-from-OF.patch +++ b/lede/target/linux/rockchip/patches-6.1/101-net-realtek-r8169-add-LED-configuration-from-OF.patch @@ -25,7 +25,7 @@ Subject: [PATCH] r8169: add LED configuration from OF TxDescStartAddrLow = 0x20, TxDescStartAddrHigh = 0x24, TxHDescStartAddrLow = 0x28, -@@ -5179,6 +5181,22 @@ static bool rtl_aspm_is_safe(struct rtl8 +@@ -5265,6 +5267,22 @@ static bool rtl_aspm_is_safe(struct rtl8 return false; } @@ -48,7 +48,7 @@ Subject: [PATCH] r8169: add LED configuration from OF static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { struct rtl8169_private *tp; -@@ -5347,6 +5365,7 @@ static int rtl_init_one(struct pci_dev * +@@ -5436,6 +5454,7 @@ static int rtl_init_one(struct pci_dev * if (!tp->counters) return -ENOMEM; diff --git a/lede/target/linux/rockchip/patches-6.6/101-net-realtek-r8169-add-LED-configuration-from-OF.patch b/lede/target/linux/rockchip/patches-6.6/101-net-realtek-r8169-add-LED-configuration-from-OF.patch index fba29fe9c7..78059056de 100644 --- a/lede/target/linux/rockchip/patches-6.6/101-net-realtek-r8169-add-LED-configuration-from-OF.patch +++ b/lede/target/linux/rockchip/patches-6.6/101-net-realtek-r8169-add-LED-configuration-from-OF.patch @@ -25,7 +25,7 @@ Subject: [PATCH] r8169: add LED configuration from OF TxDescStartAddrLow = 0x20, TxDescStartAddrHigh = 0x24, TxHDescStartAddrLow = 0x28, -@@ -5202,6 +5204,22 @@ static bool rtl_aspm_is_safe(struct rtl8 +@@ -5234,6 +5236,22 @@ static bool rtl_aspm_is_safe(struct rtl8 return false; } @@ -48,7 +48,7 @@ Subject: [PATCH] r8169: add LED configuration from OF static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { struct rtl8169_private *tp; -@@ -5373,6 +5391,7 @@ static int rtl_init_one(struct pci_dev * +@@ -5405,6 +5423,7 @@ static int rtl_init_one(struct pci_dev * if (!tp->counters) return -ENOMEM; diff --git a/mihomo/component/resolver/system.go b/mihomo/component/resolver/system.go index 4ed990a365..a134bb05ea 100644 --- a/mihomo/component/resolver/system.go +++ b/mihomo/component/resolver/system.go @@ -7,6 +7,10 @@ var blacklist struct { Mutex sync.Mutex } +func init() { + blacklist.Map = make(map[string]struct{}) +} + func AddSystemDnsBlacklist(names ...string) { blacklist.Mutex.Lock() defer blacklist.Mutex.Unlock() diff --git a/small/hysteria/Makefile b/small/hysteria/Makefile index 0e492f5e2a..d71d5c5958 100644 --- a/small/hysteria/Makefile +++ b/small/hysteria/Makefile @@ -5,12 +5,12 @@ include $(TOPDIR)/rules.mk PKG_NAME:=hysteria -PKG_VERSION:=2.4.0 +PKG_VERSION:=2.4.1 PKG_RELEASE:=1 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://codeload.github.com/apernet/hysteria/tar.gz/app/v$(PKG_VERSION)? -PKG_HASH:=7776ba22b76446d6c71f496be8e635780f1b219c7d0d3a3bd99ee81067f8c8b4 +PKG_HASH:=a03d8843048e87c26417c1eac7107aa4eea7f8d5329a18eb56d7ce89f6fedf29 PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-app-v$(PKG_VERSION) PKG_LICENSE:=MIT diff --git a/v2raya/install/universal/v2raya.desktop b/v2raya/install/universal/v2raya.desktop index d686cd65f4..5219722463 100644 --- a/v2raya/install/universal/v2raya.desktop +++ b/v2raya/install/universal/v2raya.desktop @@ -2,7 +2,7 @@ Version=1.0 Terminal=false Type=Application -Name=v2rayA +Name=v2rayA Web Panel Categories=Network; Keywords=Internet;VPN;Proxy;v2ray;v2raya Exec=xdg-open "http://127.0.0.1:2017" diff --git a/v2rayng/V2rayNG/app/src/main/AndroidManifest.xml b/v2rayng/V2rayNG/app/src/main/AndroidManifest.xml index 9efa35471f..bc4f6566ad 100644 --- a/v2rayng/V2rayNG/app/src/main/AndroidManifest.xml +++ b/v2rayng/V2rayNG/app/src/main/AndroidManifest.xml @@ -127,6 +127,9 @@ + { startActivity(Intent(this, UserAssetActivity::class.java)) } - R.id.feedback -> { - Utils.openUri(this, AppConfig.v2rayNGIssues) - } R.id.promotion -> { - Utils.openUri(this, "${Utils.decode(AppConfig.promotionUrl)}?t=${System.currentTimeMillis()}") + Utils.openUri(this, "${Utils.decode(AppConfig.PromotionUrl)}?t=${System.currentTimeMillis()}") } R.id.logcat -> { startActivity(Intent(this, LogcatActivity::class.java)) } - R.id.privacy_policy-> { - Utils.openUri(this, AppConfig.v2rayNGPrivacyPolicy) + R.id.about-> { + startActivity(Intent(this, AboutActivity::class.java)) } } binding.drawerLayout.closeDrawer(GravityCompat.START) diff --git a/v2rayng/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainRecyclerAdapter.kt b/v2rayng/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainRecyclerAdapter.kt index 1a990dad76..12278e3935 100644 --- a/v2rayng/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainRecyclerAdapter.kt +++ b/v2rayng/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainRecyclerAdapter.kt @@ -178,7 +178,7 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter + + + diff --git a/v2rayng/V2rayNG/app/src/main/res/drawable-night/ic_feedback_24dp.xml b/v2rayng/V2rayNG/app/src/main/res/drawable-night/ic_feedback_24dp.xml index 3e08fae236..212bc0724e 100644 --- a/v2rayng/V2rayNG/app/src/main/res/drawable-night/ic_feedback_24dp.xml +++ b/v2rayng/V2rayNG/app/src/main/res/drawable-night/ic_feedback_24dp.xml @@ -1,9 +1,12 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="1024" + android:viewportHeight="1024"> + android:pathData="M512,192.7v42.7a21.3,21.3 0,0 1,-21.3 21.3H213.5V770.6l552.9,-2.2v-233.4a21.3,21.3 0,0 1,21.3 -21.3h42.7a21.3,21.3 0,0 1,21.3 21.3v256.1c0,32.6 -24.9,59.3 -56.6,62.4l-6.1,0.3 -598.2,2.1c-32.6,0 -59.3,-24.8 -62.4,-56.6l-0.3,-6V234c0,-32.6 24.8,-59.3 56.6,-62.4l6,-0.3H490.7a21.3,21.3 0,0 1,21.3 21.3z" /> + diff --git a/v2rayng/V2rayNG/app/src/main/res/drawable-night/ic_file_24dp.xml b/v2rayng/V2rayNG/app/src/main/res/drawable-night/ic_file_24dp.xml index f7f9df2e09..3ddbb298ce 100644 --- a/v2rayng/V2rayNG/app/src/main/res/drawable-night/ic_file_24dp.xml +++ b/v2rayng/V2rayNG/app/src/main/res/drawable-night/ic_file_24dp.xml @@ -1,34 +1,9 @@ + android:viewportWidth="1024" + android:viewportHeight="1024"> - - - + android:pathData="M537,85.3A85.3,85.3 0,0 1,597.3 110.3L828.3,341.3A85.3,85.3 0,0 1,853.3 401.7L853.3,810.7a128,128 0,0 1,-128 128L298.7,938.7a128,128 0,0 1,-128 -128L170.7,213.3a128,128 0,0 1,128 -128zM537,170.7L298.7,170.7a42.7,42.7 0,0 0,-42.7 42.7v597.3a42.7,42.7 0,0 0,42.7 42.7h426.7a42.7,42.7 0,0 0,42.7 -42.7L768,401.7L537,170.7zM512,384a42.7,42.7 0,0 1,42.4 37.7L554.7,426.7v85.3h85.3a42.7,42.7 0,0 1,5 85L640,597.3h-85.3v85.3a42.7,42.7 0,0 1,-85 5L469.3,682.7v-85.3L384,597.3a42.7,42.7 0,0 1,-5 -85L384,512h85.3v-85.3a42.7,42.7 0,0 1,42.7 -42.7z" + android:fillColor="#FFFFFFFF"/> diff --git a/v2rayng/V2rayNG/app/src/main/res/drawable-night/ic_logcat_24dp.xml b/v2rayng/V2rayNG/app/src/main/res/drawable-night/ic_logcat_24dp.xml index e7d3eb3136..c60b60624e 100644 --- a/v2rayng/V2rayNG/app/src/main/res/drawable-night/ic_logcat_24dp.xml +++ b/v2rayng/V2rayNG/app/src/main/res/drawable-night/ic_logcat_24dp.xml @@ -1,9 +1,15 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="1024" + android:viewportHeight="1024"> + android:pathData="M273.2,212.8h583.7L856.9,906.2h-583.7L273.2,212.8zM344.9,284.5L344.9,834.6h440.3L785.2,284.5h-440.3z" + android:fillColor="#FFFFFFFF"/> + + diff --git a/v2rayng/V2rayNG/app/src/main/res/drawable-night/ic_privacy_24dp.xml b/v2rayng/V2rayNG/app/src/main/res/drawable-night/ic_privacy_24dp.xml new file mode 100644 index 0000000000..1cec1d774c --- /dev/null +++ b/v2rayng/V2rayNG/app/src/main/res/drawable-night/ic_privacy_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/v2rayng/V2rayNG/app/src/main/res/drawable-night/ic_promotion_24dp.xml b/v2rayng/V2rayNG/app/src/main/res/drawable-night/ic_promotion_24dp.xml new file mode 100644 index 0000000000..e5c48df84d --- /dev/null +++ b/v2rayng/V2rayNG/app/src/main/res/drawable-night/ic_promotion_24dp.xml @@ -0,0 +1,12 @@ + + + + diff --git a/v2rayng/V2rayNG/app/src/main/res/drawable-night/ic_settings_24dp.xml b/v2rayng/V2rayNG/app/src/main/res/drawable-night/ic_settings_24dp.xml index ce997a727d..49347d0dc1 100644 --- a/v2rayng/V2rayNG/app/src/main/res/drawable-night/ic_settings_24dp.xml +++ b/v2rayng/V2rayNG/app/src/main/res/drawable-night/ic_settings_24dp.xml @@ -1,9 +1,12 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="1024" + android:viewportHeight="1024"> + android:pathData="M924.8,625.7l-65.5,-56c3.1,-19 4.7,-38.4 4.7,-57.8s-1.6,-38.8 -4.7,-57.8l65.5,-56c10.1,-8.6 13.8,-22.6 9.3,-35.2l-0.9,-2.6c-18.1,-50.5 -44.9,-96.9 -79.7,-137.9l-1.8,-2.1c-8.6,-10.1 -22.5,-13.9 -35.1,-9.5l-81.3,28.9c-30,-24.6 -63.5,-44 -99.7,-57.6l-15.7,-85c-2.4,-13.1 -12.7,-23.3 -25.8,-25.7l-2.7,-0.5c-52.1,-9.4 -106.9,-9.4 -159,0l-2.7,0.5c-13.1,2.4 -23.4,12.6 -25.8,25.7l-15.8,85.4c-35.9,13.6 -69.2,32.9 -99,57.4l-81.9,-29.1c-12.5,-4.4 -26.5,-0.7 -35.1,9.5l-1.8,2.1c-34.8,41.1 -61.6,87.5 -79.7,137.9l-0.9,2.6c-4.5,12.5 -0.8,26.5 9.3,35.2l66.3,56.6c-3.1,18.8 -4.6,38 -4.6,57.1 0,19.2 1.5,38.4 4.6,57.1L99,625.5c-10.1,8.6 -13.8,22.6 -9.3,35.2l0.9,2.6c18.1,50.4 44.9,96.9 79.7,137.9l1.8,2.1c8.6,10.1 22.5,13.9 35.1,9.5l81.9,-29.1c29.8,24.5 63.1,43.9 99,57.4l15.8,85.4c2.4,13.1 12.7,23.3 25.8,25.7l2.7,0.5c26.1,4.7 52.8,7.1 79.5,7.1 26.7,0 53.5,-2.4 79.5,-7.1l2.7,-0.5c13.1,-2.4 23.4,-12.6 25.8,-25.7l15.7,-85c36.2,-13.6 69.7,-32.9 99.7,-57.6l81.3,28.9c12.5,4.4 26.5,0.7 35.1,-9.5l1.8,-2.1c34.8,-41.1 61.6,-87.5 79.7,-137.9l0.9,-2.6c4.5,-12.3 0.8,-26.3 -9.3,-35zM788.3,465.9c2.5,15.1 3.8,30.6 3.8,46.1s-1.3,31 -3.8,46.1l-6.6,40.1 74.7,63.9c-11.3,26.1 -25.6,50.7 -42.6,73.6L721,702.8l-31.4,25.8c-23.9,19.6 -50.5,35 -79.3,45.8l-38.1,14.3 -17.9,97c-28.1,3.2 -56.8,3.2 -85,0l-17.9,-97.2 -37.8,-14.5c-28.5,-10.8 -55,-26.2 -78.7,-45.7l-31.4,-25.9 -93.4,33.2c-17,-22.9 -31.2,-47.6 -42.6,-73.6l75.5,-64.5 -6.5,-40c-2.4,-14.9 -3.7,-30.3 -3.7,-45.5 0,-15.3 1.2,-30.6 3.7,-45.5l6.5,-40 -75.5,-64.5c11.3,-26.1 25.6,-50.7 42.6,-73.6l93.4,33.2 31.4,-25.9c23.7,-19.5 50.2,-34.9 78.7,-45.7l37.9,-14.3 17.9,-97.2c28.1,-3.2 56.8,-3.2 85,0l17.9,97 38.1,14.3c28.7,10.8 55.4,26.2 79.3,45.8l31.4,25.8 92.8,-32.9c17,22.9 31.2,47.6 42.6,73.6L781.8,426l6.5,39.9z" /> + diff --git a/v2rayng/V2rayNG/app/src/main/res/drawable-night/ic_source_code_24dp.xml b/v2rayng/V2rayNG/app/src/main/res/drawable-night/ic_source_code_24dp.xml new file mode 100644 index 0000000000..43b540fbcb --- /dev/null +++ b/v2rayng/V2rayNG/app/src/main/res/drawable-night/ic_source_code_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/v2rayng/V2rayNG/app/src/main/res/drawable-night/ic_subscriptions_24dp.xml b/v2rayng/V2rayNG/app/src/main/res/drawable-night/ic_subscriptions_24dp.xml index bc20a83af0..e935b87d76 100644 --- a/v2rayng/V2rayNG/app/src/main/res/drawable-night/ic_subscriptions_24dp.xml +++ b/v2rayng/V2rayNG/app/src/main/res/drawable-night/ic_subscriptions_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="1024" + android:viewportHeight="1024"> + android:pathData="M170.6,256h682.7v85.3L170.6,341.3L170.6,256zM256,85.3h512v85.4L256,170.7L256,85.3zM853.3,426.7L170.6,426.7c-46.9,0 -85.3,38.4 -85.3,85.3v341.3c0,47 38.4,85.4 85.3,85.4h682.7c46.9,0 85.3,-38.4 85.3,-85.4L938.6,512c0,-46.9 -38.4,-85.3 -85.3,-85.3zM853.3,853.3L170.6,853.3L170.6,512h682.7v341.3z" /> diff --git a/v2rayng/V2rayNG/app/src/main/res/drawable-night/ic_telegram_24dp.xml b/v2rayng/V2rayNG/app/src/main/res/drawable-night/ic_telegram_24dp.xml new file mode 100644 index 0000000000..9837351076 --- /dev/null +++ b/v2rayng/V2rayNG/app/src/main/res/drawable-night/ic_telegram_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/v2rayng/V2rayNG/app/src/main/res/drawable-night/ic_whatshot_24dp.xml b/v2rayng/V2rayNG/app/src/main/res/drawable-night/ic_whatshot_24dp.xml deleted file mode 100644 index ad460f3c30..0000000000 --- a/v2rayng/V2rayNG/app/src/main/res/drawable-night/ic_whatshot_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/v2rayng/V2rayNG/app/src/main/res/drawable-xxhdpi/donate.png b/v2rayng/V2rayNG/app/src/main/res/drawable-xxhdpi/donate.png deleted file mode 100644 index 8825c5320f2146691887b523c07b81874609d6db..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3237 zcmb7HX*kq<7ykWcVF-<7h6sbm9 zDPqF-u&i?iT9kM*!_#7Wc(||JHYG45H_5pmmCc4vHF9*bE(9eqv()>i=W$q2RXS+M z3hKjO0p?6FT0jei1Z}VW8HJtZlF-m6e{3hz=}l)o+R*@*K)P=Ri z-w07trLn@#c5(Agy;BU}t{dRx7ACO_orIJERBGZL>|CoYkhu2lqh~akLwVy!;kV5U z?d02Q^DI+zU@eehz1LYo)NH=8|5<|h-c9wk(Cwk1dh{A|5}^Q0rOl@V^Uwc`sT-m4v4)X>~%EA{T1+xpK(6pkLbi!r3PlPCHiXj`V3ruaGv z!_g;y6tDkkZ#)dNu1*$OpJYl;%JJ*LdHMK02A{MEHxcj;s!vv$&sLp@f9YqJjVpmN z@PxJ*Bu6|%{qdMGJSMJqgQqsm~Shoz&oQ{O5zT9 z!l)=}u{-X1$?@9W-n!tgzz^s3Y_d}muwX}T{|QxC@(PS3N*~@jWAU-n$g}Kz!E=HM zaQa=&Kn^kKU_e!HOt2gwY|m~w*acr6hf=&5YL{~u$#r?YN|*i|;dRk_fs~*j5dNh- z_AH$Ai9;`1@gHqVAQRm`WtpR|F{{yy{BYPctwQQ}UB1C*Ct)vcsPC@Km!Xv$@D8RP zyK!=6qkvv0uP;s#RRqw9u{LpR46xukpo_DUIH9Sj#(!`;!U1h#;Cp8bNL>EpD{cv% zx$jPPwfC8i6mdzg&bC>;1@^yLm^bYhHwTSJx%BbvuwH8v1Z}qTP8s@r6L@>Emn`(W zv9yZW08JJRH;7y|bT@|Ysow9@>H%?~(ObcTy9vrAn7j*;;)dfyHZzEgHRhIrw}Elp zbCtq3Q@v_Nn_6U^bE>SXr}M6JPf+iNu{j=|r<)gY5FrZqC?S=l~?G-0mU-%9K#f`dELKr6gTSP!2CQT=>m zFWekBLNOg`PJKOwsveyok_bH36e)U5}-K<_)yW;r@=|k&7g_ccU^f9B8&IfP< zY$ial;|~Y_fOUV$ZXWg=e)E7bOoLzd2=+MTkk@sjRm8|+TIScQE*2&_DSmnbpM5ZF zX_86@ZX+HJqi6+MU?~AA^AgHu(W8gsxe=tC{E1glvF061AxU!{mDOafegwH@zcee< zrx`CX6uC&}F|&TiV{$m(lcKR6uhaL>vH(dq!!@B~R!pq3GtMPld%MBd!6U)b+f1?; zhO;`_YIW9hS3`YrvO>D+%=%d2@CY$E=3Lr}O=EQp#yPR1`JVOn$1Q(# z@b{-+BFlGNY9#bMWneGV7%s2--8Hd;c#g`!nBGZ+;sG78&34^d97omE3%mS7>DDGk z^szB;-sdJ?sviF*OSg*-NT2ZT1$1k;1vk7jkTyCJzn1jD7v8*G3diwX5Q#Li?fy32 zy6i8t`KMh=WsXLN7K~@BHI?1+zMLt}p2U{y>|}Uur3JLA z`?yCkvmbWE2xo=KHHQ0$_Aek8cE!m|fTR&!m?pM$#CCkM+B8+7OtO)!K%*o<2H4vH z6R?pa)OFhXZ&aE%ueKykLPI>Ov1;9n7r^|M>LZ{2RG{0kUu5~yJ~5Dg#B!xm&~<-F;7F-Ky8bM~3$Yt4IVneX4+yX9xv!G_MbN^vy}854Q_?h#M76R4C=t8S2b>eM5%2upR$+hGCBXtmd~C5&Yz zmT!u_pFC2q=FGOHGyaUK{|L~G75BgDzdwX~-hMu?LLDRyx0>F3ax@a;#A{3uU=bN9 zg%OIeopJJUZ0`y}Ej7+s))U(9CVE;g4D?MU*NRrMM_y<1D z^rFdm-(V)6I*64S*PllH;@7tja`*7GSL&cD-)P`cM+7HU>tGg?%u`6gHP8pkz=x&GIfOwfD51>d9*JzjM&u9#C;y{=X-g1g8RnL%Gl zTA7ebxmcoQX4edG*7i>?wAxB>K?U9KYQqvw#4mWz8r&2@fMU8wv7TsV zj-uvZdxWUFuX%bG4J^}O1ZQN?*#y(8U8WW?g6(B&!eZ}q1ZE?*%7a`0 z0xFn(grU97H(~KgBm$l?XhuVh6+f!GAfw*}uIneVe)^$c|<%_D)awndTuQP!!)NDw9-jxg{^ZEC}-t$j{=$hUC47C;NT= zo?DvnQ6=-RaNT5#hCKSDB$~<;+kX^cM42LN_xp3B-QZ-!rgMlAh)@~8!*AWM|Gg}{ z69s%#!CdbR6QRLi3L9`%DU9CJS!`iv4d#NAB~fMO6IRuum83?m^n5wWU-ZhYx;~z= zjKi$4-=&xSr1LG?gD3M`Lksyg#<*SrX5qI_RQ;!Q^v?GX91gR5Sr=vY8Pgc@MPnb6 zL=YfbG3;%{JYx*F%X@IVUlY!UA@}c{REUIL1o`^DLfUV$@dKPzmBMAq{zO1%ZtJtn z4;u0}YGZSS_zNTK%+M=3*g2PPSGs%~G)HzXRo?ef$Aa)PwagcN+3VJPJ&a;!Gm$ZU z$hg3e!eFVPF=-SoAk@*{0aQ%dwXbwR6$SpLDlQeQxfe-BIXj5l)6 zzyI=2BO+Rk{G&pX>LAiSflKP!=0wd9ey3O2A1oKBc&+hSeow0d+AI-!~-rXl2uYnF8U41_cm6b$W`#@7(kVla{wzrr;Mt>iJ^6D1q)Y zc756LbT>OwJ}5C_X1Lx=5=TMMn196aN{I}Iz6-gzKJ zFq8L^ZC|Zmh#K4p@B&j6aNp0>FMbDdLDT)_3YUs6ECc~|`S=4J_HcV<@%On^T@Uat z5@JeGRou;S>Qi|*0a`aGiH)WD_F>=V<|D@OP%MFI8d;V|pFQIPYPCO$v?+rBTc(RY uB`^F`Izt7v%?NR)S&RQEesd=B3FOAQ{n_M4fb8k}0kk#ri8X4r;r{_%1kxY? diff --git a/v2rayng/V2rayNG/app/src/main/res/drawable/ic_about_24dp.xml b/v2rayng/V2rayNG/app/src/main/res/drawable/ic_about_24dp.xml new file mode 100644 index 0000000000..5e1ac1382a --- /dev/null +++ b/v2rayng/V2rayNG/app/src/main/res/drawable/ic_about_24dp.xml @@ -0,0 +1,12 @@ + + + + diff --git a/v2rayng/V2rayNG/app/src/main/res/drawable/ic_feedback_24dp.xml b/v2rayng/V2rayNG/app/src/main/res/drawable/ic_feedback_24dp.xml index 29f9baabd6..58d1876744 100644 --- a/v2rayng/V2rayNG/app/src/main/res/drawable/ic_feedback_24dp.xml +++ b/v2rayng/V2rayNG/app/src/main/res/drawable/ic_feedback_24dp.xml @@ -1,9 +1,12 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="1024" + android:viewportHeight="1024"> + android:pathData="M512,192.7v42.7a21.3,21.3 0,0 1,-21.3 21.3H213.5V770.6l552.9,-2.2v-233.4a21.3,21.3 0,0 1,21.3 -21.3h42.7a21.3,21.3 0,0 1,21.3 21.3v256.1c0,32.6 -24.9,59.3 -56.6,62.4l-6.1,0.3 -598.2,2.1c-32.6,0 -59.3,-24.8 -62.4,-56.6l-0.3,-6V234c0,-32.6 24.8,-59.3 56.6,-62.4l6,-0.3H490.7a21.3,21.3 0,0 1,21.3 21.3z" /> + diff --git a/v2rayng/V2rayNG/app/src/main/res/drawable/ic_file_24dp.xml b/v2rayng/V2rayNG/app/src/main/res/drawable/ic_file_24dp.xml index 7fcdda2d85..cb5193f6a4 100644 --- a/v2rayng/V2rayNG/app/src/main/res/drawable/ic_file_24dp.xml +++ b/v2rayng/V2rayNG/app/src/main/res/drawable/ic_file_24dp.xml @@ -1,34 +1,9 @@ + android:viewportWidth="1024" + android:viewportHeight="1024"> - - - + android:pathData="M537,85.3A85.3,85.3 0,0 1,597.3 110.3L828.3,341.3A85.3,85.3 0,0 1,853.3 401.7L853.3,810.7a128,128 0,0 1,-128 128L298.7,938.7a128,128 0,0 1,-128 -128L170.7,213.3a128,128 0,0 1,128 -128zM537,170.7L298.7,170.7a42.7,42.7 0,0 0,-42.7 42.7v597.3a42.7,42.7 0,0 0,42.7 42.7h426.7a42.7,42.7 0,0 0,42.7 -42.7L768,401.7L537,170.7zM512,384a42.7,42.7 0,0 1,42.4 37.7L554.7,426.7v85.3h85.3a42.7,42.7 0,0 1,5 85L640,597.3h-85.3v85.3a42.7,42.7 0,0 1,-85 5L469.3,682.7v-85.3L384,597.3a42.7,42.7 0,0 1,-5 -85L384,512h85.3v-85.3a42.7,42.7 0,0 1,42.7 -42.7z" + android:fillColor="#FF000000"/> diff --git a/v2rayng/V2rayNG/app/src/main/res/drawable/ic_logcat_24dp.xml b/v2rayng/V2rayNG/app/src/main/res/drawable/ic_logcat_24dp.xml index a07a0f90ac..86562d04f0 100644 --- a/v2rayng/V2rayNG/app/src/main/res/drawable/ic_logcat_24dp.xml +++ b/v2rayng/V2rayNG/app/src/main/res/drawable/ic_logcat_24dp.xml @@ -1,9 +1,15 @@ - + android:width="24dp" + android:height="24dp" + android:viewportWidth="1024" + android:viewportHeight="1024"> + + + diff --git a/v2rayng/V2rayNG/app/src/main/res/drawable/ic_privacy_24dp.xml b/v2rayng/V2rayNG/app/src/main/res/drawable/ic_privacy_24dp.xml new file mode 100644 index 0000000000..ad1a12d7ec --- /dev/null +++ b/v2rayng/V2rayNG/app/src/main/res/drawable/ic_privacy_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/v2rayng/V2rayNG/app/src/main/res/drawable/ic_promotion_24dp.xml b/v2rayng/V2rayNG/app/src/main/res/drawable/ic_promotion_24dp.xml new file mode 100644 index 0000000000..58505a6bf6 --- /dev/null +++ b/v2rayng/V2rayNG/app/src/main/res/drawable/ic_promotion_24dp.xml @@ -0,0 +1,12 @@ + + + + diff --git a/v2rayng/V2rayNG/app/src/main/res/drawable/ic_settings_24dp.xml b/v2rayng/V2rayNG/app/src/main/res/drawable/ic_settings_24dp.xml index ace746c40e..9bda172598 100644 --- a/v2rayng/V2rayNG/app/src/main/res/drawable/ic_settings_24dp.xml +++ b/v2rayng/V2rayNG/app/src/main/res/drawable/ic_settings_24dp.xml @@ -1,9 +1,12 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="1024" + android:viewportHeight="1024"> + android:fillColor="#000000" + android:pathData="M924.8,625.7l-65.5,-56c3.1,-19 4.7,-38.4 4.7,-57.8s-1.6,-38.8 -4.7,-57.8l65.5,-56c10.1,-8.6 13.8,-22.6 9.3,-35.2l-0.9,-2.6c-18.1,-50.5 -44.9,-96.9 -79.7,-137.9l-1.8,-2.1c-8.6,-10.1 -22.5,-13.9 -35.1,-9.5l-81.3,28.9c-30,-24.6 -63.5,-44 -99.7,-57.6l-15.7,-85c-2.4,-13.1 -12.7,-23.3 -25.8,-25.7l-2.7,-0.5c-52.1,-9.4 -106.9,-9.4 -159,0l-2.7,0.5c-13.1,2.4 -23.4,12.6 -25.8,25.7l-15.8,85.4c-35.9,13.6 -69.2,32.9 -99,57.4l-81.9,-29.1c-12.5,-4.4 -26.5,-0.7 -35.1,9.5l-1.8,2.1c-34.8,41.1 -61.6,87.5 -79.7,137.9l-0.9,2.6c-4.5,12.5 -0.8,26.5 9.3,35.2l66.3,56.6c-3.1,18.8 -4.6,38 -4.6,57.1 0,19.2 1.5,38.4 4.6,57.1L99,625.5c-10.1,8.6 -13.8,22.6 -9.3,35.2l0.9,2.6c18.1,50.4 44.9,96.9 79.7,137.9l1.8,2.1c8.6,10.1 22.5,13.9 35.1,9.5l81.9,-29.1c29.8,24.5 63.1,43.9 99,57.4l15.8,85.4c2.4,13.1 12.7,23.3 25.8,25.7l2.7,0.5c26.1,4.7 52.8,7.1 79.5,7.1 26.7,0 53.5,-2.4 79.5,-7.1l2.7,-0.5c13.1,-2.4 23.4,-12.6 25.8,-25.7l15.7,-85c36.2,-13.6 69.7,-32.9 99.7,-57.6l81.3,28.9c12.5,4.4 26.5,0.7 35.1,-9.5l1.8,-2.1c34.8,-41.1 61.6,-87.5 79.7,-137.9l0.9,-2.6c4.5,-12.3 0.8,-26.3 -9.3,-35zM788.3,465.9c2.5,15.1 3.8,30.6 3.8,46.1s-1.3,31 -3.8,46.1l-6.6,40.1 74.7,63.9c-11.3,26.1 -25.6,50.7 -42.6,73.6L721,702.8l-31.4,25.8c-23.9,19.6 -50.5,35 -79.3,45.8l-38.1,14.3 -17.9,97c-28.1,3.2 -56.8,3.2 -85,0l-17.9,-97.2 -37.8,-14.5c-28.5,-10.8 -55,-26.2 -78.7,-45.7l-31.4,-25.9 -93.4,33.2c-17,-22.9 -31.2,-47.6 -42.6,-73.6l75.5,-64.5 -6.5,-40c-2.4,-14.9 -3.7,-30.3 -3.7,-45.5 0,-15.3 1.2,-30.6 3.7,-45.5l6.5,-40 -75.5,-64.5c11.3,-26.1 25.6,-50.7 42.6,-73.6l93.4,33.2 31.4,-25.9c23.7,-19.5 50.2,-34.9 78.7,-45.7l37.9,-14.3 17.9,-97.2c28.1,-3.2 56.8,-3.2 85,0l17.9,97 38.1,14.3c28.7,10.8 55.4,26.2 79.3,45.8l31.4,25.8 92.8,-32.9c17,22.9 31.2,47.6 42.6,73.6L781.8,426l6.5,39.9z" /> + diff --git a/v2rayng/V2rayNG/app/src/main/res/drawable/ic_source_code_24dp.xml b/v2rayng/V2rayNG/app/src/main/res/drawable/ic_source_code_24dp.xml new file mode 100644 index 0000000000..73879a2a40 --- /dev/null +++ b/v2rayng/V2rayNG/app/src/main/res/drawable/ic_source_code_24dp.xml @@ -0,0 +1,4 @@ + + + diff --git a/v2rayng/V2rayNG/app/src/main/res/drawable/ic_subscriptions_24dp.xml b/v2rayng/V2rayNG/app/src/main/res/drawable/ic_subscriptions_24dp.xml index 6f0ed45510..9e82c7f969 100644 --- a/v2rayng/V2rayNG/app/src/main/res/drawable/ic_subscriptions_24dp.xml +++ b/v2rayng/V2rayNG/app/src/main/res/drawable/ic_subscriptions_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="1024" + android:viewportHeight="1024"> + android:pathData="M170.6,256h682.7v85.3L170.6,341.3L170.6,256zM256,85.3h512v85.4L256,170.7L256,85.3zM853.3,426.7L170.6,426.7c-46.9,0 -85.3,38.4 -85.3,85.3v341.3c0,47 38.4,85.4 85.3,85.4h682.7c46.9,0 85.3,-38.4 85.3,-85.4L938.6,512c0,-46.9 -38.4,-85.3 -85.3,-85.3zM853.3,853.3L170.6,853.3L170.6,512h682.7v341.3z" /> diff --git a/v2rayng/V2rayNG/app/src/main/res/drawable/ic_telegram_24dp.xml b/v2rayng/V2rayNG/app/src/main/res/drawable/ic_telegram_24dp.xml new file mode 100644 index 0000000000..f1f8fa5a25 --- /dev/null +++ b/v2rayng/V2rayNG/app/src/main/res/drawable/ic_telegram_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/v2rayng/V2rayNG/app/src/main/res/drawable/ic_whatshot_24dp.xml b/v2rayng/V2rayNG/app/src/main/res/drawable/ic_whatshot_24dp.xml deleted file mode 100644 index 1cbc037f72..0000000000 --- a/v2rayng/V2rayNG/app/src/main/res/drawable/ic_whatshot_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/v2rayng/V2rayNG/app/src/main/res/layout/activity_about.xml b/v2rayng/V2rayNG/app/src/main/res/layout/activity_about.xml new file mode 100644 index 0000000000..d139ea8d59 --- /dev/null +++ b/v2rayng/V2rayNG/app/src/main/res/layout/activity_about.xml @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/v2rayng/V2rayNG/app/src/main/res/layout/activity_main.xml b/v2rayng/V2rayNG/app/src/main/res/layout/activity_main.xml index 1f7e44ebd2..d7ace3e6dc 100644 --- a/v2rayng/V2rayNG/app/src/main/res/layout/activity_main.xml +++ b/v2rayng/V2rayNG/app/src/main/res/layout/activity_main.xml @@ -108,18 +108,6 @@ app:itemIconTint="@color/colorAccent" app:menu="@menu/menu_drawer" > - - - - diff --git a/v2rayng/V2rayNG/app/src/main/res/menu/menu_drawer.xml b/v2rayng/V2rayNG/app/src/main/res/menu/menu_drawer.xml index 100d522fe4..5f978b232a 100644 --- a/v2rayng/V2rayNG/app/src/main/res/menu/menu_drawer.xml +++ b/v2rayng/V2rayNG/app/src/main/res/menu/menu_drawer.xml @@ -22,20 +22,16 @@ - + android:id="@+id/about" + android:icon="@drawable/ic_about_24dp" + android:title="@string/title_about" /> الانضمام إلى مجموعة Telegram لم يتم العثور على تطبيق Telegram حریم خصوصی + About + Source code + Telegram channel ترقية ترقية، انقر للحصول على التفاصيل (يمكن إزالة التبرع) اشتراكات التحديث التلقائي diff --git a/v2rayng/V2rayNG/app/src/main/res/values-fa/strings.xml b/v2rayng/V2rayNG/app/src/main/res/values-fa/strings.xml index f5299b6c7c..cd8937068a 100644 --- a/v2rayng/V2rayNG/app/src/main/res/values-fa/strings.xml +++ b/v2rayng/V2rayNG/app/src/main/res/values-fa/strings.xml @@ -170,6 +170,9 @@ برنامه تلگرام پیدا نشد حریم خصوصی + About + Source code + Telegram channel تبلیغات تبلیغات، برای جزئیات بیشتر کلیک کنید (کمک مالی کنید تا حذف شود) diff --git a/v2rayng/V2rayNG/app/src/main/res/values-ru/strings.xml b/v2rayng/V2rayNG/app/src/main/res/values-ru/strings.xml index 5830d92b37..ad6095a915 100644 --- a/v2rayng/V2rayNG/app/src/main/res/values-ru/strings.xml +++ b/v2rayng/V2rayNG/app/src/main/res/values-ru/strings.xml @@ -46,8 +46,8 @@ Другие параметры Тип заголовка Режим gRPC - Запрос узла (WS/H2) / Шифрование QUIC/gRPC Authority - Путь (WS/H2) / Ключ QUIC / Сид KCP / Сервис gRPC + Запрос узла (WS/H2)/HTTPUpgrade/Шифрование QUIC/Полномочия gRPC + Путь (WS/H2)/HTTPUpgrade/Ключ QUIC/Сид KCP/Сервис gRPC TLS Разрешать небезопасные SNI @@ -108,8 +108,8 @@ Настройки мультиплексирования Использовать мультиплексирование Быстрее, но это может привести к нестабильному соединению.\nНиже можно настроить обработку TCP, UDP и QUIC. - TCP-соединения (диапазон от -1 до 1024) - XUDP-соединения (диапазон от -1 до 1024) + TCP-соединения (диапазон от 1 до 1024) + XUDP-соединения (диапазон от 1 до 1024) Обработка QUIC в мультиплексном туннеле отклонять @@ -137,7 +137,7 @@ Режим маршрутизации Пользовательские правила - Удалённая DNS (необязательно) + Удалённая DNS (UDP/TCP/HTTPS/QUIC) (необязательно) DNS VPN DNS (только IPv4/v6) @@ -172,6 +172,9 @@ Присоединиться к группе в Telegram Приложение Telegram не найдено Конфиденциальность + О приложении + Исходный код + Telegram-канал Содействие Содействие, нажмите для получения подробной информации (пожертвование может быть удалено) diff --git a/v2rayng/V2rayNG/app/src/main/res/values-vi/strings.xml b/v2rayng/V2rayNG/app/src/main/res/values-vi/strings.xml index 064adcef8f..7b890f5e3e 100644 --- a/v2rayng/V2rayNG/app/src/main/res/values-vi/strings.xml +++ b/v2rayng/V2rayNG/app/src/main/res/values-vi/strings.xml @@ -172,6 +172,9 @@ Không tìm thấy ứng dụng Telegram Chính sách bảo mật + About + Source code + Telegram channel Quảng bá server Quảng cáo, nhấn để biết thêm (Ủng hộ có thể được gỡ bỏ) diff --git a/v2rayng/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml b/v2rayng/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml index a517af0a47..09dddc4f3d 100644 --- a/v2rayng/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml +++ b/v2rayng/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml @@ -170,6 +170,9 @@ 加入Telegram Group 未找到Telegram app 隐私权政策 + 关于 + 源代码 + Telegram 频道 推广 一些推广,点击查看详情(捐赠可去除) diff --git a/v2rayng/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml b/v2rayng/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml index 22c82d6ccd..6161a75db7 100644 --- a/v2rayng/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml +++ b/v2rayng/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml @@ -170,6 +170,9 @@ 加入 Telegram 群組 未找到 Telegram 應用程式 隱私權政策 + 關於 + 原始碼 + Telegram 頻道 推廣 一些推廣,輕觸以檢視 (捐贈可去除) diff --git a/v2rayng/V2rayNG/app/src/main/res/values/strings.xml b/v2rayng/V2rayNG/app/src/main/res/values/strings.xml index 3bfd95d0f9..6b07cb9e48 100644 --- a/v2rayng/V2rayNG/app/src/main/res/values/strings.xml +++ b/v2rayng/V2rayNG/app/src/main/res/values/strings.xml @@ -183,6 +183,9 @@ Join Telegram Group Telegram app not found Privacy policy + About + Source code + Telegram channel Promotion Promotion,click for details(Donation can be removed) diff --git a/yass/src/config/config_impl_apple.mm b/yass/src/config/config_impl_apple.mm index 1ef1254cd8..ffe547b703 100644 --- a/yass/src/config/config_impl_apple.mm +++ b/yass/src/config/config_impl_apple.mm @@ -18,6 +18,8 @@ ABSL_DECLARE_FLAG(std::string, configfile); +using namespace gurl_base::apple; + // Because a suite manages the defaults of a specified app group, a suite name // must be distinct from your app’s main bundle identifier. static const char* kYassSuiteName = "it.gui.yass.suite"; @@ -28,7 +30,7 @@ namespace config { bool ConfigImplApple::OpenImpl(bool dontread) { dontread_ = dontread; - gurl_base::apple::ScopedCFTypeRef mutable_root(CFDictionaryCreateMutable( + ScopedCFTypeRef mutable_root(CFDictionaryCreateMutable( kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); // Overwrite all fields from UserDefaults @@ -39,7 +41,7 @@ bool ConfigImplApple::OpenImpl(bool dontread) { if (root) { for (NSString* key in root) { id value = root[key]; - gurl_base::apple::ScopedCFTypeRef cf_key((CFStringRef)CFBridgingRetain(key)); + ScopedCFTypeRef cf_key((CFStringRef)CFBridgingRetain(key)); CFDictionarySetValue(mutable_root, cf_key, (void*)value); } } @@ -69,9 +71,9 @@ bool ConfigImplApple::CloseImpl() { bool ConfigImplApple::ReadImpl(const std::string& key, std::string* value) { CFStringRef obj; - if (CFDictionaryGetValueIfPresent(root_, gurl_base::SysUTF8ToCFStringRef(key).get(), (const void**)&obj) && + if (CFDictionaryGetValueIfPresent(root_, SysUTF8ToCFStringRef(key).get(), (const void**)&obj) && CFGetTypeID(obj) == CFStringGetTypeID()) { - *value = gurl_base::SysCFStringRefToUTF8(obj); + *value = SysCFStringRefToUTF8(obj); return true; } LOG(WARNING) << "bad field: " << key; @@ -80,7 +82,7 @@ bool ConfigImplApple::ReadImpl(const std::string& key, std::string* value) { bool ConfigImplApple::ReadImpl(const std::string& key, bool* value) { CFBooleanRef obj; - if (CFDictionaryGetValueIfPresent(root_, gurl_base::SysUTF8ToCFStringRef(key).get(), (const void**)&obj) && + if (CFDictionaryGetValueIfPresent(root_, SysUTF8ToCFStringRef(key).get(), (const void**)&obj) && CFGetTypeID(obj) == CFBooleanGetTypeID()) { *value = CFBooleanGetValue(obj); return true; @@ -91,7 +93,7 @@ bool ConfigImplApple::ReadImpl(const std::string& key, bool* value) { bool ConfigImplApple::ReadImpl(const std::string& key, uint32_t* value) { CFNumberRef obj; - if (CFDictionaryGetValueIfPresent(root_, gurl_base::SysUTF8ToCFStringRef(key).get(), (const void**)&obj) && + if (CFDictionaryGetValueIfPresent(root_, SysUTF8ToCFStringRef(key).get(), (const void**)&obj) && CFGetTypeID(obj) == CFNumberGetTypeID() && CFNumberGetValue(obj, kCFNumberSInt32Type, value)) { return true; } @@ -101,7 +103,7 @@ bool ConfigImplApple::ReadImpl(const std::string& key, uint32_t* value) { bool ConfigImplApple::ReadImpl(const std::string& key, int32_t* value) { CFNumberRef obj; - if (CFDictionaryGetValueIfPresent(root_, gurl_base::SysUTF8ToCFStringRef(key).get(), (const void**)&obj) && + if (CFDictionaryGetValueIfPresent(root_, SysUTF8ToCFStringRef(key).get(), (const void**)&obj) && CFGetTypeID(obj) == CFNumberGetTypeID() && CFNumberGetValue(obj, kCFNumberSInt32Type, value)) { return true; } @@ -111,7 +113,7 @@ bool ConfigImplApple::ReadImpl(const std::string& key, int32_t* value) { bool ConfigImplApple::ReadImpl(const std::string& key, uint64_t* value) { CFNumberRef obj; - if (CFDictionaryGetValueIfPresent(root_, gurl_base::SysUTF8ToCFStringRef(key).get(), (const void**)&obj) && + if (CFDictionaryGetValueIfPresent(root_, SysUTF8ToCFStringRef(key).get(), (const void**)&obj) && CFGetTypeID(obj) == CFNumberGetTypeID() && CFNumberGetValue(obj, kCFNumberSInt64Type, value)) { return true; } @@ -121,7 +123,7 @@ bool ConfigImplApple::ReadImpl(const std::string& key, uint64_t* value) { bool ConfigImplApple::ReadImpl(const std::string& key, int64_t* value) { CFNumberRef obj; - if (CFDictionaryGetValueIfPresent(root_, gurl_base::SysUTF8ToCFStringRef(key).get(), (const void**)&obj) && + if (CFDictionaryGetValueIfPresent(root_, SysUTF8ToCFStringRef(key).get(), (const void**)&obj) && CFGetTypeID(obj) == CFNumberGetTypeID() && CFNumberGetValue(obj, kCFNumberSInt64Type, value)) { return true; } @@ -130,45 +132,45 @@ bool ConfigImplApple::ReadImpl(const std::string& key, int64_t* value) { } bool ConfigImplApple::WriteImpl(const std::string& key, std::string_view value) { - gurl_base::apple::ScopedCFTypeRef obj(CFStringCreateWithBytes( + ScopedCFTypeRef obj(CFStringCreateWithBytes( kCFAllocatorDefault, reinterpret_cast(value.data()), value.size(), kCFStringEncodingUTF8, FALSE)); - CFDictionarySetValue(write_root_, gurl_base::SysUTF8ToCFStringRef(key).get(), obj); + CFDictionarySetValue(write_root_, SysUTF8ToCFStringRef(key).get(), obj); return true; } bool ConfigImplApple::WriteImpl(const std::string& key, bool value) { - gurl_base::apple::ScopedCFTypeRef obj(value ? kCFBooleanTrue : kCFBooleanFalse); - CFDictionarySetValue(write_root_, gurl_base::SysUTF8ToCFStringRef(key).get(), obj); + ScopedCFTypeRef obj(value ? kCFBooleanTrue : kCFBooleanFalse); + CFDictionarySetValue(write_root_, SysUTF8ToCFStringRef(key).get(), obj); return true; } bool ConfigImplApple::WriteImpl(const std::string& key, uint32_t value) { - gurl_base::apple::ScopedCFTypeRef obj(CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value)); - CFDictionarySetValue(write_root_, gurl_base::SysUTF8ToCFStringRef(key).get(), obj); + ScopedCFTypeRef obj(CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value)); + CFDictionarySetValue(write_root_, SysUTF8ToCFStringRef(key).get(), obj); return true; } bool ConfigImplApple::WriteImpl(const std::string& key, int32_t value) { - gurl_base::apple::ScopedCFTypeRef obj(CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value)); - CFDictionarySetValue(write_root_, gurl_base::SysUTF8ToCFStringRef(key).get(), obj); + ScopedCFTypeRef obj(CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value)); + CFDictionarySetValue(write_root_, SysUTF8ToCFStringRef(key).get(), obj); return true; } bool ConfigImplApple::WriteImpl(const std::string& key, uint64_t value) { - gurl_base::apple::ScopedCFTypeRef obj(CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &value)); - CFDictionarySetValue(write_root_, gurl_base::SysUTF8ToCFStringRef(key).get(), obj); + ScopedCFTypeRef obj(CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &value)); + CFDictionarySetValue(write_root_, SysUTF8ToCFStringRef(key).get(), obj); return true; } bool ConfigImplApple::WriteImpl(const std::string& key, int64_t value) { - gurl_base::apple::ScopedCFTypeRef obj(CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &value)); - CFDictionarySetValue(write_root_, gurl_base::SysUTF8ToCFStringRef(key).get(), obj); + ScopedCFTypeRef obj(CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &value)); + CFDictionarySetValue(write_root_, SysUTF8ToCFStringRef(key).get(), obj); return true; } bool ConfigImplApple::DeleteImpl(const std::string& key) { - if (CFDictionaryGetValueIfPresent(write_root_, gurl_base::SysUTF8ToCFStringRef(key).get(), nullptr)) { - CFDictionaryRemoveValue(write_root_, gurl_base::SysUTF8ToCFStringRef(key).get()); + if (CFDictionaryGetValueIfPresent(write_root_, SysUTF8ToCFStringRef(key).get(), nullptr)) { + CFDictionaryRemoveValue(write_root_, SysUTF8ToCFStringRef(key).get()); return true; } return false; diff --git a/yass/src/core/logging.cpp b/yass/src/core/logging.cpp index 9027d0aad9..df9c8f0cbb 100644 --- a/yass/src/core/logging.cpp +++ b/yass/src/core/logging.cpp @@ -990,8 +990,7 @@ inline void LogDestination::MaybeLogToStderr(LogSeverity severity, // CF-1153.18/CFUtilities.c __CFLogCString(). CFBundleRef main_bundle = CFBundleGetMainBundle(); CFStringRef main_bundle_id_cf = main_bundle ? CFBundleGetIdentifier(main_bundle) : nullptr; - std::string main_bundle_id = - main_bundle_id_cf ? gurl_base::SysCFStringRefToUTF8(main_bundle_id_cf) : std::string(""); + std::string main_bundle_id = main_bundle_id_cf ? SysCFStringRefToUTF8(main_bundle_id_cf) : std::string(""); #if defined(USE_ASL) // The facility is set to the main bundle ID if available. Otherwise, // "com.apple.console" is used. diff --git a/yass/src/core/utils.hpp b/yass/src/core/utils.hpp index 4dc8bb7631..8ae09620ee 100644 --- a/yass/src/core/utils.hpp +++ b/yass/src/core/utils.hpp @@ -96,6 +96,35 @@ using gurl_base::SysMultiByteToWide; using gurl_base::SysWideToMultiByte; #endif // _WIN32 +// Apple-specific ------------------------------------------------------------ +#if BUILDFLAG(IS_APPLE) + +// Converts between strings and CFStringRefs/NSStrings. + +// Converts a string to a CFStringRef. Returns null on failure. +using gurl_base::SysUTF16ToCFStringRef; +using gurl_base::SysUTF8ToCFStringRef; + +// Converts a CFStringRef to a string. Returns an empty string on failure. It is +// not valid to call these with a null `ref`. +using gurl_base::SysCFStringRefToUTF16; +using gurl_base::SysCFStringRefToUTF8; + +#ifdef __OBJC__ + +// Converts a string to an autoreleased NSString. Returns nil on failure. +using gurl_base::SysUTF16ToNSString; +using gurl_base::SysUTF8ToNSString; + +// Converts an NSString to a string. Returns an empty string on failure or if +// `ref` is nil. +using gurl_base::SysNSStringToUTF16; +using gurl_base::SysNSStringToUTF8; + +#endif // __OBJC__ + +#endif // BUILDFLAG(IS_APPLE) + extern const char kSeparators[]; // A portable interface that returns the dirname of the filename passed as an diff --git a/yass/src/core/utils_mac.mm b/yass/src/core/utils_mac.mm index e58ad3ec55..741672240e 100644 --- a/yass/src/core/utils_mac.mm +++ b/yass/src/core/utils_mac.mm @@ -116,14 +116,14 @@ bool GetTempDir(std::string *path) { if (tmp == nil) { return false; } - *path = gurl_base::SysNSStringToUTF8(tmp); + *path = SysNSStringToUTF8(tmp); return true; } std::string GetHomeDir() { NSString* tmp = NSHomeDirectory(); if (tmp != nil) { - auto path = gurl_base::SysNSStringToUTF8(tmp); + auto path = SysNSStringToUTF8(tmp); if (!path.empty()) { return path; } diff --git a/yass/src/ios/YassAppDelegate.mm b/yass/src/ios/YassAppDelegate.mm index 1884b2e646..76539e918b 100644 --- a/yass/src/ios/YassAppDelegate.mm +++ b/yass/src/ios/YassAppDelegate.mm @@ -133,20 +133,20 @@ - (NSString*)getStatus { std::ostringstream ss; if (state_ == STARTED) { - ss << gurl_base::SysNSStringToUTF8(status_) << ":"; + ss << SysNSStringToUTF8(status_) << ":"; } else if (state_ == STARTING) { - ss << gurl_base::SysNSStringToUTF8(NSLocalizedString(@"CONNECTING", @"Connecting")); + ss << SysNSStringToUTF8(NSLocalizedString(@"CONNECTING", @"Connecting")); } else if (state_ == START_FAILED) { NSString* prefixMessage = NSLocalizedString(@"FAILED_TO_CONNECT_DUE_TO", @"Failed to connect due to "); - ss << gurl_base::SysNSStringToUTF8(prefixMessage) << error_msg_.c_str(); + ss << SysNSStringToUTF8(prefixMessage) << error_msg_.c_str(); } else if (state_ == STOPPING) { - ss << gurl_base::SysNSStringToUTF8(NSLocalizedString(@"DISCONNECTING", @"Disconnecting")); + ss << SysNSStringToUTF8(NSLocalizedString(@"DISCONNECTING", @"Disconnecting")); } else { NSString* prefixMessage = NSLocalizedString(@"DISCONNECTED_WITH", @"Disconnected with "); - ss << gurl_base::SysNSStringToUTF8(prefixMessage) << absl::GetFlag(FLAGS_server_host); + ss << SysNSStringToUTF8(prefixMessage) << absl::GetFlag(FLAGS_server_host); } - return gurl_base::SysUTF8ToNSString(ss.str()); + return SysUTF8ToNSString(ss.str()); } - (void)OnStart:(BOOL)quiet { @@ -154,7 +154,7 @@ if (!connectedToNetwork()) { NSString* message = NSLocalizedString(@"NETWORK_UNREACHABLE", @"Network unreachable"); - [self OnStartFailed:gurl_base::SysNSStringToUTF8(message)]; + [self OnStartFailed:SysNSStringToUTF8(message)]; return; } auto err_msg = [self SaveConfig]; @@ -218,7 +218,7 @@ }); }]; if (error) { - std::string err_msg = gurl_base::SysNSStringToUTF8([error localizedDescription]); + std::string err_msg = SysNSStringToUTF8([error localizedDescription]); LOG(WARNING) << "telemetry: send Request failed: " << err_msg; } } @@ -354,7 +354,7 @@ } - (void)OnStartFailedWithNSError:(NSError* _Nullable)error { - std::string err_msg = gurl_base::SysNSStringToUTF8([error localizedDescription]); + std::string err_msg = SysNSStringToUTF8([error localizedDescription]); [self OnStartFailed:err_msg]; } @@ -405,14 +405,14 @@ dot_host_ = viewController.dotHost.text; connect_timeout_ = viewController.timeout.text; - auto server_host = gurl_base::SysNSStringToUTF8(server_host_); - auto server_port = gurl_base::SysNSStringToUTF8(server_port_); - auto username = gurl_base::SysNSStringToUTF8(username_); - auto password = gurl_base::SysNSStringToUTF8(password_); - auto method_string = gurl_base::SysNSStringToUTF8(method_string_); - auto doh_url = gurl_base::SysNSStringToUTF8(doh_url_); - auto dot_host = gurl_base::SysNSStringToUTF8(dot_host_); - auto connect_timeout = gurl_base::SysNSStringToUTF8(connect_timeout_); + auto server_host = SysNSStringToUTF8(server_host_); + auto server_port = SysNSStringToUTF8(server_port_); + auto username = SysNSStringToUTF8(username_); + auto password = SysNSStringToUTF8(password_); + auto method_string = SysNSStringToUTF8(method_string_); + auto doh_url = SysNSStringToUTF8(doh_url_); + auto dot_host = SysNSStringToUTF8(dot_host_); + auto connect_timeout = SysNSStringToUTF8(connect_timeout_); return config::ReadConfigFromArgument(server_host, "" /*server_sni*/, server_port, username, password, method_string, "127.0.0.1", "0", doh_url, dot_host, connect_timeout); diff --git a/yass/src/ios/YassViewController.mm b/yass/src/ios/YassViewController.mm index 7c9a7751ac..6fd7d81ef1 100644 --- a/yass/src/ios/YassViewController.mm +++ b/yass/src/ios/YassViewController.mm @@ -126,7 +126,7 @@ } } if (textField == self.serverPort) { - auto port = StringToInteger(gurl_base::SysNSStringToUTF8(textField.text)); + auto port = StringToInteger(SysNSStringToUTF8(textField.text)); return port.has_value() && port.value() > 0 && port.value() < 65536 ? YES : NO; } if (textField == self.dohURL && [textField.text length]) { @@ -139,7 +139,7 @@ } } if (textField == self.timeout) { - auto port = StringToInteger(gurl_base::SysNSStringToUTF8(textField.text)); + auto port = StringToInteger(SysNSStringToUTF8(textField.text)); return port.has_value() && port.value() >= 0 ? YES : NO; } return YES; @@ -268,17 +268,17 @@ std::ostringstream ss; NSString* message = [appDelegate getStatus]; - ss << gurl_base::SysNSStringToUTF8(message); + ss << SysNSStringToUTF8(message); message = NSLocalizedString(@"TXRATE", @" tx rate: "); - ss << gurl_base::SysNSStringToUTF8(message); + ss << SysNSStringToUTF8(message); HumanReadableByteCountBin(&ss, rx_rate_); ss << "/s"; message = NSLocalizedString(@"RXRATE", @" rx rate: "); - ss << gurl_base::SysNSStringToUTF8(message); + ss << SysNSStringToUTF8(message); HumanReadableByteCountBin(&ss, tx_rate_); ss << "/s"; - return gurl_base::SysUTF8ToNSString(ss.str()); + return SysUTF8ToNSString(ss.str()); } - (void)UpdateStatusBar { @@ -286,21 +286,21 @@ } - (void)LoadChanges { - self.serverHost.text = gurl_base::SysUTF8ToNSString(absl::GetFlag(FLAGS_server_host)); - self.serverPort.text = gurl_base::SysUTF8ToNSString(std::to_string(absl::GetFlag(FLAGS_server_port))); - self.username.text = gurl_base::SysUTF8ToNSString(absl::GetFlag(FLAGS_username)); - self.password.text = gurl_base::SysUTF8ToNSString(absl::GetFlag(FLAGS_password)); + self.serverHost.text = SysUTF8ToNSString(absl::GetFlag(FLAGS_server_host)); + self.serverPort.text = SysUTF8ToNSString(std::to_string(absl::GetFlag(FLAGS_server_port))); + self.username.text = SysUTF8ToNSString(absl::GetFlag(FLAGS_username)); + self.password.text = SysUTF8ToNSString(absl::GetFlag(FLAGS_password)); auto cipherMethod = absl::GetFlag(FLAGS_method).method; - NSUInteger row = [cipher_methods_ indexOfObject:gurl_base::SysUTF8ToNSString(to_cipher_method_str(cipherMethod))]; + NSUInteger row = [cipher_methods_ indexOfObject:SysUTF8ToNSString(to_cipher_method_str(cipherMethod))]; if (row != NSNotFound) { - self.currentCiphermethod = gurl_base::SysUTF8ToNSString(to_cipher_method_str(cipherMethod)); + self.currentCiphermethod = SysUTF8ToNSString(to_cipher_method_str(cipherMethod)); [self.cipherMethod selectRow:row inComponent:0 animated:NO]; } - self.dohURL.text = gurl_base::SysUTF8ToNSString(absl::GetFlag(FLAGS_doh_url)); - self.dotHost.text = gurl_base::SysUTF8ToNSString(absl::GetFlag(FLAGS_dot_host)); - self.timeout.text = gurl_base::SysUTF8ToNSString(std::to_string(absl::GetFlag(FLAGS_connect_timeout))); + self.dohURL.text = SysUTF8ToNSString(absl::GetFlag(FLAGS_doh_url)); + self.dotHost.text = SysUTF8ToNSString(absl::GetFlag(FLAGS_dot_host)); + self.timeout.text = SysUTF8ToNSString(std::to_string(absl::GetFlag(FLAGS_connect_timeout))); } @end diff --git a/yass/src/ios/extensions/YassPacketTunnelProvider.mm b/yass/src/ios/extensions/YassPacketTunnelProvider.mm index a60438360e..760d5ba0b5 100644 --- a/yass/src/ios/extensions/YassPacketTunnelProvider.mm +++ b/yass/src/ios/extensions/YassPacketTunnelProvider.mm @@ -40,16 +40,16 @@ static constexpr const uint32_t kYieldConcurrencyOfConnections = 12u; NETunnelProviderProtocol* protocolConfiguration = (NETunnelProviderProtocol*)self.protocolConfiguration; NSDictionary* dict = protocolConfiguration.providerConfiguration; - auto server_host = gurl_base::SysNSStringToUTF8(dict[@(kServerHostFieldName)]); - auto server_port = gurl_base::SysNSStringToUTF8(dict[@(kServerPortFieldName)]); - auto username = gurl_base::SysNSStringToUTF8(dict[@(kUsernameFieldName)]); - auto password = gurl_base::SysNSStringToUTF8(dict[@(kPasswordFieldName)]); + auto server_host = SysNSStringToUTF8(dict[@(kServerHostFieldName)]); + auto server_port = SysNSStringToUTF8(dict[@(kServerPortFieldName)]); + auto username = SysNSStringToUTF8(dict[@(kUsernameFieldName)]); + auto password = SysNSStringToUTF8(dict[@(kPasswordFieldName)]); auto local_host = std::string("127.0.0.1"); auto local_port = std::string("0"); - auto method_string = gurl_base::SysNSStringToUTF8(dict[@(kMethodStringFieldName)]); - auto doh_url = gurl_base::SysNSStringToUTF8(dict[@(kDoHURLFieldName)]); - auto dot_host = gurl_base::SysNSStringToUTF8(dict[@(kDoTHostFieldName)]); - auto connect_timeout = gurl_base::SysNSStringToUTF8(dict[@(kConnectTimeoutFieldName)]); + auto method_string = SysNSStringToUTF8(dict[@(kMethodStringFieldName)]); + auto doh_url = SysNSStringToUTF8(dict[@(kDoHURLFieldName)]); + auto dot_host = SysNSStringToUTF8(dict[@(kDoTHostFieldName)]); + auto connect_timeout = SysNSStringToUTF8(dict[@(kConnectTimeoutFieldName)]); auto err_msg = config::ReadConfigFromArgument(server_host, "" /*server_sni*/, server_port, username, password, method_string, @@ -115,7 +115,7 @@ static constexpr const uint32_t kYieldConcurrencyOfConnections = 12u; return; } - std::string proxy_url = absl::StrFormat("socks5://%s:%d", gurl_base::SysNSStringToUTF8(local_host), local_port); + std::string proxy_url = absl::StrFormat("socks5://%s:%d", SysNSStringToUTF8(local_host), local_port); context_ = Tun2Proxy_Init(self.packetFlow, proxy_url, DEFAULT_MTU, 0, true); if (!context_) { diff --git a/yass/src/mac/YassAppDelegate.mm b/yass/src/mac/YassAppDelegate.mm index 40a38f3d03..d594dd6963 100644 --- a/yass/src/mac/YassAppDelegate.mm +++ b/yass/src/mac/YassAppDelegate.mm @@ -94,20 +94,20 @@ std::ostringstream ss; if (state_ == STARTED) { NSString* prefixMessage = NSLocalizedString(@"CONNECTED", @"Connected"); - ss << gurl_base::SysNSStringToUTF8(prefixMessage) << ":"; + ss << SysNSStringToUTF8(prefixMessage) << ":"; } else if (state_ == STARTING) { - ss << gurl_base::SysNSStringToUTF8(NSLocalizedString(@"CONNECTING", @"Connecting")); + ss << SysNSStringToUTF8(NSLocalizedString(@"CONNECTING", @"Connecting")); } else if (state_ == START_FAILED) { NSString* prefixMessage = NSLocalizedString(@"FAILED_TO_CONNECT_DUE_TO", @"Failed to connect due to "); - ss << gurl_base::SysNSStringToUTF8(prefixMessage) << error_msg_.c_str(); + ss << SysNSStringToUTF8(prefixMessage) << error_msg_.c_str(); } else if (state_ == STOPPING) { - ss << gurl_base::SysNSStringToUTF8(NSLocalizedString(@"DISCONNECTING", @"Disconnecting")); + ss << SysNSStringToUTF8(NSLocalizedString(@"DISCONNECTING", @"Disconnecting")); } else { NSString* prefixMessage = NSLocalizedString(@"DISCONNECTED_WITH", @"Disconnected with "); - ss << gurl_base::SysNSStringToUTF8(prefixMessage) << worker_.GetRemoteDomain(); + ss << SysNSStringToUTF8(prefixMessage) << worker_.GetRemoteDomain(); } - return gurl_base::SysUTF8ToNSString(ss.str()); + return SysUTF8ToNSString(ss.str()); } - (void)OnStart { @@ -189,17 +189,17 @@ - (std::string)SaveConfig { YassViewController* viewController = (YassViewController*)NSApplication.sharedApplication.mainWindow.contentViewController; - auto server_host = gurl_base::SysNSStringToUTF8(viewController.serverHost.stringValue); - auto server_sni = gurl_base::SysNSStringToUTF8(viewController.serverSNI.stringValue); - auto server_port = gurl_base::SysNSStringToUTF8(viewController.serverPort.stringValue); - auto username = gurl_base::SysNSStringToUTF8(viewController.username.stringValue); - auto password = gurl_base::SysNSStringToUTF8(viewController.password.stringValue); - auto method_string = gurl_base::SysNSStringToUTF8(viewController.cipherMethod.stringValue); - auto local_host = gurl_base::SysNSStringToUTF8(viewController.localHost.stringValue); - auto local_port = gurl_base::SysNSStringToUTF8(viewController.localPort.stringValue); - auto doh_url = gurl_base::SysNSStringToUTF8(viewController.dohURL.stringValue); - auto dot_host = gurl_base::SysNSStringToUTF8(viewController.dotHost.stringValue); - auto connect_timeout = gurl_base::SysNSStringToUTF8(viewController.timeout.stringValue); + auto server_host = SysNSStringToUTF8(viewController.serverHost.stringValue); + auto server_sni = SysNSStringToUTF8(viewController.serverSNI.stringValue); + auto server_port = SysNSStringToUTF8(viewController.serverPort.stringValue); + auto username = SysNSStringToUTF8(viewController.username.stringValue); + auto password = SysNSStringToUTF8(viewController.password.stringValue); + auto method_string = SysNSStringToUTF8(viewController.cipherMethod.stringValue); + auto local_host = SysNSStringToUTF8(viewController.localHost.stringValue); + auto local_port = SysNSStringToUTF8(viewController.localPort.stringValue); + auto doh_url = SysNSStringToUTF8(viewController.dohURL.stringValue); + auto dot_host = SysNSStringToUTF8(viewController.dotHost.stringValue); + auto connect_timeout = SysNSStringToUTF8(viewController.timeout.stringValue); return config::ReadConfigFromArgument(server_host, server_sni, server_port, username, password, method_string, local_host, local_port, doh_url, dot_host, connect_timeout); diff --git a/yass/src/mac/YassViewController.mm b/yass/src/mac/YassViewController.mm index 298c3117ad..1213e4703c 100644 --- a/yass/src/mac/YassViewController.mm +++ b/yass/src/mac/YassViewController.mm @@ -144,17 +144,17 @@ } - (void)LoadChanges { - self.serverHost.stringValue = gurl_base::SysUTF8ToNSString(absl::GetFlag(FLAGS_server_host)); - self.serverSNI.stringValue = gurl_base::SysUTF8ToNSString(absl::GetFlag(FLAGS_server_sni)); + self.serverHost.stringValue = SysUTF8ToNSString(absl::GetFlag(FLAGS_server_host)); + self.serverSNI.stringValue = SysUTF8ToNSString(absl::GetFlag(FLAGS_server_sni)); self.serverPort.intValue = absl::GetFlag(FLAGS_server_port); - self.username.stringValue = gurl_base::SysUTF8ToNSString(absl::GetFlag(FLAGS_username)); - self.password.stringValue = gurl_base::SysUTF8ToNSString(absl::GetFlag(FLAGS_password)); + self.username.stringValue = SysUTF8ToNSString(absl::GetFlag(FLAGS_username)); + self.password.stringValue = SysUTF8ToNSString(absl::GetFlag(FLAGS_password)); auto cipherMethod = absl::GetFlag(FLAGS_method).method; - self.cipherMethod.stringValue = gurl_base::SysUTF8ToNSString(to_cipher_method_str(cipherMethod)); - self.localHost.stringValue = gurl_base::SysUTF8ToNSString(absl::GetFlag(FLAGS_local_host)); + self.cipherMethod.stringValue = SysUTF8ToNSString(to_cipher_method_str(cipherMethod)); + self.localHost.stringValue = SysUTF8ToNSString(absl::GetFlag(FLAGS_local_host)); self.localPort.intValue = absl::GetFlag(FLAGS_local_port); - self.dohURL.stringValue = gurl_base::SysUTF8ToNSString(absl::GetFlag(FLAGS_doh_url)); - self.dotHost.stringValue = gurl_base::SysUTF8ToNSString(absl::GetFlag(FLAGS_dot_host)); + self.dohURL.stringValue = SysUTF8ToNSString(absl::GetFlag(FLAGS_doh_url)); + self.dotHost.stringValue = SysUTF8ToNSString(absl::GetFlag(FLAGS_dot_host)); self.timeout.intValue = absl::GetFlag(FLAGS_connect_timeout); } diff --git a/yass/src/mac/YassWindowController.mm b/yass/src/mac/YassWindowController.mm index 0f1b7a152f..8c951dd950 100644 --- a/yass/src/mac/YassWindowController.mm +++ b/yass/src/mac/YassWindowController.mm @@ -101,17 +101,17 @@ std::ostringstream ss; NSString* message = [appDelegate getStatus]; - ss << gurl_base::SysNSStringToUTF8(message); + ss << SysNSStringToUTF8(message); message = NSLocalizedString(@"TXRATE", @" tx rate: "); - ss << gurl_base::SysNSStringToUTF8(message); + ss << SysNSStringToUTF8(message); HumanReadableByteCountBin(&ss, rx_rate_); ss << "/s"; message = NSLocalizedString(@"RXRATE", @" rx rate: "); - ss << gurl_base::SysNSStringToUTF8(message); + ss << SysNSStringToUTF8(message); HumanReadableByteCountBin(&ss, tx_rate_); ss << "/s"; - return gurl_base::SysUTF8ToNSString(ss.str()); + return SysUTF8ToNSString(ss.str()); } - (void)refreshTimerExceeded { diff --git a/yass/src/mac/utils_mac.mm b/yass/src/mac/utils_mac.mm index 45214f341e..4b7ff15013 100644 --- a/yass/src/mac/utils_mac.mm +++ b/yass/src/mac/utils_mac.mm @@ -29,6 +29,8 @@ #include #include +using namespace gurl_base::apple; + namespace { class LoginItemsFileList { @@ -60,10 +62,10 @@ class LoginItemsFileList { // representing the specified bundle. If such an item is found, returns a // retained reference to it. Caller is responsible for releasing the // reference. - gurl_base::apple::ScopedCFTypeRef GetLoginItemForApp(NSURL* url) { + ScopedCFTypeRef GetLoginItemForApp(NSURL* url) { DCHECK(login_items_.get()) << "Initialize() failed or not called."; - gurl_base::apple::ScopedCFTypeRef cfurl((CFURLRef)(CFBridgingRetain(url))); + ScopedCFTypeRef cfurl((CFURLRef)(CFBridgingRetain(url))); #pragma clang diagnostic push // https://crbug.com/1154377 #pragma clang diagnostic ignored "-Wdeprecated-declarations" @@ -77,31 +79,31 @@ class LoginItemsFileList { // kLSSharedFileListDoNotMountVolumes is used so that we don't trigger // mounting when it's not expected by a user. Just listing the login // items should not cause any side-effects. - gurl_base::apple::ScopedCFTypeRef item_url( + ScopedCFTypeRef item_url( LSSharedFileListItemCopyResolvedURL(item, kLSSharedFileListDoNotMountVolumes, nullptr)); #pragma clang diagnostic pop if (item_url && CFEqual(item_url.get(), cfurl.get())) { - return gurl_base::apple::ScopedCFTypeRef(item, gurl_base::scoped_policy::RETAIN); + return ScopedCFTypeRef(item, gurl_base::scoped_policy::RETAIN); } } - return gurl_base::apple::ScopedCFTypeRef(); + return ScopedCFTypeRef(); } - gurl_base::apple::ScopedCFTypeRef GetLoginItemForMainApp() { + ScopedCFTypeRef GetLoginItemForMainApp() { NSURL* url = [NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]]; return GetLoginItemForApp(url); } private: - gurl_base::apple::ScopedCFTypeRef login_items_; + ScopedCFTypeRef login_items_; }; bool IsHiddenLoginItem(LSSharedFileListItemRef item) { #pragma clang diagnostic push // https://crbug.com/1154377 #pragma clang diagnostic ignored "-Wdeprecated-declarations" - gurl_base::apple::ScopedCFTypeRef hidden(reinterpret_cast( + ScopedCFTypeRef hidden(reinterpret_cast( LSSharedFileListItemCopyProperty(item, reinterpret_cast(kLSSharedFileListLoginItemHidden)))); #pragma clang diagnostic pop @@ -115,7 +117,7 @@ bool CheckLoginItemStatus(bool* is_hidden) { if (!login_items.Initialize()) return false; - gurl_base::apple::ScopedCFTypeRef item(login_items.GetLoginItemForMainApp()); + ScopedCFTypeRef item(login_items.GetLoginItemForMainApp()); if (!item.get()) return false; @@ -127,7 +129,7 @@ bool CheckLoginItemStatus(bool* is_hidden) { void AddToLoginItems(bool hide_on_startup) { NSBundle* bundle = [NSBundle mainBundle]; - AddToLoginItems(gurl_base::SysNSStringToUTF8([bundle bundlePath]), hide_on_startup); + AddToLoginItems(SysNSStringToUTF8([bundle bundlePath]), hide_on_startup); } void AddToLoginItems(const std::string& app_bundle_file_path, bool hide_on_startup) { @@ -137,9 +139,9 @@ void AddToLoginItems(const std::string& app_bundle_file_path, bool hide_on_start NSURL* app_bundle_url = [NSURL fileURLWithPath:@(app_bundle_file_path.c_str()) isDirectory:TRUE]; - gurl_base::apple::ScopedCFTypeRef cf_app_bundle_url((CFURLRef)CFBridgingRetain(app_bundle_url)); + ScopedCFTypeRef cf_app_bundle_url((CFURLRef)CFBridgingRetain(app_bundle_url)); - gurl_base::apple::ScopedCFTypeRef item(login_items.GetLoginItemForApp(app_bundle_url)); + ScopedCFTypeRef item(login_items.GetLoginItemForApp(app_bundle_url)); if (item.get() && (IsHiddenLoginItem(item) == hide_on_startup)) { return; // Already is a login item with required hide flag. @@ -157,9 +159,9 @@ void AddToLoginItems(const std::string& app_bundle_file_path, bool hide_on_start #pragma clang diagnostic ignored "-Wdeprecated-declarations" BOOL hide = hide_on_startup ? YES : NO; NSDictionary* properties = @{CFBridgingRelease(CFRetain(kLSSharedFileListLoginItemHidden)) : @(hide)}; - gurl_base::apple::ScopedCFTypeRef cfproperties((CFDictionaryRef)CFBridgingRetain(properties)); + ScopedCFTypeRef cfproperties((CFDictionaryRef)CFBridgingRetain(properties)); - gurl_base::apple::ScopedCFTypeRef new_item( + ScopedCFTypeRef new_item( LSSharedFileListInsertItemURL(login_items.GetLoginFileList(), kLSSharedFileListItemLast, nullptr, nullptr, cf_app_bundle_url, cfproperties, nullptr)); #pragma clang diagnostic pop @@ -173,7 +175,7 @@ void AddToLoginItems(const std::string& app_bundle_file_path, bool hide_on_start void RemoveFromLoginItems() { NSBundle* bundle = [NSBundle mainBundle]; - RemoveFromLoginItems(gurl_base::SysNSStringToUTF8([bundle bundlePath])); + RemoveFromLoginItems(SysNSStringToUTF8([bundle bundlePath])); } void RemoveFromLoginItems(const std::string& app_bundle_file_path) { @@ -182,7 +184,7 @@ void RemoveFromLoginItems(const std::string& app_bundle_file_path) { return; NSURL* app_bundle_url = [NSURL fileURLWithPath:@(app_bundle_file_path.c_str())]; - gurl_base::apple::ScopedCFTypeRef item(login_items.GetLoginItemForApp(app_bundle_url)); + ScopedCFTypeRef item(login_items.GetLoginItemForApp(app_bundle_url)); if (!item.get()) return; @@ -226,7 +228,7 @@ bool WasLaunchedAsLoginItemRestoreState() { CFStringRef app = CFSTR("com.apple.loginwindow"); CFStringRef save_state = CFSTR("TALLogoutSavesState"); - gurl_base::apple::ScopedCFTypeRef plist(CFPreferencesCopyAppValue(save_state, app)); + ScopedCFTypeRef plist(CFPreferencesCopyAppValue(save_state, app)); // According to documentation, com.apple.loginwindow.plist does not exist on a // fresh installation until the user changes a login window setting. The // "reopen windows" option is checked by default, so the plist would exist had @@ -249,7 +251,7 @@ bool WasLaunchedAsHiddenLoginItem() { if (!login_items.Initialize()) return false; - gurl_base::apple::ScopedCFTypeRef item(login_items.GetLoginItemForMainApp()); + ScopedCFTypeRef item(login_items.GetLoginItemForMainApp()); if (!item.get()) { // OS X can launch items for the resume feature. return false; @@ -373,7 +375,7 @@ std::string GetModelIdentifier() { gurl_base::mac::ScopedIOObject platform_expert( IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice"))); if (platform_expert) { - gurl_base::apple::ScopedCFTypeRef model_data(static_cast( + ScopedCFTypeRef model_data(static_cast( IORegistryEntryCreateCFProperty(platform_expert, CFSTR("model"), kCFAllocatorDefault, 0))); if (model_data) { return_string = reinterpret_cast(CFDataGetBytePtr(model_data)); @@ -405,7 +407,7 @@ std::string GetOSDisplayName() { os_name = "OS X"; else os_name = "macOS"; - std::string version_string = gurl_base::SysNSStringToUTF8([[NSProcessInfo processInfo] operatingSystemVersionString]); + std::string version_string = SysNSStringToUTF8([[NSProcessInfo processInfo] operatingSystemVersionString]); return os_name + " " + version_string; } @@ -417,7 +419,7 @@ std::string GetPlatformSerialNumber() { return std::string(); } - gurl_base::apple::ScopedCFTypeRef serial_number( + ScopedCFTypeRef serial_number( IORegistryEntryCreateCFProperty(expert_device, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0)); CFStringRef serial_number_cfstring = CFCast(serial_number); if (!serial_number_cfstring) { @@ -425,7 +427,7 @@ std::string GetPlatformSerialNumber() { return std::string(); } - return gurl_base::SysCFStringRefToUTF8(serial_number_cfstring); + return SysCFStringRefToUTF8(serial_number_cfstring); } static std::string GetLocalAddr() { @@ -476,7 +478,7 @@ bool QuerySystemProxy(bool* enabled, std::string* server_addr, int32_t* server_p *server_port = 0; bypass_addr->clear(); - gurl_base::apple::ScopedCFTypeRef proxies{SCDynamicStoreCopyProxies(nullptr)}; + ScopedCFTypeRef proxies{SCDynamicStoreCopyProxies(nullptr)}; { CFNumberRef obj; @@ -490,7 +492,7 @@ bool QuerySystemProxy(bool* enabled, std::string* server_addr, int32_t* server_p CFStringRef obj; if (CFDictionaryGetValueIfPresent(proxies, kSCPropNetProxiesHTTPProxy, (const void**)&obj) && CFGetTypeID(obj) == CFStringGetTypeID()) { - *server_addr = gurl_base::SysCFStringRefToUTF8(obj); + *server_addr = SysCFStringRefToUTF8(obj); LOG(INFO) << "QuerySystemProxy: server_addr " << *server_addr; } } @@ -510,7 +512,7 @@ bool QuerySystemProxy(bool* enabled, std::string* server_addr, int32_t* server_p CFIndex array_count = CFArrayGetCount(obj); for (CFIndex i = 0; i < array_count; ++i) { CFStringRef str_obj = (CFStringRef)CFArrayGetValueAtIndex(obj, i); - *bypass_addr = *bypass_addr + gurl_base::SysCFStringRefToUTF8(str_obj) + ", "; + *bypass_addr = *bypass_addr + SysCFStringRefToUTF8(str_obj) + ", "; } if (array_count) { bypass_addr->resize(bypass_addr->size() - 2); @@ -585,6 +587,6 @@ void SetDockIconStyle(bool hidden) { } if (err != noErr) { NSError* error = [NSError errorWithDomain:NSOSStatusErrorDomain code:err userInfo:nil]; - LOG(WARNING) << "SetDockIconStyle failed: " << gurl_base::SysNSStringToUTF8([error localizedDescription]); + LOG(WARNING) << "SetDockIconStyle failed: " << SysNSStringToUTF8([error localizedDescription]); } } diff --git a/yass/src/net/asio_ssl.cpp b/yass/src/net/asio_ssl.cpp index 121ac4c551..55ef37472d 100644 --- a/yass/src/net/asio_ssl.cpp +++ b/yass/src/net/asio_ssl.cpp @@ -66,6 +66,8 @@ std::ostream& operator<<(std::ostream& o, asio::error_code ec) { #if BUILDFLAG(IS_MAC) +using namespace gurl_base::apple; + namespace { // The rules for interpreting trust settings are documented at: @@ -117,15 +119,14 @@ TrustStatus IsTrustDictionaryTrustedForPolicy(CFDictionaryRef trust_dict, // |target_policy_oid|. If there is no kSecTrustSettingsPolicy key, it's // considered a match for all policies. if (CFDictionaryContainsKey(trust_dict, kSecTrustSettingsPolicy)) { - SecPolicyRef policy_ref = - gurl_base::apple::GetValueFromDictionary(trust_dict, kSecTrustSettingsPolicy); + SecPolicyRef policy_ref = GetValueFromDictionary(trust_dict, kSecTrustSettingsPolicy); if (!policy_ref) { return TrustStatus::UNSPECIFIED; } - gurl_base::apple::ScopedCFTypeRef policy_dict(SecPolicyCopyProperties(policy_ref)); + ScopedCFTypeRef policy_dict(SecPolicyCopyProperties(policy_ref)); // kSecPolicyOid is guaranteed to be present in the policy dictionary. - CFStringRef policy_oid = gurl_base::apple::GetValueFromDictionary(policy_dict.get(), kSecPolicyOid); + CFStringRef policy_oid = GetValueFromDictionary(policy_dict.get(), kSecPolicyOid); if (!CFEqual(policy_oid, target_policy_oid)) return TrustStatus::UNSPECIFIED; @@ -135,8 +136,7 @@ TrustStatus IsTrustDictionaryTrustedForPolicy(CFDictionaryRef trust_dict, // kSecTrustSettingsResultTrustRoot is assumed. int trust_settings_result = kSecTrustSettingsResultTrustRoot; if (CFDictionaryContainsKey(trust_dict, kSecTrustSettingsResult)) { - CFNumberRef trust_settings_result_ref = - gurl_base::apple::GetValueFromDictionary(trust_dict, kSecTrustSettingsResult); + CFNumberRef trust_settings_result_ref = GetValueFromDictionary(trust_dict, kSecTrustSettingsResult); if (!trust_settings_result_ref || !CFNumberGetValue(trust_settings_result_ref, kCFNumberIntType, &trust_settings_result)) { return TrustStatus::UNSPECIFIED; @@ -199,7 +199,7 @@ TrustStatus IsCertificateTrustedForPolicy(const bssl::ParsedCertificate* cert, // Evaluate user trust domain, then admin. User settings can override // admin (and both override the system domain, but we don't check that). for (const auto& trust_domain : {kSecTrustSettingsDomainUser, kSecTrustSettingsDomainAdmin}) { - gurl_base::apple::ScopedCFTypeRef trust_settings; + ScopedCFTypeRef trust_settings; OSStatus err; err = SecTrustSettingsCopyTrustSettings(cert_handle, trust_domain, trust_settings.InitializeInto()); if (err != errSecSuccess) { @@ -570,7 +570,7 @@ out: size = CFArrayGetCount(certs); for (CFIndex i = 0; i < size; ++i) { SecCertificateRef sec_cert = (SecCertificateRef)CFArrayGetValueAtIndex(certs, i); - gurl_base::apple::ScopedCFTypeRef der_data(SecCertificateCopyData(sec_cert)); + ScopedCFTypeRef der_data(SecCertificateCopyData(sec_cert)); if (!der_data) { LOG(ERROR) << "SecCertificateCopyData error"; diff --git a/yt-dlp/README.md b/yt-dlp/README.md index 458541d68e..08afff201a 100644 --- a/yt-dlp/README.md +++ b/yt-dlp/README.md @@ -1837,6 +1837,9 @@ The following extractors use this feature: #### jiosaavn * `bitrate`: Audio bitrates to request. One or more of `16`, `32`, `64`, `128`, `320`. Default is `128,320` +#### afreecatvlive +* `cdn`: One or more CDN IDs to use with the API call for stream URLs, e.g. `gcp_cdn`, `gs_cdn_pc_app`, `gs_cdn_mobile_web`, `gs_cdn_pc_web` + **Note**: These options may be changed/removed in the future without concern for backward compatibility diff --git a/yt-dlp/yt_dlp/extractor/afreecatv.py b/yt-dlp/yt_dlp/extractor/afreecatv.py index 2c33c90dbb..3e5738f6ab 100644 --- a/yt-dlp/yt_dlp/extractor/afreecatv.py +++ b/yt-dlp/yt_dlp/extractor/afreecatv.py @@ -8,9 +8,11 @@ from ..utils import ( determine_ext, filter_dict, int_or_none, + orderedSet, unified_timestamp, url_or_none, urlencode_postdata, + urljoin, ) from ..utils.traversal import traverse_obj @@ -276,6 +278,47 @@ class AfreecaTVLiveIE(AfreecaTVBaseIE): }] _LIVE_API_URL = 'https://live.afreecatv.com/afreeca/player_live_api.php' + _WORKING_CDNS = [ + 'gcp_cdn', # live-global-cdn-v02.afreecatv.com + 'gs_cdn_pc_app', # pc-app.stream.afreecatv.com + 'gs_cdn_mobile_web', # mobile-web.stream.afreecatv.com + 'gs_cdn_pc_web', # pc-web.stream.afreecatv.com + ] + _BAD_CDNS = [ + 'gs_cdn', # chromecast.afreeca.gscdn.com (cannot resolve) + 'gs_cdn_chromecast', # chromecast.stream.afreecatv.com (HTTP Error 400) + 'azure_cdn', # live-global-cdn-v01.afreecatv.com (cannot resolve) + 'aws_cf', # live-global-cdn-v03.afreecatv.com (cannot resolve) + 'kt_cdn', # kt.stream.afreecatv.com (HTTP Error 400) + ] + + def _extract_formats(self, channel_info, broadcast_no, aid): + stream_base_url = channel_info.get('RMD') or 'https://livestream-manager.afreecatv.com' + + # If user has not passed CDN IDs, try API-provided CDN ID followed by other working CDN IDs + default_cdn_ids = orderedSet([ + *traverse_obj(channel_info, ('CDN', {str}, all, lambda _, v: v not in self._BAD_CDNS)), + *self._WORKING_CDNS, + ]) + cdn_ids = self._configuration_arg('cdn', default_cdn_ids) + + for attempt, cdn_id in enumerate(cdn_ids, start=1): + m3u8_url = traverse_obj(self._download_json( + urljoin(stream_base_url, 'broad_stream_assign.html'), broadcast_no, + f'Downloading {cdn_id} stream info', f'Unable to download {cdn_id} stream info', + fatal=False, query={ + 'return_type': cdn_id, + 'broad_key': f'{broadcast_no}-common-master-hls', + }), ('view_url', {url_or_none})) + try: + return self._extract_m3u8_formats( + m3u8_url, broadcast_no, 'mp4', m3u8_id='hls', query={'aid': aid}, + headers={'Referer': 'https://play.afreecatv.com/'}) + except ExtractorError as e: + if attempt == len(cdn_ids): + raise + self.report_warning( + f'{e.cause or e.msg}. Retrying... (attempt {attempt} of {len(cdn_ids)})') def _real_extract(self, url): broadcaster_id, broadcast_no = self._match_valid_url(url).group('id', 'bno') @@ -294,7 +337,7 @@ class AfreecaTVLiveIE(AfreecaTVBaseIE): 'This livestream is protected by a password, use the --video-password option', expected=True) - aid = self._download_json( + token_info = traverse_obj(self._download_json( self._LIVE_API_URL, broadcast_no, 'Downloading access token for stream', 'Unable to download access token for stream', data=urlencode_postdata(filter_dict({ 'bno': broadcast_no, @@ -302,18 +345,17 @@ class AfreecaTVLiveIE(AfreecaTVBaseIE): 'type': 'aid', 'quality': 'master', 'pwd': password, - })))['CHANNEL']['AID'] + }))), ('CHANNEL', {dict})) or {} + aid = token_info.get('AID') + if not aid: + result = token_info.get('RESULT') + if result == 0: + raise ExtractorError('This livestream has ended', expected=True) + elif result == -6: + self.raise_login_required('This livestream is for subscribers only', method='password') + raise ExtractorError('Unable to extract access token') - stream_base_url = channel_info.get('RMD') or 'https://livestream-manager.afreecatv.com' - stream_info = self._download_json(f'{stream_base_url}/broad_stream_assign.html', broadcast_no, query={ - # works: gs_cdn_pc_app, gs_cdn_mobile_web, gs_cdn_pc_web - 'return_type': 'gs_cdn_pc_app', - 'broad_key': f'{broadcast_no}-common-master-hls', - }, note='Downloading metadata for stream', errnote='Unable to download metadata for stream') - - formats = self._extract_m3u8_formats( - stream_info['view_url'], broadcast_no, 'mp4', m3u8_id='hls', - query={'aid': aid}, headers={'Referer': url}) + formats = self._extract_formats(channel_info, broadcast_no, aid) station_info = traverse_obj(self._download_json( 'https://st.afreecatv.com/api/get_station_status.php', broadcast_no, diff --git a/yt-dlp/yt_dlp/extractor/jiosaavn.py b/yt-dlp/yt_dlp/extractor/jiosaavn.py index d7f0a2dba8..35fb3fd6b1 100644 --- a/yt-dlp/yt_dlp/extractor/jiosaavn.py +++ b/yt-dlp/yt_dlp/extractor/jiosaavn.py @@ -1,10 +1,12 @@ import functools +import math +import re from .common import InfoExtractor from ..utils import ( - format_field, + InAdvancePagedList, + clean_html, int_or_none, - js_to_json, make_archive_id, smuggle_url, unsmuggle_url, @@ -16,6 +18,7 @@ from ..utils.traversal import traverse_obj class JioSaavnBaseIE(InfoExtractor): + _API_URL = 'https://www.jiosaavn.com/api.php' _VALID_BITRATES = {'16', '32', '64', '128', '320'} @functools.cached_property @@ -30,7 +33,7 @@ class JioSaavnBaseIE(InfoExtractor): def _extract_formats(self, song_data): for bitrate in self.requested_bitrates: media_data = self._download_json( - 'https://www.jiosaavn.com/api.php', song_data['id'], + self._API_URL, song_data['id'], f'Downloading format info for {bitrate}', fatal=False, data=urlencode_postdata({ '__call': 'song.generateAuthToken', @@ -50,31 +53,45 @@ class JioSaavnBaseIE(InfoExtractor): 'vcodec': 'none', } - def _extract_song(self, song_data): + def _extract_song(self, song_data, url=None): info = traverse_obj(song_data, { 'id': ('id', {str}), - 'title': ('title', 'text', {str}), - 'album': ('album', 'text', {str}), - 'thumbnail': ('image', 0, {url_or_none}), + 'title': ('song', {clean_html}), + 'album': ('album', {clean_html}), + 'thumbnail': ('image', {url_or_none}, {lambda x: re.sub(r'-\d+x\d+\.', '-500x500.', x)}), 'duration': ('duration', {int_or_none}), 'view_count': ('play_count', {int_or_none}), 'release_year': ('year', {int_or_none}), - 'artists': ('artists', lambda _, v: v['role'] == 'singer', 'name', {str}), - 'webpage_url': ('perma_url', {url_or_none}), # for song, playlist extraction + 'artists': ('primary_artists', {lambda x: x.split(', ') if x else None}), + 'webpage_url': ('perma_url', {url_or_none}), }) - if not info.get('webpage_url'): # for album extraction / fallback - info['webpage_url'] = format_field( - song_data, [('title', 'action')], 'https://www.jiosaavn.com%s') or None - if webpage_url := info['webpage_url']: - info['_old_archive_ids'] = [make_archive_id(JioSaavnSongIE, url_basename(webpage_url))] + if webpage_url := info.get('webpage_url') or url: + info['display_id'] = url_basename(webpage_url) + info['_old_archive_ids'] = [make_archive_id(JioSaavnSongIE, info['display_id'])] return info - def _extract_initial_data(self, url, display_id): - webpage = self._download_webpage(url, display_id) - return self._search_json( - r'window\.__INITIAL_DATA__\s*=', webpage, - 'initial data', display_id, transform_source=js_to_json) + def _call_api(self, type_, token, note='API', params={}): + return self._download_json( + self._API_URL, token, f'Downloading {note} JSON', f'Unable to download {note} JSON', + query={ + '__call': 'webapi.get', + '_format': 'json', + '_marker': '0', + 'ctx': 'web6dot0', + 'token': token, + 'type': type_, + **params, + }) + + def _yield_songs(self, playlist_data): + for song_data in traverse_obj(playlist_data, ('songs', lambda _, v: v['id'] and v['perma_url'])): + song_info = self._extract_song(song_data) + url = smuggle_url(song_info['webpage_url'], { + 'id': song_data['id'], + 'encrypted_media_url': song_data['encrypted_media_url'], + }) + yield self.url_result(url, JioSaavnSongIE, url_transparent=True, **song_info) class JioSaavnSongIE(JioSaavnBaseIE): @@ -85,10 +102,11 @@ class JioSaavnSongIE(JioSaavnBaseIE): 'md5': '3b84396d15ed9e083c3106f1fa589c04', 'info_dict': { 'id': 'IcoLuefJ', + 'display_id': 'OQsEfQFVUXk', 'ext': 'm4a', 'title': 'Leja Re', 'album': 'Leja Re', - 'thumbnail': 'https://c.saavncdn.com/258/Leja-Re-Hindi-2018-20181124024539-500x500.jpg', + 'thumbnail': r're:https?://c.saavncdn.com/258/Leja-Re-Hindi-2018-20181124024539-500x500.jpg', 'duration': 205, 'view_count': int, 'release_year': 2018, @@ -111,8 +129,8 @@ class JioSaavnSongIE(JioSaavnBaseIE): result = {'id': song_data['id']} else: # only extract metadata if this is not a url_transparent result - song_data = self._extract_initial_data(url, self._match_id(url))['song']['song'] - result = self._extract_song(song_data) + song_data = self._call_api('song', self._match_id(url))['songs'][0] + result = self._extract_song(song_data, url) result['formats'] = list(self._extract_formats(song_data)) return result @@ -130,19 +148,12 @@ class JioSaavnAlbumIE(JioSaavnBaseIE): 'playlist_count': 10, }] - def _entries(self, playlist_data): - for song_data in traverse_obj(playlist_data, ( - 'modules', lambda _, x: x['key'] == 'list', 'data', lambda _, v: v['title']['action'])): - song_info = self._extract_song(song_data) - # album song data is missing artists and release_year, need to re-extract metadata - yield self.url_result(song_info['webpage_url'], JioSaavnSongIE, **song_info) - def _real_extract(self, url): display_id = self._match_id(url) - album_data = self._extract_initial_data(url, display_id)['albumView'] + album_data = self._call_api('album', display_id) return self.playlist_result( - self._entries(album_data), display_id, traverse_obj(album_data, ('album', 'title', 'text', {str}))) + self._yield_songs(album_data), display_id, traverse_obj(album_data, ('title', {str}))) class JioSaavnPlaylistIE(JioSaavnBaseIE): @@ -154,21 +165,30 @@ class JioSaavnPlaylistIE(JioSaavnBaseIE): 'id': 'LlJ8ZWT1ibN5084vKHRj2Q__', 'title': 'Mood English', }, - 'playlist_mincount': 50, + 'playlist_mincount': 301, + }, { + 'url': 'https://www.jiosaavn.com/s/playlist/2279fbe391defa793ad7076929a2f5c9/mood-hindi/DVR,pFUOwyXqIp77B1JF,A__', + 'info_dict': { + 'id': 'DVR,pFUOwyXqIp77B1JF,A__', + 'title': 'Mood Hindi', + }, + 'playlist_mincount': 801, }] + _PAGE_SIZE = 50 - def _entries(self, playlist_data): - for song_data in traverse_obj(playlist_data, ('list', lambda _, v: v['perma_url'])): - song_info = self._extract_song(song_data) - url = smuggle_url(song_info['webpage_url'], { - 'id': song_data['id'], - 'encrypted_media_url': song_data['encrypted_media_url'], - }) - yield self.url_result(url, JioSaavnSongIE, url_transparent=True, **song_info) + def _fetch_page(self, token, page): + return self._call_api( + 'playlist', token, f'playlist page {page}', {'p': page, 'n': self._PAGE_SIZE}) + + def _entries(self, token, first_page_data, page): + page_data = first_page_data if not page else self._fetch_page(token, page + 1) + yield from self._yield_songs(page_data) def _real_extract(self, url): display_id = self._match_id(url) - playlist_data = self._extract_initial_data(url, display_id)['playlist']['playlist'] + playlist_data = self._fetch_page(display_id, 1) + total_pages = math.ceil(int(playlist_data['list_count']) / self._PAGE_SIZE) - return self.playlist_result( - self._entries(playlist_data), display_id, traverse_obj(playlist_data, ('title', 'text', {str}))) + return self.playlist_result(InAdvancePagedList( + functools.partial(self._entries, display_id, playlist_data), + total_pages, self._PAGE_SIZE), display_id, traverse_obj(playlist_data, ('listname', {str})))