diff --git a/.github/update.log b/.github/update.log
index 553351a37e..97b5c5a28b 100644
--- a/.github/update.log
+++ b/.github/update.log
@@ -733,3 +733,4 @@ Update On Sat Aug 10 20:30:21 CEST 2024
Update On Sun Aug 11 20:32:52 CEST 2024
Update On Mon Aug 12 20:34:23 CEST 2024
Update On Tue Aug 13 20:34:16 CEST 2024
+Update On Wed Aug 14 20:33:18 CEST 2024
diff --git a/bbdown/.gitignore b/bbdown/.gitignore
index 1ee53850b8..9107fd7151 100644
--- a/bbdown/.gitignore
+++ b/bbdown/.gitignore
@@ -16,6 +16,12 @@
# Mono auto generated files
mono_crash.*
+# Rider
+.idea
+
+# macOS shit
+.DS_Store
+
# Build results
[Dd]ebug/
[Dd]ebugPublic/
diff --git a/bbdown/BBDown/BBDown.csproj b/bbdown/BBDown/BBDown.csproj
index 35b8129562..14d887cf2e 100644
--- a/bbdown/BBDown/BBDown.csproj
+++ b/bbdown/BBDown/BBDown.csproj
@@ -4,7 +4,7 @@
Exe
net8.0
MIT
- 1.6.2
+ 1.6.3
BBDown是一个免费且便捷高效的哔哩哔哩下载/解析软件.
https://github.com/nilaoda/BBDown
diff --git a/bbdown/BBDown/BBDownUtil.cs b/bbdown/BBDown/BBDownUtil.cs
index 66cc882d53..5500ae8307 100644
--- a/bbdown/BBDown/BBDownUtil.cs
+++ b/bbdown/BBDown/BBDownUtil.cs
@@ -84,12 +84,12 @@ namespace BBDown
string epId = await GetEpIdByBangumiSSIdAsync(SsRegex().Match(input).Groups[1].Value);
avid = $"ep:{epId}";
}
- else if (input.Contains("/medialist/") && input.Contains("business_id=") && input.Contains("business=space_collection")) //列表类型是合集
+ else if (input.Contains("/medialist/") && input.Contains("business_id=") && input.Contains("business=space_collection")) // 列表类型是合集
{
string bizId = GetQueryString("business_id", input);
avid = $"listBizId:{bizId}";
}
- else if (input.Contains("/medialist/") && input.Contains("business_id=") && input.Contains("business=space_series")) //列表类型是系列
+ else if (input.Contains("/medialist/") && input.Contains("business_id=") && input.Contains("business=space_series")) // 列表类型是系列
{
string bizId = GetQueryString("business_id", input);
avid = $"seriesBizId:{bizId}";
@@ -145,10 +145,23 @@ namespace BBDown
{
avid = GetAidByBV(input[3..]);
}
- else if (input.ToLower().StartsWith("av")) //av
+ else if (input.ToLower().StartsWith("av")) // av
{
avid = input.ToLower()[2..];
}
+ else if (input.StartsWith("cheese/")) // ^cheese/(ep|ss)\d+ 格式
+ {
+ string epId = "";
+ if (input.Contains("/ep"))
+ {
+ epId = EpRegex().Match(input).Groups[1].Value;
+ }
+ else if (input.Contains("/ss"))
+ {
+ epId = await GetEpidBySSIdAsync(SsRegex().Match(input).Groups[1].Value);
+ }
+ avid = $"cheese:{epId}";
+ }
else if (input.StartsWith("ep"))
{
string epId = input[2..];
diff --git a/bbdown/BBDown/Program.cs b/bbdown/BBDown/Program.cs
index ef980b14a0..d14661d6f6 100644
--- a/bbdown/BBDown/Program.cs
+++ b/bbdown/BBDown/Program.cs
@@ -226,14 +226,15 @@ namespace BBDown
public static async Task<(string fetchedAid, VInfo vInfo, string apiType)> GetVideoInfoAsync(MyOption myOption, string aidOri, string input)
{
- //加载认证信息
+ Log("检测账号登录...");
+
+ // 加载认证信息
LoadCredentials(myOption);
// 检测是否登录了账号
bool is_login = await CheckLogin(Config.COOKIE);
if (!myOption.UseIntlApi && !myOption.UseTvApi && Config.AREA == "")
{
- Log("检测账号登录...");
if (!is_login)
{
LogWarn("你尚未登录B站账号, 解析可能受到限制");
@@ -251,7 +252,33 @@ namespace BBDown
Log("获取视频信息...");
IFetcher fetcher = FetcherFactory.CreateFetcher(aidOri, myOption.UseIntlApi);
- var vInfo = await fetcher.FetchAsync(aidOri);
+ VInfo? vInfo = null;
+
+ // 只输入 EP/SS 时优先按番剧查找,如果找不到则尝试按课程查找
+ try
+ {
+ vInfo = await fetcher.FetchAsync(aidOri);
+ }
+ catch (KeyNotFoundException e)
+ {
+ if (e.Message != "Arg_KeyNotFound") throw; // 错误消息不符合预期,抛出异常
+ if (aidOri.StartsWith("cheese:")) throw; // 已经按课程查找过,不再重复尝试
+
+ LogWarn("未找到此 EP/SS 对应番剧信息, 正在尝试按课程查找。");
+
+ aidOri = aidOri.Replace("ep", "cheese");
+ Log("新的 aid: " + aidOri);
+
+ if (string.IsNullOrEmpty(aidOri))
+ {
+ throw new Exception("输入有误");
+ }
+
+ Log("获取视频信息...");
+ fetcher = FetcherFactory.CreateFetcher(aidOri, myOption.UseIntlApi);
+ vInfo = await fetcher.FetchAsync(aidOri);
+ }
+
string title = vInfo.Title;
long pubTime = vInfo.PubTime;
LogColor("视频标题: " + title);
diff --git a/clash-meta/.github/patch_go122/48042aa09c2f878c4faa576948b07fe625c4707a.diff b/clash-meta/.github/patch_go122/48042aa09c2f878c4faa576948b07fe625c4707a.diff
deleted file mode 100644
index 2c68233358..0000000000
--- a/clash-meta/.github/patch_go122/48042aa09c2f878c4faa576948b07fe625c4707a.diff
+++ /dev/null
@@ -1,54 +0,0 @@
-diff --git a/src/syscall/exec_windows.go b/src/syscall/exec_windows.go
-index 06e684c7116b4..b311a5c74684b 100644
---- a/src/syscall/exec_windows.go
-+++ b/src/syscall/exec_windows.go
-@@ -319,17 +319,6 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle
- }
- }
-
-- var maj, min, build uint32
-- rtlGetNtVersionNumbers(&maj, &min, &build)
-- isWin7 := maj < 6 || (maj == 6 && min <= 1)
-- // NT kernel handles are divisible by 4, with the bottom 3 bits left as
-- // a tag. The fully set tag correlates with the types of handles we're
-- // concerned about here. Except, the kernel will interpret some
-- // special handle values, like -1, -2, and so forth, so kernelbase.dll
-- // checks to see that those bottom three bits are checked, but that top
-- // bit is not checked.
-- isLegacyWin7ConsoleHandle := func(handle Handle) bool { return isWin7 && handle&0x10000003 == 3 }
--
- p, _ := GetCurrentProcess()
- parentProcess := p
- if sys.ParentProcess != 0 {
-@@ -338,15 +327,7 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle
- fd := make([]Handle, len(attr.Files))
- for i := range attr.Files {
- if attr.Files[i] > 0 {
-- destinationProcessHandle := parentProcess
--
-- // On Windows 7, console handles aren't real handles, and can only be duplicated
-- // into the current process, not a parent one, which amounts to the same thing.
-- if parentProcess != p && isLegacyWin7ConsoleHandle(Handle(attr.Files[i])) {
-- destinationProcessHandle = p
-- }
--
-- err := DuplicateHandle(p, Handle(attr.Files[i]), destinationProcessHandle, &fd[i], 0, true, DUPLICATE_SAME_ACCESS)
-+ err := DuplicateHandle(p, Handle(attr.Files[i]), parentProcess, &fd[i], 0, true, DUPLICATE_SAME_ACCESS)
- if err != nil {
- return 0, 0, err
- }
-@@ -377,14 +358,6 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle
-
- fd = append(fd, sys.AdditionalInheritedHandles...)
-
-- // On Windows 7, console handles aren't real handles, so don't pass them
-- // through to PROC_THREAD_ATTRIBUTE_HANDLE_LIST.
-- for i := range fd {
-- if isLegacyWin7ConsoleHandle(fd[i]) {
-- fd[i] = 0
-- }
-- }
--
- // The presence of a NULL handle in the list is enough to cause PROC_THREAD_ATTRIBUTE_HANDLE_LIST
- // to treat the entire list as empty, so remove NULL handles.
- j := 0
\ No newline at end of file
diff --git a/clash-meta/.github/patch_go122/693def151adff1af707d82d28f55dba81ceb08e1.diff b/clash-meta/.github/patch_go122/693def151adff1af707d82d28f55dba81ceb08e1.diff
deleted file mode 100644
index ca41ec317e..0000000000
--- a/clash-meta/.github/patch_go122/693def151adff1af707d82d28f55dba81ceb08e1.diff
+++ /dev/null
@@ -1,158 +0,0 @@
-diff --git a/src/crypto/rand/rand.go b/src/crypto/rand/rand.go
-index 62738e2cb1a7d..d0dcc7cc71fc0 100644
---- a/src/crypto/rand/rand.go
-+++ b/src/crypto/rand/rand.go
-@@ -15,7 +15,7 @@ import "io"
- // available, /dev/urandom otherwise.
- // On OpenBSD and macOS, Reader uses getentropy(2).
- // On other Unix-like systems, Reader reads from /dev/urandom.
--// On Windows systems, Reader uses the RtlGenRandom API.
-+// On Windows systems, Reader uses the ProcessPrng API.
- // On JS/Wasm, Reader uses the Web Crypto API.
- // On WASIP1/Wasm, Reader uses random_get from wasi_snapshot_preview1.
- var Reader io.Reader
-diff --git a/src/crypto/rand/rand_windows.go b/src/crypto/rand/rand_windows.go
-index 6c0655c72b692..7380f1f0f1e6e 100644
---- a/src/crypto/rand/rand_windows.go
-+++ b/src/crypto/rand/rand_windows.go
-@@ -15,11 +15,8 @@ func init() { Reader = &rngReader{} }
-
- type rngReader struct{}
-
--func (r *rngReader) Read(b []byte) (n int, err error) {
-- // RtlGenRandom only returns 1<<32-1 bytes at a time. We only read at
-- // most 1<<31-1 bytes at a time so that this works the same on 32-bit
-- // and 64-bit systems.
-- if err := batched(windows.RtlGenRandom, 1<<31-1)(b); err != nil {
-+func (r *rngReader) Read(b []byte) (int, error) {
-+ if err := windows.ProcessPrng(b); err != nil {
- return 0, err
- }
- return len(b), nil
-diff --git a/src/internal/syscall/windows/syscall_windows.go b/src/internal/syscall/windows/syscall_windows.go
-index ab4ad2ec64108..5854ca60b5cef 100644
---- a/src/internal/syscall/windows/syscall_windows.go
-+++ b/src/internal/syscall/windows/syscall_windows.go
-@@ -373,7 +373,7 @@ func ErrorLoadingGetTempPath2() error {
- //sys DestroyEnvironmentBlock(block *uint16) (err error) = userenv.DestroyEnvironmentBlock
- //sys CreateEvent(eventAttrs *SecurityAttributes, manualReset uint32, initialState uint32, name *uint16) (handle syscall.Handle, err error) = kernel32.CreateEventW
-
--//sys RtlGenRandom(buf []byte) (err error) = advapi32.SystemFunction036
-+//sys ProcessPrng(buf []byte) (err error) = bcryptprimitives.ProcessPrng
-
- type FILE_ID_BOTH_DIR_INFO struct {
- NextEntryOffset uint32
-diff --git a/src/internal/syscall/windows/zsyscall_windows.go b/src/internal/syscall/windows/zsyscall_windows.go
-index e3f6d8d2a2208..5a587ad4f146c 100644
---- a/src/internal/syscall/windows/zsyscall_windows.go
-+++ b/src/internal/syscall/windows/zsyscall_windows.go
-@@ -37,13 +37,14 @@ func errnoErr(e syscall.Errno) error {
- }
-
- var (
-- modadvapi32 = syscall.NewLazyDLL(sysdll.Add("advapi32.dll"))
-- modiphlpapi = syscall.NewLazyDLL(sysdll.Add("iphlpapi.dll"))
-- modkernel32 = syscall.NewLazyDLL(sysdll.Add("kernel32.dll"))
-- modnetapi32 = syscall.NewLazyDLL(sysdll.Add("netapi32.dll"))
-- modpsapi = syscall.NewLazyDLL(sysdll.Add("psapi.dll"))
-- moduserenv = syscall.NewLazyDLL(sysdll.Add("userenv.dll"))
-- modws2_32 = syscall.NewLazyDLL(sysdll.Add("ws2_32.dll"))
-+ modadvapi32 = syscall.NewLazyDLL(sysdll.Add("advapi32.dll"))
-+ modbcryptprimitives = syscall.NewLazyDLL(sysdll.Add("bcryptprimitives.dll"))
-+ modiphlpapi = syscall.NewLazyDLL(sysdll.Add("iphlpapi.dll"))
-+ modkernel32 = syscall.NewLazyDLL(sysdll.Add("kernel32.dll"))
-+ modnetapi32 = syscall.NewLazyDLL(sysdll.Add("netapi32.dll"))
-+ modpsapi = syscall.NewLazyDLL(sysdll.Add("psapi.dll"))
-+ moduserenv = syscall.NewLazyDLL(sysdll.Add("userenv.dll"))
-+ modws2_32 = syscall.NewLazyDLL(sysdll.Add("ws2_32.dll"))
-
- procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges")
- procDuplicateTokenEx = modadvapi32.NewProc("DuplicateTokenEx")
-@@ -55,7 +56,7 @@ var (
- procQueryServiceStatus = modadvapi32.NewProc("QueryServiceStatus")
- procRevertToSelf = modadvapi32.NewProc("RevertToSelf")
- procSetTokenInformation = modadvapi32.NewProc("SetTokenInformation")
-- procSystemFunction036 = modadvapi32.NewProc("SystemFunction036")
-+ procProcessPrng = modbcryptprimitives.NewProc("ProcessPrng")
- procGetAdaptersAddresses = modiphlpapi.NewProc("GetAdaptersAddresses")
- procCreateEventW = modkernel32.NewProc("CreateEventW")
- procGetACP = modkernel32.NewProc("GetACP")
-@@ -179,12 +180,12 @@ func SetTokenInformation(tokenHandle syscall.Token, tokenInformationClass uint32
- return
- }
-
--func RtlGenRandom(buf []byte) (err error) {
-+func ProcessPrng(buf []byte) (err error) {
- var _p0 *byte
- if len(buf) > 0 {
- _p0 = &buf[0]
- }
-- r1, _, e1 := syscall.Syscall(procSystemFunction036.Addr(), 2, uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), 0)
-+ r1, _, e1 := syscall.Syscall(procProcessPrng.Addr(), 2, uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), 0)
- if r1 == 0 {
- err = errnoErr(e1)
- }
-diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go
-index 8ca8d7790909e..3772a864b2ff4 100644
---- a/src/runtime/os_windows.go
-+++ b/src/runtime/os_windows.go
-@@ -127,15 +127,8 @@ var (
- _WriteFile,
- _ stdFunction
-
-- // Use RtlGenRandom to generate cryptographically random data.
-- // This approach has been recommended by Microsoft (see issue
-- // 15589 for details).
-- // The RtlGenRandom is not listed in advapi32.dll, instead
-- // RtlGenRandom function can be found by searching for SystemFunction036.
-- // Also some versions of Mingw cannot link to SystemFunction036
-- // when building executable as Cgo. So load SystemFunction036
-- // manually during runtime startup.
-- _RtlGenRandom stdFunction
-+ // Use ProcessPrng to generate cryptographically random data.
-+ _ProcessPrng stdFunction
-
- // Load ntdll.dll manually during startup, otherwise Mingw
- // links wrong printf function to cgo executable (see issue
-@@ -151,11 +144,11 @@ var (
- )
-
- var (
-- advapi32dll = [...]uint16{'a', 'd', 'v', 'a', 'p', 'i', '3', '2', '.', 'd', 'l', 'l', 0}
-- ntdlldll = [...]uint16{'n', 't', 'd', 'l', 'l', '.', 'd', 'l', 'l', 0}
-- powrprofdll = [...]uint16{'p', 'o', 'w', 'r', 'p', 'r', 'o', 'f', '.', 'd', 'l', 'l', 0}
-- winmmdll = [...]uint16{'w', 'i', 'n', 'm', 'm', '.', 'd', 'l', 'l', 0}
-- ws2_32dll = [...]uint16{'w', 's', '2', '_', '3', '2', '.', 'd', 'l', 'l', 0}
-+ bcryptprimitivesdll = [...]uint16{'b', 'c', 'r', 'y', 'p', 't', 'p', 'r', 'i', 'm', 'i', 't', 'i', 'v', 'e', 's', '.', 'd', 'l', 'l', 0}
-+ ntdlldll = [...]uint16{'n', 't', 'd', 'l', 'l', '.', 'd', 'l', 'l', 0}
-+ powrprofdll = [...]uint16{'p', 'o', 'w', 'r', 'p', 'r', 'o', 'f', '.', 'd', 'l', 'l', 0}
-+ winmmdll = [...]uint16{'w', 'i', 'n', 'm', 'm', '.', 'd', 'l', 'l', 0}
-+ ws2_32dll = [...]uint16{'w', 's', '2', '_', '3', '2', '.', 'd', 'l', 'l', 0}
- )
-
- // Function to be called by windows CreateThread
-@@ -251,11 +244,11 @@ func windowsLoadSystemLib(name []uint16) uintptr {
- }
-
- func loadOptionalSyscalls() {
-- a32 := windowsLoadSystemLib(advapi32dll[:])
-- if a32 == 0 {
-- throw("advapi32.dll not found")
-+ bcryptPrimitives := windowsLoadSystemLib(bcryptprimitivesdll[:])
-+ if bcryptPrimitives == 0 {
-+ throw("bcryptprimitives.dll not found")
- }
-- _RtlGenRandom = windowsFindfunc(a32, []byte("SystemFunction036\000"))
-+ _ProcessPrng = windowsFindfunc(bcryptPrimitives, []byte("ProcessPrng\000"))
-
- n32 := windowsLoadSystemLib(ntdlldll[:])
- if n32 == 0 {
-@@ -531,7 +524,7 @@ func osinit() {
- //go:nosplit
- func readRandom(r []byte) int {
- n := 0
-- if stdcall2(_RtlGenRandom, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 {
-+ if stdcall2(_ProcessPrng, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 {
- n = len(r)
- }
- return n
\ No newline at end of file
diff --git a/clash-meta/.github/patch_go122/7c1157f9544922e96945196b47b95664b1e39108.diff b/clash-meta/.github/patch_go122/7c1157f9544922e96945196b47b95664b1e39108.diff
deleted file mode 100644
index c1fc5f6d2d..0000000000
--- a/clash-meta/.github/patch_go122/7c1157f9544922e96945196b47b95664b1e39108.diff
+++ /dev/null
@@ -1,162 +0,0 @@
-diff --git a/src/net/hook_windows.go b/src/net/hook_windows.go
-index ab8656cbbf343..28c49cc6de7e7 100644
---- a/src/net/hook_windows.go
-+++ b/src/net/hook_windows.go
-@@ -14,7 +14,6 @@ var (
- testHookDialChannel = func() { time.Sleep(time.Millisecond) } // see golang.org/issue/5349
-
- // Placeholders for socket system calls.
-- socketFunc func(int, int, int) (syscall.Handle, error) = syscall.Socket
- wsaSocketFunc func(int32, int32, int32, *syscall.WSAProtocolInfo, uint32, uint32) (syscall.Handle, error) = windows.WSASocket
- connectFunc func(syscall.Handle, syscall.Sockaddr) error = syscall.Connect
- listenFunc func(syscall.Handle, int) error = syscall.Listen
-diff --git a/src/net/internal/socktest/main_test.go b/src/net/internal/socktest/main_test.go
-index 0197feb3f199a..967ce6795aedb 100644
---- a/src/net/internal/socktest/main_test.go
-+++ b/src/net/internal/socktest/main_test.go
-@@ -2,7 +2,7 @@
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
-
--//go:build !js && !plan9 && !wasip1
-+//go:build !js && !plan9 && !wasip1 && !windows
-
- package socktest_test
-
-diff --git a/src/net/internal/socktest/main_windows_test.go b/src/net/internal/socktest/main_windows_test.go
-deleted file mode 100644
-index df1cb97784b51..0000000000000
---- a/src/net/internal/socktest/main_windows_test.go
-+++ /dev/null
-@@ -1,22 +0,0 @@
--// Copyright 2015 The Go Authors. All rights reserved.
--// Use of this source code is governed by a BSD-style
--// license that can be found in the LICENSE file.
--
--package socktest_test
--
--import "syscall"
--
--var (
-- socketFunc func(int, int, int) (syscall.Handle, error)
-- closeFunc func(syscall.Handle) error
--)
--
--func installTestHooks() {
-- socketFunc = sw.Socket
-- closeFunc = sw.Closesocket
--}
--
--func uninstallTestHooks() {
-- socketFunc = syscall.Socket
-- closeFunc = syscall.Closesocket
--}
-diff --git a/src/net/internal/socktest/sys_windows.go b/src/net/internal/socktest/sys_windows.go
-index 8c1c862f33c9b..1c42e5c7f34b7 100644
---- a/src/net/internal/socktest/sys_windows.go
-+++ b/src/net/internal/socktest/sys_windows.go
-@@ -9,38 +9,6 @@ import (
- "syscall"
- )
-
--// Socket wraps syscall.Socket.
--func (sw *Switch) Socket(family, sotype, proto int) (s syscall.Handle, err error) {
-- sw.once.Do(sw.init)
--
-- so := &Status{Cookie: cookie(family, sotype, proto)}
-- sw.fmu.RLock()
-- f, _ := sw.fltab[FilterSocket]
-- sw.fmu.RUnlock()
--
-- af, err := f.apply(so)
-- if err != nil {
-- return syscall.InvalidHandle, err
-- }
-- s, so.Err = syscall.Socket(family, sotype, proto)
-- if err = af.apply(so); err != nil {
-- if so.Err == nil {
-- syscall.Closesocket(s)
-- }
-- return syscall.InvalidHandle, err
-- }
--
-- sw.smu.Lock()
-- defer sw.smu.Unlock()
-- if so.Err != nil {
-- sw.stats.getLocked(so.Cookie).OpenFailed++
-- return syscall.InvalidHandle, so.Err
-- }
-- nso := sw.addLocked(s, family, sotype, proto)
-- sw.stats.getLocked(nso.Cookie).Opened++
-- return s, nil
--}
--
- // WSASocket wraps [syscall.WSASocket].
- func (sw *Switch) WSASocket(family, sotype, proto int32, protinfo *syscall.WSAProtocolInfo, group uint32, flags uint32) (s syscall.Handle, err error) {
- sw.once.Do(sw.init)
-diff --git a/src/net/main_windows_test.go b/src/net/main_windows_test.go
-index 07f21b72eb1fc..bc024c0bbd82d 100644
---- a/src/net/main_windows_test.go
-+++ b/src/net/main_windows_test.go
-@@ -8,7 +8,6 @@ import "internal/poll"
-
- var (
- // Placeholders for saving original socket system calls.
-- origSocket = socketFunc
- origWSASocket = wsaSocketFunc
- origClosesocket = poll.CloseFunc
- origConnect = connectFunc
-@@ -18,7 +17,6 @@ var (
- )
-
- func installTestHooks() {
-- socketFunc = sw.Socket
- wsaSocketFunc = sw.WSASocket
- poll.CloseFunc = sw.Closesocket
- connectFunc = sw.Connect
-@@ -28,7 +26,6 @@ func installTestHooks() {
- }
-
- func uninstallTestHooks() {
-- socketFunc = origSocket
- wsaSocketFunc = origWSASocket
- poll.CloseFunc = origClosesocket
- connectFunc = origConnect
-diff --git a/src/net/sock_windows.go b/src/net/sock_windows.go
-index fa11c7af2e727..5540135a2c43e 100644
---- a/src/net/sock_windows.go
-+++ b/src/net/sock_windows.go
-@@ -19,21 +19,6 @@ func maxListenerBacklog() int {
- func sysSocket(family, sotype, proto int) (syscall.Handle, error) {
- s, err := wsaSocketFunc(int32(family), int32(sotype), int32(proto),
- nil, 0, windows.WSA_FLAG_OVERLAPPED|windows.WSA_FLAG_NO_HANDLE_INHERIT)
-- if err == nil {
-- return s, nil
-- }
-- // WSA_FLAG_NO_HANDLE_INHERIT flag is not supported on some
-- // old versions of Windows, see
-- // https://msdn.microsoft.com/en-us/library/windows/desktop/ms742212(v=vs.85).aspx
-- // for details. Just use syscall.Socket, if windows.WSASocket failed.
--
-- // See ../syscall/exec_unix.go for description of ForkLock.
-- syscall.ForkLock.RLock()
-- s, err = socketFunc(family, sotype, proto)
-- if err == nil {
-- syscall.CloseOnExec(s)
-- }
-- syscall.ForkLock.RUnlock()
- if err != nil {
- return syscall.InvalidHandle, os.NewSyscallError("socket", err)
- }
-diff --git a/src/syscall/exec_windows.go b/src/syscall/exec_windows.go
-index 0a93bc0a80d4e..06e684c7116b4 100644
---- a/src/syscall/exec_windows.go
-+++ b/src/syscall/exec_windows.go
-@@ -14,6 +14,7 @@ import (
- "unsafe"
- )
-
-+// ForkLock is not used on Windows.
- var ForkLock sync.RWMutex
-
- // EscapeArg rewrites command line argument s as prescribed
\ No newline at end of file
diff --git a/clash-meta/.github/workflows/build.yml b/clash-meta/.github/workflows/build.yml
index cd5202e12e..fb3eb621a4 100644
--- a/clash-meta/.github/workflows/build.yml
+++ b/clash-meta/.github/workflows/build.yml
@@ -67,6 +67,12 @@ jobs:
- { goos: android, goarch: arm, ndk: armv7a-linux-androideabi34, output: armv7 }
- { goos: android, goarch: arm64, ndk: aarch64-linux-android34, output: arm64-v8 }
+ # Go 1.22 with special patch can work on Windows 7
+ # https://github.com/MetaCubeX/go/commits/release-branch.go1.22/
+ - { goos: windows, goarch: '386', output: '386-go122', goversion: '1.22' }
+ - { goos: windows, goarch: amd64, goamd64: v1, output: amd64-compatible-go122, goversion: '1.22' }
+ - { goos: windows, goarch: amd64, goamd64: v3, output: amd64-go122, goversion: '1.22' }
+
# Go 1.21 can revert commit `9e4385` to work on Windows 7
# https://github.com/golang/go/issues/64622#issuecomment-1847475161
# (OR we can just use golang1.21.4 which unneeded any patch)
@@ -79,6 +85,11 @@ jobs:
- { goos: windows, goarch: amd64, goamd64: v1, output: amd64-compatible-go120, goversion: '1.20' }
- { goos: windows, goarch: amd64, goamd64: v3, output: amd64-go120, goversion: '1.20' }
+ # Go 1.22 is the last release that will run on macOS 10.15 Catalina. Go 1.23 will require macOS 11 Big Sur or later.
+ - { goos: darwin, goarch: arm64, output: arm64-go122, goversion: '1.22' }
+ - { goos: darwin, goarch: amd64, goamd64: v1, output: amd64-compatible-go122, goversion: '1.22' }
+ - { goos: darwin, goarch: amd64, goamd64: v3, output: amd64-go122, goversion: '1.22' }
+
# Go 1.20 is the last release that will run on macOS 10.13 High Sierra or 10.14 Mojave. Go 1.21 will require macOS 10.15 Catalina or later.
- { goos: darwin, goarch: arm64, output: arm64-go120, goversion: '1.20' }
- { goos: darwin, goarch: amd64, goamd64: v1, output: amd64-compatible-go120, goversion: '1.20' }
@@ -96,7 +107,7 @@ jobs:
if: ${{ matrix.jobs.goversion == '' && matrix.jobs.goarch != 'loong64' }}
uses: actions/setup-go@v5
with:
- go-version: '1.22'
+ go-version: '1.23'
- name: Set up Go
if: ${{ matrix.jobs.goversion != '' && matrix.jobs.goarch != 'loong64' }}
@@ -119,19 +130,40 @@ jobs:
echo "/usr/local/go/bin" >> $GITHUB_PATH
# modify from https://github.com/restic/restic/issues/4636#issuecomment-1896455557
- # this patch file only works on golang1.22.x
- # that means after golang1.23 release it must be changed
+ # this patch file only works on golang1.23.x
+ # that means after golang1.24 release it must be changed
+ # see: https://github.com/MetaCubeX/go/commits/release-branch.go1.23/
# revert:
# 693def151adff1af707d82d28f55dba81ceb08e1: "crypto/rand,runtime: switch RtlGenRandom for ProcessPrng"
# 7c1157f9544922e96945196b47b95664b1e39108: "net: remove sysSocket fallback for Windows 7"
# 48042aa09c2f878c4faa576948b07fe625c4707a: "syscall: remove Windows 7 console handle workaround"
- - name: Revert Golang1.22 commit for Windows7/8
+ # a17d959debdb04cd550016a3501dd09d50cd62e7: "runtime: always use LoadLibraryEx to load system libraries"
+ - name: Revert Golang1.23 commit for Windows7/8
if: ${{ matrix.jobs.goos == 'windows' && matrix.jobs.goversion == '' }}
run: |
cd $(go env GOROOT)
- patch --verbose -R -p 1 < $GITHUB_WORKSPACE/.github/patch_go122/693def151adff1af707d82d28f55dba81ceb08e1.diff
- patch --verbose -R -p 1 < $GITHUB_WORKSPACE/.github/patch_go122/7c1157f9544922e96945196b47b95664b1e39108.diff
- patch --verbose -R -p 1 < $GITHUB_WORKSPACE/.github/patch_go122/48042aa09c2f878c4faa576948b07fe625c4707a.diff
+ curl https://github.com/MetaCubeX/go/commit/9ac42137ef6730e8b7daca016ece831297a1d75b.diff | patch --verbose -p 1
+ curl https://github.com/MetaCubeX/go/commit/21290de8a4c91408de7c2b5b68757b1e90af49dd.diff | patch --verbose -p 1
+ curl https://github.com/MetaCubeX/go/commit/6a31d3fa8e47ddabc10bd97bff10d9a85f4cfb76.diff | patch --verbose -p 1
+ curl https://github.com/MetaCubeX/go/commit/69e2eed6dd0f6d815ebf15797761c13f31213dd6.diff | patch --verbose -p 1
+
+ # modify from https://github.com/restic/restic/issues/4636#issuecomment-1896455557
+ # this patch file only works on golang1.22.x
+ # that means after golang1.23 release it must be changed
+ # see: https://github.com/MetaCubeX/go/commits/release-branch.go1.22/
+ # revert:
+ # 693def151adff1af707d82d28f55dba81ceb08e1: "crypto/rand,runtime: switch RtlGenRandom for ProcessPrng"
+ # 7c1157f9544922e96945196b47b95664b1e39108: "net: remove sysSocket fallback for Windows 7"
+ # 48042aa09c2f878c4faa576948b07fe625c4707a: "syscall: remove Windows 7 console handle workaround"
+ # a17d959debdb04cd550016a3501dd09d50cd62e7: "runtime: always use LoadLibraryEx to load system libraries"
+ - name: Revert Golang1.22 commit for Windows7/8
+ if: ${{ matrix.jobs.goos == 'windows' && matrix.jobs.goversion == '1.22' }}
+ run: |
+ cd $(go env GOROOT)
+ curl https://github.com/MetaCubeX/go/commit/9779155f18b6556a034f7bb79fb7fb2aad1e26a9.diff | patch --verbose -p 1
+ curl https://github.com/MetaCubeX/go/commit/ef0606261340e608017860b423ffae5c1ce78239.diff | patch --verbose -p 1
+ curl https://github.com/MetaCubeX/go/commit/7f83badcb925a7e743188041cb6e561fc9b5b642.diff | patch --verbose -p 1
+ curl https://github.com/MetaCubeX/go/commit/83ff9782e024cb328b690cbf0da4e7848a327f4f.diff | patch --verbose -p 1
# modify from https://github.com/restic/restic/issues/4636#issuecomment-1896455557
- name: Revert Golang1.21 commit for Windows7/8
diff --git a/clash-meta/common/net/tcp_keepalive.go b/clash-meta/common/net/tcp_keepalive.go
new file mode 100644
index 0000000000..047a1c05eb
--- /dev/null
+++ b/clash-meta/common/net/tcp_keepalive.go
@@ -0,0 +1,23 @@
+package net
+
+import (
+ "net"
+ "runtime"
+ "time"
+)
+
+var (
+ KeepAliveIdle = 0 * time.Second
+ KeepAliveInterval = 0 * time.Second
+ DisableKeepAlive = false
+)
+
+func TCPKeepAlive(c net.Conn) {
+ if tcp, ok := c.(*net.TCPConn); ok {
+ if runtime.GOOS == "android" || DisableKeepAlive {
+ _ = tcp.SetKeepAlive(false)
+ } else {
+ tcpKeepAlive(tcp)
+ }
+ }
+}
diff --git a/clash-meta/common/net/tcp_keepalive_go122.go b/clash-meta/common/net/tcp_keepalive_go122.go
new file mode 100644
index 0000000000..1287316868
--- /dev/null
+++ b/clash-meta/common/net/tcp_keepalive_go122.go
@@ -0,0 +1,10 @@
+//go:build !go1.23
+
+package net
+
+import "net"
+
+func tcpKeepAlive(tcp *net.TCPConn) {
+ _ = tcp.SetKeepAlive(true)
+ _ = tcp.SetKeepAlivePeriod(KeepAliveInterval)
+}
diff --git a/clash-meta/common/net/tcp_keepalive_go123.go b/clash-meta/common/net/tcp_keepalive_go123.go
new file mode 100644
index 0000000000..2dd4754bbe
--- /dev/null
+++ b/clash-meta/common/net/tcp_keepalive_go123.go
@@ -0,0 +1,19 @@
+//go:build go1.23
+
+package net
+
+import "net"
+
+func tcpKeepAlive(tcp *net.TCPConn) {
+ config := net.KeepAliveConfig{
+ Enable: true,
+ Idle: KeepAliveIdle,
+ Interval: KeepAliveInterval,
+ }
+ if !SupportTCPKeepAliveCount() {
+ // it's recommended to set both Idle and Interval to non-negative values in conjunction with a -1
+ // for Count on those old Windows if you intend to customize the TCP keep-alive settings.
+ config.Count = -1
+ }
+ _ = tcp.SetKeepAliveConfig(config)
+}
diff --git a/clash-meta/common/net/tcp_keepalive_go123_unix.go b/clash-meta/common/net/tcp_keepalive_go123_unix.go
new file mode 100644
index 0000000000..0ead7ca472
--- /dev/null
+++ b/clash-meta/common/net/tcp_keepalive_go123_unix.go
@@ -0,0 +1,15 @@
+//go:build go1.23 && unix
+
+package net
+
+func SupportTCPKeepAliveIdle() bool {
+ return true
+}
+
+func SupportTCPKeepAliveInterval() bool {
+ return true
+}
+
+func SupportTCPKeepAliveCount() bool {
+ return true
+}
diff --git a/clash-meta/common/net/tcp_keepalive_go123_windows.go b/clash-meta/common/net/tcp_keepalive_go123_windows.go
new file mode 100644
index 0000000000..8f1e61f959
--- /dev/null
+++ b/clash-meta/common/net/tcp_keepalive_go123_windows.go
@@ -0,0 +1,63 @@
+//go:build go1.23 && windows
+
+// copy and modify from golang1.23's internal/syscall/windows/version_windows.go
+
+package net
+
+import (
+ "errors"
+ "sync"
+ "syscall"
+
+ "github.com/metacubex/mihomo/constant/features"
+
+ "golang.org/x/sys/windows"
+)
+
+var (
+ supportTCPKeepAliveIdle bool
+ supportTCPKeepAliveInterval bool
+ supportTCPKeepAliveCount bool
+)
+
+var initTCPKeepAlive = sync.OnceFunc(func() {
+ s, err := windows.WSASocket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP, nil, 0, windows.WSA_FLAG_NO_HANDLE_INHERIT)
+ if err != nil {
+ // Fallback to checking the Windows version.
+ major, build := features.WindowsMajorVersion, features.WindowsBuildNumber
+ supportTCPKeepAliveIdle = major >= 10 && build >= 16299
+ supportTCPKeepAliveInterval = major >= 10 && build >= 16299
+ supportTCPKeepAliveCount = major >= 10 && build >= 15063
+ return
+ }
+ defer windows.Closesocket(s)
+ var optSupported = func(opt int) bool {
+ err := windows.SetsockoptInt(s, syscall.IPPROTO_TCP, opt, 1)
+ return !errors.Is(err, syscall.WSAENOPROTOOPT)
+ }
+ supportTCPKeepAliveIdle = optSupported(windows.TCP_KEEPIDLE)
+ supportTCPKeepAliveInterval = optSupported(windows.TCP_KEEPINTVL)
+ supportTCPKeepAliveCount = optSupported(windows.TCP_KEEPCNT)
+})
+
+// SupportTCPKeepAliveIdle indicates whether TCP_KEEPIDLE is supported.
+// The minimal requirement is Windows 10.0.16299.
+func SupportTCPKeepAliveIdle() bool {
+ initTCPKeepAlive()
+ return supportTCPKeepAliveIdle
+}
+
+// SupportTCPKeepAliveInterval indicates whether TCP_KEEPINTVL is supported.
+// The minimal requirement is Windows 10.0.16299.
+func SupportTCPKeepAliveInterval() bool {
+ initTCPKeepAlive()
+ return supportTCPKeepAliveInterval
+}
+
+// SupportTCPKeepAliveCount indicates whether TCP_KEEPCNT is supported.
+// supports TCP_KEEPCNT.
+// The minimal requirement is Windows 10.0.15063.
+func SupportTCPKeepAliveCount() bool {
+ initTCPKeepAlive()
+ return supportTCPKeepAliveCount
+}
diff --git a/clash-meta/common/net/tcpip.go b/clash-meta/common/net/tcpip.go
index 0499e54c17..a84e7e4c4f 100644
--- a/clash-meta/common/net/tcpip.go
+++ b/clash-meta/common/net/tcpip.go
@@ -4,11 +4,8 @@ import (
"fmt"
"net"
"strings"
- "time"
)
-var KeepAliveInterval = 15 * time.Second
-
func SplitNetworkType(s string) (string, string, error) {
var (
shecme string
@@ -47,10 +44,3 @@ func SplitHostPort(s string) (host, port string, hasPort bool, err error) {
host, port, err = net.SplitHostPort(temp)
return
}
-
-func TCPKeepAlive(c net.Conn) {
- if tcp, ok := c.(*net.TCPConn); ok {
- _ = tcp.SetKeepAlive(true)
- _ = tcp.SetKeepAlivePeriod(KeepAliveInterval)
- }
-}
diff --git a/clash-meta/component/dialer/dialer.go b/clash-meta/component/dialer/dialer.go
index ba95c31b81..2a39508f3f 100644
--- a/clash-meta/component/dialer/dialer.go
+++ b/clash-meta/component/dialer/dialer.go
@@ -13,7 +13,6 @@ import (
"time"
"github.com/metacubex/mihomo/component/resolver"
- "github.com/metacubex/mihomo/constant/features"
"github.com/metacubex/mihomo/log"
)
@@ -79,29 +78,29 @@ func DialContext(ctx context.Context, network, address string, options ...Option
}
func ListenPacket(ctx context.Context, network, address string, rAddrPort netip.AddrPort, options ...Option) (net.PacketConn, error) {
- if features.CMFA && DefaultSocketHook != nil {
- return listenPacketHooked(ctx, network, address)
- }
-
cfg := applyOptions(options...)
lc := &net.ListenConfig{}
- if cfg.interfaceName != "" {
- bind := bindIfaceToListenConfig
- if cfg.fallbackBind {
- bind = fallbackBindIfaceToListenConfig
- }
- addr, err := bind(cfg.interfaceName, lc, network, address, rAddrPort)
- if err != nil {
- return nil, err
- }
- address = addr
- }
if cfg.addrReuse {
addrReuseToListenConfig(lc)
}
- if cfg.routingMark != 0 {
- bindMarkToListenConfig(cfg.routingMark, lc, network, address)
+ if DefaultSocketHook != nil { // ignore interfaceName, routingMark when DefaultSocketHook not null (in CFMA)
+ socketHookToListenConfig(lc)
+ } else {
+ if cfg.interfaceName != "" {
+ bind := bindIfaceToListenConfig
+ if cfg.fallbackBind {
+ bind = fallbackBindIfaceToListenConfig
+ }
+ addr, err := bind(cfg.interfaceName, lc, network, address, rAddrPort)
+ if err != nil {
+ return nil, err
+ }
+ address = addr
+ }
+ if cfg.routingMark != 0 {
+ bindMarkToListenConfig(cfg.routingMark, lc, network, address)
+ }
}
return lc.ListenPacket(ctx, network, address)
@@ -149,25 +148,26 @@ func dialContext(ctx context.Context, network string, destination netip.Addr, po
setMultiPathTCP(dialer)
}
- if features.CMFA && DefaultSocketHook != nil { // ignore interfaceName, routingMark and tfo in CMFA
- return dialContextHooked(ctx, dialer, network, address)
+ if DefaultSocketHook != nil { // ignore interfaceName, routingMark and tfo when DefaultSocketHook not null (in CFMA)
+ socketHookToToDialer(dialer)
+ } else {
+ if opt.interfaceName != "" {
+ bind := bindIfaceToDialer
+ if opt.fallbackBind {
+ bind = fallbackBindIfaceToDialer
+ }
+ if err := bind(opt.interfaceName, dialer, network, destination); err != nil {
+ return nil, err
+ }
+ }
+ if opt.routingMark != 0 {
+ bindMarkToDialer(opt.routingMark, dialer, network, destination)
+ }
+ if opt.tfo && !DisableTFO {
+ return dialTFO(ctx, *dialer, network, address)
+ }
}
- if opt.interfaceName != "" {
- bind := bindIfaceToDialer
- if opt.fallbackBind {
- bind = fallbackBindIfaceToDialer
- }
- if err := bind(opt.interfaceName, dialer, network, destination); err != nil {
- return nil, err
- }
- }
- if opt.routingMark != 0 {
- bindMarkToDialer(opt.routingMark, dialer, network, destination)
- }
- if opt.tfo && !DisableTFO {
- return dialTFO(ctx, *dialer, network, address)
- }
return dialer.DialContext(ctx, network, address)
}
diff --git a/clash-meta/component/dialer/patch_android.go b/clash-meta/component/dialer/patch_android.go
deleted file mode 100644
index 079b9772ab..0000000000
--- a/clash-meta/component/dialer/patch_android.go
+++ /dev/null
@@ -1,38 +0,0 @@
-//go:build android && cmfa
-
-package dialer
-
-import (
- "context"
- "net"
- "syscall"
-)
-
-type SocketControl func(network, address string, conn syscall.RawConn) error
-
-var DefaultSocketHook SocketControl
-
-func dialContextHooked(ctx context.Context, dialer *net.Dialer, network string, address string) (net.Conn, error) {
- addControlToDialer(dialer, func(ctx context.Context, network, address string, c syscall.RawConn) error {
- return DefaultSocketHook(network, address, c)
- })
-
- conn, err := dialer.DialContext(ctx, network, address)
- if err != nil {
- return nil, err
- }
-
- if t, ok := conn.(*net.TCPConn); ok {
- t.SetKeepAlive(false)
- }
-
- return conn, nil
-}
-
-func listenPacketHooked(ctx context.Context, network, address string) (net.PacketConn, error) {
- lc := &net.ListenConfig{
- Control: DefaultSocketHook,
- }
-
- return lc.ListenPacket(ctx, network, address)
-}
diff --git a/clash-meta/component/dialer/patch_common.go b/clash-meta/component/dialer/patch_common.go
deleted file mode 100644
index 2c96fe60b7..0000000000
--- a/clash-meta/component/dialer/patch_common.go
+++ /dev/null
@@ -1,21 +0,0 @@
-//go:build !(android && cmfa)
-
-package dialer
-
-import (
- "context"
- "net"
- "syscall"
-)
-
-type SocketControl func(network, address string, conn syscall.RawConn) error
-
-var DefaultSocketHook SocketControl
-
-func dialContextHooked(ctx context.Context, dialer *net.Dialer, network string, address string) (net.Conn, error) {
- return nil, nil
-}
-
-func listenPacketHooked(ctx context.Context, network, address string) (net.PacketConn, error) {
- return nil, nil
-}
diff --git a/clash-meta/component/dialer/socket_hook.go b/clash-meta/component/dialer/socket_hook.go
new file mode 100644
index 0000000000..7a2ea43215
--- /dev/null
+++ b/clash-meta/component/dialer/socket_hook.go
@@ -0,0 +1,27 @@
+package dialer
+
+import (
+ "context"
+ "net"
+ "syscall"
+)
+
+// SocketControl
+// never change type traits because it's used in CFMA
+type SocketControl func(network, address string, conn syscall.RawConn) error
+
+// DefaultSocketHook
+// never change type traits because it's used in CFMA
+var DefaultSocketHook SocketControl
+
+func socketHookToToDialer(dialer *net.Dialer) {
+ addControlToDialer(dialer, func(ctx context.Context, network, address string, c syscall.RawConn) error {
+ return DefaultSocketHook(network, address, c)
+ })
+}
+
+func socketHookToListenConfig(lc *net.ListenConfig) {
+ addControlToListenConfig(lc, func(ctx context.Context, network, address string, c syscall.RawConn) error {
+ return DefaultSocketHook(network, address, c)
+ })
+}
diff --git a/clash-meta/component/fakeip/pool.go b/clash-meta/component/fakeip/pool.go
index 2b06fc0bdf..8096a868af 100644
--- a/clash-meta/component/fakeip/pool.go
+++ b/clash-meta/component/fakeip/pool.go
@@ -8,7 +8,7 @@ import (
"github.com/metacubex/mihomo/common/nnip"
"github.com/metacubex/mihomo/component/profile/cachefile"
- "github.com/metacubex/mihomo/component/trie"
+ C "github.com/metacubex/mihomo/constant"
)
const (
@@ -35,7 +35,7 @@ type Pool struct {
offset netip.Addr
cycle bool
mux sync.Mutex
- host *trie.DomainTrie[struct{}]
+ host []C.Rule
ipnet netip.Prefix
store store
}
@@ -66,10 +66,12 @@ func (p *Pool) LookBack(ip netip.Addr) (string, bool) {
// ShouldSkipped return if domain should be skipped
func (p *Pool) ShouldSkipped(domain string) bool {
- if p.host == nil {
- return false
+ for _, rule := range p.host {
+ if match, _ := rule.Match(&C.Metadata{Host: domain}); match {
+ return true
+ }
}
- return p.host.Search(domain) != nil
+ return false
}
// Exist returns if given ip exists in fake-ip pool
@@ -154,7 +156,7 @@ func (p *Pool) restoreState() {
type Options struct {
IPNet netip.Prefix
- Host *trie.DomainTrie[struct{}]
+ Host []C.Rule
// Size sets the maximum number of entries in memory
// and does not work if Persistence is true
diff --git a/clash-meta/component/fakeip/pool_test.go b/clash-meta/component/fakeip/pool_test.go
index cc50fcf7b5..9c05a32778 100644
--- a/clash-meta/component/fakeip/pool_test.go
+++ b/clash-meta/component/fakeip/pool_test.go
@@ -9,6 +9,8 @@ import (
"github.com/metacubex/mihomo/component/profile/cachefile"
"github.com/metacubex/mihomo/component/trie"
+ C "github.com/metacubex/mihomo/constant"
+ RP "github.com/metacubex/mihomo/rules/provider"
"github.com/sagernet/bbolt"
"github.com/stretchr/testify/assert"
@@ -154,7 +156,7 @@ func TestPool_Skip(t *testing.T) {
pools, tempfile, err := createPools(Options{
IPNet: ipnet,
Size: 10,
- Host: tree,
+ Host: []C.Rule{RP.NewDomainSet(tree.NewDomainSet(), "")},
})
assert.Nil(t, err)
defer os.Remove(tempfile)
diff --git a/clash-meta/component/sniffer/dispatcher.go b/clash-meta/component/sniffer/dispatcher.go
index 4438638dad..c96f5a4b03 100644
--- a/clash-meta/component/sniffer/dispatcher.go
+++ b/clash-meta/component/sniffer/dispatcher.go
@@ -9,7 +9,6 @@ import (
"github.com/metacubex/mihomo/common/lru"
N "github.com/metacubex/mihomo/common/net"
- "github.com/metacubex/mihomo/component/trie"
C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/constant/sniffer"
"github.com/metacubex/mihomo/log"
@@ -26,17 +25,26 @@ var Dispatcher *SnifferDispatcher
type SnifferDispatcher struct {
enable bool
sniffers map[sniffer.Sniffer]SnifferConfig
- forceDomain *trie.DomainSet
- skipSNI *trie.DomainSet
+ forceDomain []C.Rule
+ skipDomain []C.Rule
skipList *lru.LruCache[string, uint8]
forceDnsMapping bool
parsePureIp bool
}
func (sd *SnifferDispatcher) shouldOverride(metadata *C.Metadata) bool {
- return (metadata.Host == "" && sd.parsePureIp) ||
- sd.forceDomain.Has(metadata.Host) ||
- (metadata.DNSMode == C.DNSMapping && sd.forceDnsMapping)
+ if metadata.Host == "" && sd.parsePureIp {
+ return true
+ }
+ if metadata.DNSMode == C.DNSMapping && sd.forceDnsMapping {
+ return true
+ }
+ for _, rule := range sd.forceDomain {
+ if ok, _ := rule.Match(&C.Metadata{Host: metadata.Host}); ok {
+ return true
+ }
+ }
+ return false
}
func (sd *SnifferDispatcher) UDPSniff(packet C.PacketAdapter) bool {
@@ -94,9 +102,11 @@ func (sd *SnifferDispatcher) TCPSniff(conn *N.BufferedConn, metadata *C.Metadata
log.Debugln("[Sniffer] All sniffing sniff failed with from [%s:%d] to [%s:%d]", metadata.SrcIP, metadata.SrcPort, metadata.String(), metadata.DstPort)
return false
} else {
- if sd.skipSNI.Has(host) {
- log.Debugln("[Sniffer] Skip sni[%s]", host)
- return false
+ for _, rule := range sd.skipDomain {
+ if ok, _ := rule.Match(&C.Metadata{Host: host}); ok {
+ log.Debugln("[Sniffer] Skip sni[%s]", host)
+ return false
+ }
}
sd.skipList.Delete(dst)
@@ -187,12 +197,12 @@ func NewCloseSnifferDispatcher() (*SnifferDispatcher, error) {
}
func NewSnifferDispatcher(snifferConfig map[sniffer.Type]SnifferConfig,
- forceDomain *trie.DomainSet, skipSNI *trie.DomainSet,
+ forceDomain []C.Rule, skipDomain []C.Rule,
forceDnsMapping bool, parsePureIp bool) (*SnifferDispatcher, error) {
dispatcher := SnifferDispatcher{
enable: true,
forceDomain: forceDomain,
- skipSNI: skipSNI,
+ skipDomain: skipDomain,
skipList: lru.New(lru.WithSize[string, uint8](128), lru.WithAge[string, uint8](600)),
forceDnsMapping: forceDnsMapping,
parsePureIp: parsePureIp,
diff --git a/clash-meta/component/trie/domain.go b/clash-meta/component/trie/domain.go
index db30402ede..6d3e37f70a 100644
--- a/clash-meta/component/trie/domain.go
+++ b/clash-meta/component/trie/domain.go
@@ -134,6 +134,13 @@ func (t *DomainTrie[T]) Foreach(fn func(domain string, data T) bool) {
}
}
+func (t *DomainTrie[T]) IsEmpty() bool {
+ if t == nil {
+ return true
+ }
+ return t.root.isEmpty()
+}
+
func recursion[T any](items []string, node *Node[T], fn func(domain string, data T) bool) bool {
for key, data := range node.getChildren() {
newItems := append([]string{key}, items...)
diff --git a/clash-meta/config/config.go b/clash-meta/config/config.go
index 5f2b68453d..74ffdd038c 100644
--- a/clash-meta/config/config.go
+++ b/clash-meta/config/config.go
@@ -38,6 +38,7 @@ import (
LC "github.com/metacubex/mihomo/listener/config"
"github.com/metacubex/mihomo/log"
R "github.com/metacubex/mihomo/rules"
+ RC "github.com/metacubex/mihomo/rules/common"
RP "github.com/metacubex/mihomo/rules/provider"
T "github.com/metacubex/mihomo/tunnel"
@@ -163,8 +164,8 @@ type IPTables struct {
type Sniffer struct {
Enable bool
Sniffers map[snifferTypes.Type]SNIFF.SnifferConfig
- ForceDomain *trie.DomainSet
- SkipDomain *trie.DomainSet
+ ForceDomain []C.Rule
+ SkipDomain []C.Rule
ForceDnsMapping bool
ParsePureIp bool
}
@@ -338,7 +339,9 @@ type RawConfig struct {
FindProcessMode P.FindProcessMode `yaml:"find-process-mode" json:"find-process-mode"`
GlobalClientFingerprint string `yaml:"global-client-fingerprint"`
GlobalUA string `yaml:"global-ua"`
+ KeepAliveIdle int `yaml:"keep-alive-idle"`
KeepAliveInterval int `yaml:"keep-alive-interval"`
+ DisableKeepAlive bool `yaml:"disable-keep-alive"`
Sniffer RawSniffer `yaml:"sniffer" json:"sniffer"`
ProxyProvider map[string]map[string]any `yaml:"proxy-providers"`
@@ -624,7 +627,7 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) {
}
}
- config.Sniffer, err = parseSniffer(rawCfg.Sniffer)
+ config.Sniffer, err = parseSniffer(rawCfg.Sniffer, rules, ruleProviders)
if err != nil {
return nil, err
}
@@ -649,9 +652,14 @@ func parseGeneral(cfg *RawConfig) (*General, error) {
C.ASNUrl = cfg.GeoXUrl.ASN
C.GeodataMode = cfg.GeodataMode
C.UA = cfg.GlobalUA
+
+ if cfg.KeepAliveIdle != 0 {
+ N.KeepAliveIdle = time.Duration(cfg.KeepAliveIdle) * time.Second
+ }
if cfg.KeepAliveInterval != 0 {
N.KeepAliveInterval = time.Duration(cfg.KeepAliveInterval) * time.Second
}
+ N.DisableKeepAlive = cfg.DisableKeepAlive
updater.ExternalUIPath = cfg.ExternalUI
// checkout externalUI exist
@@ -1400,27 +1408,21 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rul
return nil, err
}
- var host *trie.DomainTrie[struct{}]
- // fake ip skip host filter
- if len(cfg.FakeIPFilter) != 0 {
- host = trie.New[struct{}]()
- for _, domain := range cfg.FakeIPFilter {
- _ = host.Insert(domain, struct{}{})
- }
- host.Optimize()
- }
-
+ var fakeIPTrie *trie.DomainTrie[struct{}]
if len(dnsCfg.Fallback) != 0 {
- if host == nil {
- host = trie.New[struct{}]()
- }
+ fakeIPTrie = trie.New[struct{}]()
for _, fb := range dnsCfg.Fallback {
if net.ParseIP(fb.Addr) != nil {
continue
}
- _ = host.Insert(fb.Addr, struct{}{})
+ _ = fakeIPTrie.Insert(fb.Addr, struct{}{})
}
- host.Optimize()
+ }
+
+ // fake ip skip host filter
+ host, err := parseDomain(cfg.FakeIPFilter, fakeIPTrie, rules, ruleProviders)
+ if err != nil {
+ return nil, err
}
pool, err := fakeip.New(fakeip.Options{
@@ -1547,7 +1549,7 @@ func parseTuicServer(rawTuic RawTuicServer, general *General) error {
return nil
}
-func parseSniffer(snifferRaw RawSniffer) (*Sniffer, error) {
+func parseSniffer(snifferRaw RawSniffer, rules []C.Rule, ruleProviders map[string]providerTypes.RuleProvider) (*Sniffer, error) {
sniffer := &Sniffer{
Enable: snifferRaw.Enable,
ForceDnsMapping: snifferRaw.ForceDnsMapping,
@@ -1610,23 +1612,83 @@ func parseSniffer(snifferRaw RawSniffer) (*Sniffer, error) {
sniffer.Sniffers = loadSniffer
- forceDomainTrie := trie.New[struct{}]()
- for _, domain := range snifferRaw.ForceDomain {
- err := forceDomainTrie.Insert(domain, struct{}{})
- if err != nil {
- return nil, fmt.Errorf("error domian[%s] in force-domain, error:%v", domain, err)
- }
+ forceDomain, err := parseDomain(snifferRaw.ForceDomain, nil, rules, ruleProviders)
+ if err != nil {
+ return nil, fmt.Errorf("error in force-domain, error:%w", err)
}
- sniffer.ForceDomain = forceDomainTrie.NewDomainSet()
+ sniffer.ForceDomain = forceDomain
- skipDomainTrie := trie.New[struct{}]()
- for _, domain := range snifferRaw.SkipDomain {
- err := skipDomainTrie.Insert(domain, struct{}{})
- if err != nil {
- return nil, fmt.Errorf("error domian[%s] in force-domain, error:%v", domain, err)
- }
+ skipDomain, err := parseDomain(snifferRaw.SkipDomain, nil, rules, ruleProviders)
+ if err != nil {
+ return nil, fmt.Errorf("error in skip-domain, error:%w", err)
}
- sniffer.SkipDomain = skipDomainTrie.NewDomainSet()
+ sniffer.SkipDomain = skipDomain
return sniffer, nil
}
+
+func parseDomain(domains []string, domainTrie *trie.DomainTrie[struct{}], rules []C.Rule, ruleProviders map[string]providerTypes.RuleProvider) (domainRules []C.Rule, err error) {
+ var rule C.Rule
+ for _, domain := range domains {
+ domainLower := strings.ToLower(domain)
+ if strings.Contains(domainLower, "geosite:") {
+ subkeys := strings.Split(domain, ":")
+ subkeys = subkeys[1:]
+ subkeys = strings.Split(subkeys[0], ",")
+ for _, country := range subkeys {
+ found := false
+ for _, rule = range rules {
+ if rule.RuleType() == C.GEOSITE {
+ if strings.EqualFold(country, rule.Payload()) {
+ found = true
+ domainRules = append(domainRules, rule)
+ }
+ }
+ }
+ if !found {
+ rule, err = RC.NewGEOSITE(country, "")
+ if err != nil {
+ return nil, err
+ }
+ domainRules = append(domainRules, rule)
+ }
+ }
+ } else if strings.Contains(domainLower, "rule-set:") {
+ subkeys := strings.Split(domain, ":")
+ subkeys = subkeys[1:]
+ subkeys = strings.Split(subkeys[0], ",")
+ for _, domainSetName := range subkeys {
+ if rp, ok := ruleProviders[domainSetName]; !ok {
+ return nil, fmt.Errorf("not found rule-set: %s", domainSetName)
+ } else {
+ switch rp.Behavior() {
+ case providerTypes.IPCIDR:
+ return nil, fmt.Errorf("rule provider type error, except domain,actual %s", rp.Behavior())
+ case providerTypes.Classical:
+ log.Warnln("%s provider is %s, only matching it contain domain rule", rp.Name(), rp.Behavior())
+ default:
+ }
+ }
+ rule, err = RP.NewRuleSet(domainSetName, "", true)
+ if err != nil {
+ return nil, err
+ }
+
+ domainRules = append(domainRules, rule)
+ }
+ } else {
+ if domainTrie == nil {
+ domainTrie = trie.New[struct{}]()
+ }
+ err = domainTrie.Insert(domain, struct{}{})
+ if err != nil {
+ return nil, err
+ }
+ }
+ }
+ if !domainTrie.IsEmpty() {
+ rule = RP.NewDomainSet(domainTrie.NewDomainSet(), "")
+ domainRules = append(domainRules, rule)
+ }
+ return
+}
diff --git a/clash-meta/constant/rule.go b/clash-meta/constant/rule.go
index a91ee6cb07..f9f987e672 100644
--- a/clash-meta/constant/rule.go
+++ b/clash-meta/constant/rule.go
@@ -27,6 +27,7 @@ const (
ProcessNameRegex
ProcessPathRegex
RuleSet
+ DomainSet
Network
Uid
SubRules
@@ -90,6 +91,8 @@ func (rt RuleType) String() string {
return "Match"
case RuleSet:
return "RuleSet"
+ case DomainSet:
+ return "DomainSet"
case Network:
return "Network"
case DSCP:
diff --git a/clash-meta/docs/config.yaml b/clash-meta/docs/config.yaml
index d7c686d01f..8db06b6d7d 100644
--- a/clash-meta/docs/config.yaml
+++ b/clash-meta/docs/config.yaml
@@ -82,7 +82,9 @@ external-doh-server: /dns-query
global-client-fingerprint: chrome
# TCP keep alive interval
-keep-alive-interval: 15
+# disable-keep-alive: false #目前在android端强制为true
+# keep-alive-idle: 15
+# keep-alive-interval: 15
# routing-mark:6666 # 配置 fwmark 仅用于 Linux
experimental:
@@ -241,6 +243,16 @@ dns:
fake-ip-range: 198.18.0.1/16 # fake-ip 池设置
+ # 配置不使用 fake-ip 的域名
+ fake-ip-filter:
+ - '*.lan'
+ - localhost.ptlogin2.qq.com
+ # fakeip-filter 为 rule-providers 中的名为 fakeip-filter 规则订阅,
+ # 且 behavior 必须为 domain/classical,当为 classical 时仅会生效域名类规则
+ - rule-set:fakeip-filter
+ # fakeip-filter 为 geosite 中名为 fakeip-filter 的分类(需要自行保证该分类存在)
+ - geosite:fakeip-filter
+
# use-hosts: true # 查询 hosts
# 配置后面的nameserver、fallback和nameserver-policy向dns服务器的连接过程是否遵守遵守rules规则
@@ -250,11 +262,6 @@ dns:
# 此外,这三者配置中的dns服务器如果出现域名会采用default-nameserver配置项解析,也请确保正确配置default-nameserver
respect-rules: false
- # 配置不使用 fake-ip 的域名
- # fake-ip-filter:
- # - '*.lan'
- # - localhost.ptlogin2.qq.com
-
# DNS 主要域名配置
# 支持 UDP,TCP,DoT,DoH,DoQ
# 这部分为主要 DNS 配置,影响所有直连,确保使用对大陆解析精准的 DNS
diff --git a/clash-meta/listener/http/server.go b/clash-meta/listener/http/server.go
index 77e10f0841..f61dd03609 100644
--- a/clash-meta/listener/http/server.go
+++ b/clash-meta/listener/http/server.go
@@ -4,9 +4,9 @@ import (
"net"
"github.com/metacubex/mihomo/adapter/inbound"
+ N "github.com/metacubex/mihomo/common/net"
"github.com/metacubex/mihomo/component/auth"
C "github.com/metacubex/mihomo/constant"
- "github.com/metacubex/mihomo/constant/features"
authStore "github.com/metacubex/mihomo/listener/auth"
)
@@ -74,11 +74,7 @@ func NewWithAuthenticator(addr string, tunnel C.Tunnel, authenticator auth.Authe
}
continue
}
- if features.CMFA {
- if t, ok := conn.(*net.TCPConn); ok {
- t.SetKeepAlive(false)
- }
- }
+ N.TCPKeepAlive(conn)
if isDefault { // only apply on default listener
if !inbound.IsRemoteAddrDisAllowed(conn.RemoteAddr()) {
_ = conn.Close()
diff --git a/clash-meta/rules/provider/domain_set.go b/clash-meta/rules/provider/domain_set.go
new file mode 100644
index 0000000000..372b438ea8
--- /dev/null
+++ b/clash-meta/rules/provider/domain_set.go
@@ -0,0 +1,43 @@
+package provider
+
+import (
+ "github.com/metacubex/mihomo/component/trie"
+ C "github.com/metacubex/mihomo/constant"
+)
+
+type DomainSet struct {
+ *domainStrategy
+ adapter string
+}
+
+func (d *DomainSet) ProviderNames() []string {
+ return nil
+}
+
+func (d *DomainSet) RuleType() C.RuleType {
+ return C.DomainSet
+}
+
+func (d *DomainSet) Match(metadata *C.Metadata) (bool, string) {
+ if d.domainSet == nil {
+ return false, ""
+ }
+ return d.domainSet.Has(metadata.RuleHost()), d.adapter
+}
+
+func (d *DomainSet) Adapter() string {
+ return d.adapter
+}
+
+func (d *DomainSet) Payload() string {
+ return ""
+}
+
+func NewDomainSet(domainSet *trie.DomainSet, adapter string) *DomainSet {
+ return &DomainSet{
+ domainStrategy: &domainStrategy{domainSet: domainSet},
+ adapter: adapter,
+ }
+}
+
+var _ C.Rule = (*DomainSet)(nil)
diff --git a/clash-nyanpasu/backend/Cargo.lock b/clash-nyanpasu/backend/Cargo.lock
index ca703b8f7b..09d5278234 100644
--- a/clash-nyanpasu/backend/Cargo.lock
+++ b/clash-nyanpasu/backend/Cargo.lock
@@ -774,7 +774,7 @@ dependencies = [
"bitflags 2.6.0",
"boa_interner",
"boa_macros",
- "indexmap 2.3.0",
+ "indexmap 2.4.0",
"num-bigint",
"rustc-hash 2.0.0",
]
@@ -800,7 +800,7 @@ dependencies = [
"fast-float",
"hashbrown 0.14.5",
"icu_normalizer",
- "indexmap 2.3.0",
+ "indexmap 2.4.0",
"intrusive-collections",
"itertools 0.13.0",
"num-bigint",
@@ -846,7 +846,7 @@ dependencies = [
"boa_gc",
"boa_macros",
"hashbrown 0.14.5",
- "indexmap 2.3.0",
+ "indexmap 2.4.0",
"once_cell",
"phf 0.11.2",
"rustc-hash 2.0.0",
@@ -1265,7 +1265,7 @@ dependencies = [
"hex",
"humansize",
"image 0.25.2",
- "indexmap 2.3.0",
+ "indexmap 2.4.0",
"log 0.4.22",
"md-5",
"mime",
@@ -1667,12 +1667,12 @@ dependencies = [
[[package]]
name = "ctrlc"
-version = "3.4.4"
+version = "3.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "672465ae37dc1bc6380a6547a8883d5dd397b0f1faaad4f265726cc7042a5345"
+checksum = "90eeab0aa92f3f9b4e87f258c72b139c207d251f9cbc1080a0086b86a8870dd3"
dependencies = [
- "nix 0.28.0",
- "windows-sys 0.52.0",
+ "nix 0.29.0",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -2911,7 +2911,7 @@ dependencies = [
"futures-sink",
"futures-util",
"http 0.2.12",
- "indexmap 2.3.0",
+ "indexmap 2.4.0",
"slab",
"tokio",
"tokio-util",
@@ -2930,7 +2930,7 @@ dependencies = [
"futures-core",
"futures-sink",
"http 1.1.0",
- "indexmap 2.3.0",
+ "indexmap 2.4.0",
"slab",
"tokio",
"tokio-util",
@@ -3481,9 +3481,9 @@ dependencies = [
[[package]]
name = "indexmap"
-version = "2.3.0"
+version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0"
+checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c"
dependencies = [
"equivalent",
"hashbrown 0.14.5",
@@ -5252,7 +5252,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db"
dependencies = [
"fixedbitset",
- "indexmap 2.3.0",
+ "indexmap 2.4.0",
]
[[package]]
@@ -5455,7 +5455,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42cf17e9a1800f5f396bc67d193dc9411b59012a5876445ef450d449881e1016"
dependencies = [
"base64 0.22.1",
- "indexmap 2.3.0",
+ "indexmap 2.4.0",
"quick-xml 0.32.0",
"serde",
"time",
@@ -6450,7 +6450,7 @@ version = "1.0.124"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66ad62847a56b3dba58cc891acd13884b9c61138d330c0d7b6181713d4fce38d"
dependencies = [
- "indexmap 2.3.0",
+ "indexmap 2.4.0",
"itoa 1.0.11",
"memchr",
"ryu",
@@ -6509,7 +6509,7 @@ dependencies = [
"chrono",
"hex",
"indexmap 1.9.3",
- "indexmap 2.3.0",
+ "indexmap 2.4.0",
"serde",
"serde_derive",
"serde_json",
@@ -6535,7 +6535,7 @@ version = "0.9.34+deprecated"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
dependencies = [
- "indexmap 2.3.0",
+ "indexmap 2.4.0",
"itoa 1.0.11",
"ryu",
"serde",
@@ -6548,7 +6548,7 @@ version = "0.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48e76bab63c3fd98d27c17f9cbce177f64a91f5e69ac04cafe04e1bb25d1dc3c"
dependencies = [
- "indexmap 2.3.0",
+ "indexmap 2.4.0",
"itoa 1.0.11",
"libyml",
"log 0.4.22",
@@ -7790,7 +7790,7 @@ version = "0.19.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
dependencies = [
- "indexmap 2.3.0",
+ "indexmap 2.4.0",
"serde",
"serde_spanned",
"toml_datetime",
@@ -7803,7 +7803,7 @@ version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1"
dependencies = [
- "indexmap 2.3.0",
+ "indexmap 2.4.0",
"toml_datetime",
"winnow 0.5.40",
]
@@ -7814,7 +7814,7 @@ version = "0.22.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d"
dependencies = [
- "indexmap 2.3.0",
+ "indexmap 2.4.0",
"serde",
"serde_spanned",
"toml_datetime",
@@ -9643,7 +9643,7 @@ dependencies = [
"displaydoc",
"flate2",
"hmac",
- "indexmap 2.3.0",
+ "indexmap 2.4.0",
"lzma-rs",
"memchr",
"pbkdf2",
diff --git a/clash-nyanpasu/frontend/nyanpasu/package.json b/clash-nyanpasu/frontend/nyanpasu/package.json
index 54e63fafe2..08cade995e 100644
--- a/clash-nyanpasu/frontend/nyanpasu/package.json
+++ b/clash-nyanpasu/frontend/nyanpasu/package.json
@@ -24,7 +24,6 @@
"@tauri-apps/api": "1.6.0",
"ahooks": "3.8.1",
"allotment": "1.20.2",
- "axios": "1.7.3",
"country-code-emoji": "2.3.0",
"dayjs": "1.11.12",
"framer-motion": "12.0.0-alpha.0",
@@ -48,7 +47,7 @@
"devDependencies": {
"@emotion/babel-plugin": "11.12.0",
"@emotion/react": "11.13.0",
- "@iconify/json": "2.2.236",
+ "@iconify/json": "2.2.237",
"@types/react": "18.3.3",
"@types/react-dom": "18.3.0",
"@vitejs/plugin-react": "4.3.1",
diff --git a/clash-nyanpasu/manifest/version.json b/clash-nyanpasu/manifest/version.json
index f5c6e063a8..c2378752f9 100644
--- a/clash-nyanpasu/manifest/version.json
+++ b/clash-nyanpasu/manifest/version.json
@@ -2,7 +2,7 @@
"manifest_version": 1,
"latest": {
"mihomo": "v1.18.7",
- "mihomo_alpha": "alpha-c17d7c0",
+ "mihomo_alpha": "alpha-50d0cd3",
"clash_rs": "v0.2.0",
"clash_premium": "2023-09-05-gdcc8d87"
},
@@ -36,5 +36,5 @@
"darwin-x64": "clash-darwin-amd64-n{}.gz"
}
},
- "updated_at": "2024-08-12T22:20:24.265Z"
+ "updated_at": "2024-08-13T22:20:21.347Z"
}
diff --git a/clash-nyanpasu/package.json b/clash-nyanpasu/package.json
index e19b0694b4..14fe205f95 100644
--- a/clash-nyanpasu/package.json
+++ b/clash-nyanpasu/package.json
@@ -94,7 +94,7 @@
"stylelint-declaration-block-no-ignored-properties": "2.8.0",
"stylelint-order": "6.0.4",
"stylelint-scss": "6.5.0",
- "tailwindcss": "3.4.9",
+ "tailwindcss": "3.4.10",
"tsx": "4.17.0",
"typescript": "5.5.4"
},
diff --git a/clash-nyanpasu/pnpm-lock.yaml b/clash-nyanpasu/pnpm-lock.yaml
index be1e61d29a..b241133d17 100644
--- a/clash-nyanpasu/pnpm-lock.yaml
+++ b/clash-nyanpasu/pnpm-lock.yaml
@@ -152,8 +152,8 @@ importers:
specifier: 6.5.0
version: 6.5.0(stylelint@16.8.1(typescript@5.5.4))
tailwindcss:
- specifier: 3.4.9
- version: 3.4.9
+ specifier: 3.4.10
+ version: 3.4.10
tsx:
specifier: 4.17.0
version: 4.17.0
@@ -230,9 +230,6 @@ importers:
allotment:
specifier: 1.20.2
version: 1.20.2(react-dom@19.0.0-rc-e948a5ac-20240807(react@19.0.0-rc-e948a5ac-20240807))(react@19.0.0-rc-e948a5ac-20240807)
- axios:
- specifier: 1.7.3
- version: 1.7.3
country-code-emoji:
specifier: 2.3.0
version: 2.3.0
@@ -298,8 +295,8 @@ importers:
specifier: 11.13.0
version: 11.13.0(react@19.0.0-rc-e948a5ac-20240807)(types-react@19.0.0-rc.1)
'@iconify/json':
- specifier: 2.2.236
- version: 2.2.236
+ specifier: 2.2.237
+ version: 2.2.237
'@types/react':
specifier: npm:types-react@rc
version: types-react@19.0.0-rc.1
@@ -1359,8 +1356,8 @@ packages:
'@vue/compiler-sfc':
optional: true
- '@iconify/json@2.2.236':
- resolution: {integrity: sha512-eiIOW9RfIMlrJl77+VfM4FM+bF46zQNxanNe8MADgXxNCeFR8LW70bC2u6El5Cqf/gx/n0EmqA5kXQ8J+BHjsg==}
+ '@iconify/json@2.2.237':
+ resolution: {integrity: sha512-lMK5I9JX90L2LR18Lz6C5XrivxxAGejdJvFQI7TQkj03WcaxRJlciPbQb6z4nX+DS1T6k591ZQ2/hyRVbjqrgA==}
'@iconify/types@2.0.0':
resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==}
@@ -2677,9 +2674,6 @@ packages:
async-mutex@0.3.2:
resolution: {integrity: sha512-HuTK7E7MT7jZEh1P9GtRW9+aTWiDWWi9InbZ5hjxrnRa39KS4BW04+xLBhYNS2aXhHUIKZSw3gj4Pn1pj+qGAA==}
- asynckit@0.4.0:
- resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
-
autoprefixer@10.4.20:
resolution: {integrity: sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==}
engines: {node: ^10 || ^12 || >=14}
@@ -2695,9 +2689,6 @@ packages:
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
engines: {node: '>= 0.4'}
- axios@1.7.3:
- resolution: {integrity: sha512-Ar7ND9pU99eJ9GpoGQKhKf58GpUOgnzuaB7ueNQ5BMi0p+LZ5oaEnfF999fAArcTIBwXTCHAmGcHOZJaWPq9Nw==}
-
babel-plugin-macros@3.1.0:
resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==}
engines: {node: '>=10', npm: '>=6'}
@@ -2927,10 +2918,6 @@ packages:
colorize-template@1.0.0:
resolution: {integrity: sha512-beJ9v9RjpbYZ8OdwJgIRZD3YUkZPXmi1MK+yX0J24UupKVHa9yk0jiARgt2i6MBX6AKjYA0SNsBn65bUPuVQiw==}
- combined-stream@1.0.8:
- resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
- engines: {node: '>= 0.8'}
-
comma-separated-tokens@2.0.3:
resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==}
@@ -3303,10 +3290,6 @@ packages:
delaunator@5.0.1:
resolution: {integrity: sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==}
- delayed-stream@1.0.0:
- resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
- engines: {node: '>=0.4.0'}
-
deprecation@2.3.1:
resolution: {integrity: sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==}
@@ -3780,15 +3763,6 @@ packages:
flatted@3.3.1:
resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==}
- follow-redirects@1.15.6:
- resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==}
- engines: {node: '>=4.0'}
- peerDependencies:
- debug: '*'
- peerDependenciesMeta:
- debug:
- optional: true
-
for-each@0.3.3:
resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
@@ -3796,10 +3770,6 @@ packages:
resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==}
engines: {node: '>=14'}
- form-data@4.0.0:
- resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==}
- engines: {node: '>= 6'}
-
formdata-polyfill@4.0.10:
resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==}
engines: {node: '>=12.20.0'}
@@ -4755,14 +4725,6 @@ packages:
resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==}
engines: {node: '>=8.6'}
- mime-db@1.52.0:
- resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
- engines: {node: '>= 0.6'}
-
- mime-types@2.1.35:
- resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
- engines: {node: '>= 0.6'}
-
mime@1.6.0:
resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==}
engines: {node: '>=4'}
@@ -5395,9 +5357,6 @@ packages:
property-information@6.5.0:
resolution: {integrity: sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==}
- proxy-from-env@1.1.0:
- resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
-
prr@1.0.1:
resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==}
@@ -6020,8 +5979,8 @@ packages:
engines: {node: '>=8.9.0'}
hasBin: true
- tailwindcss@3.4.9:
- resolution: {integrity: sha512-1SEOvRr6sSdV5IDf9iC+NU4dhwdqzF4zKKq3sAbasUWHEM6lsMhX+eNN5gkPx1BvLFEnZQEUFbXnGj8Qlp83Pg==}
+ tailwindcss@3.4.10:
+ resolution: {integrity: sha512-KWZkVPm7yJRhdu4SRSl9d4AK2wM3a50UsvgHZO7xY77NQr2V+fIrEuoDGQcbvswWvFGbS2f6e+jC/6WJm1Dl0w==}
engines: {node: '>=14.0.0'}
hasBin: true
@@ -7408,7 +7367,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@iconify/json@2.2.236':
+ '@iconify/json@2.2.237':
dependencies:
'@iconify/types': 2.0.0
pathe: 1.1.2
@@ -8760,8 +8719,6 @@ snapshots:
dependencies:
tslib: 2.6.2
- asynckit@0.4.0: {}
-
autoprefixer@10.4.20(postcss@8.4.41):
dependencies:
browserslist: 4.23.3
@@ -8786,14 +8743,6 @@ snapshots:
dependencies:
possible-typed-array-names: 1.0.0
- axios@1.7.3:
- dependencies:
- follow-redirects: 1.15.6
- form-data: 4.0.0
- proxy-from-env: 1.1.0
- transitivePeerDependencies:
- - debug
-
babel-plugin-macros@3.1.0:
dependencies:
'@babel/runtime': 7.24.8
@@ -9016,10 +8965,6 @@ snapshots:
colorize-template@1.0.0: {}
- combined-stream@1.0.8:
- dependencies:
- delayed-stream: 1.0.0
-
comma-separated-tokens@2.0.3: {}
commander@12.1.0: {}
@@ -9391,8 +9336,6 @@ snapshots:
dependencies:
robust-predicates: 3.0.2
- delayed-stream@1.0.0: {}
-
deprecation@2.3.1: {}
dequal@2.0.3: {}
@@ -10089,8 +10032,6 @@ snapshots:
flatted@3.3.1: {}
- follow-redirects@1.15.6: {}
-
for-each@0.3.3:
dependencies:
is-callable: 1.2.7
@@ -10100,12 +10041,6 @@ snapshots:
cross-spawn: 7.0.3
signal-exit: 4.1.0
- form-data@4.0.0:
- dependencies:
- asynckit: 0.4.0
- combined-stream: 1.0.8
- mime-types: 2.1.35
-
formdata-polyfill@4.0.10:
dependencies:
fetch-blob: 3.2.0
@@ -11179,12 +11114,6 @@ snapshots:
braces: 3.0.3
picomatch: 2.3.1
- mime-db@1.52.0: {}
-
- mime-types@2.1.35:
- dependencies:
- mime-db: 1.52.0
-
mime@1.6.0:
optional: true
@@ -11723,8 +11652,6 @@ snapshots:
property-information@6.5.0: {}
- proxy-from-env@1.1.0: {}
-
prr@1.0.1:
optional: true
@@ -12465,7 +12392,7 @@ snapshots:
reduce-css-calc: 2.1.8
resolve: 1.22.8
- tailwindcss@3.4.9:
+ tailwindcss@3.4.10:
dependencies:
'@alloc/quick-lru': 5.2.0
arg: 5.0.2
diff --git a/glider/Dockerfile b/glider/Dockerfile
index 73794eacbc..953f5d2409 100644
--- a/glider/Dockerfile
+++ b/glider/Dockerfile
@@ -1,5 +1,5 @@
# Build Stage
-FROM golang:1.20-alpine AS build-env
+FROM golang:1.23-alpine AS build-env
ADD . /src
RUN apk --no-cache add git \
&& cd /src && go build -v -ldflags "-s -w"
diff --git a/glider/go.mod b/glider/go.mod
index de899b82e3..47bf0e60b5 100644
--- a/glider/go.mod
+++ b/glider/go.mod
@@ -1,36 +1,29 @@
module github.com/nadoo/glider
-go 1.20
+go 1.23
require (
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da
github.com/dgryski/go-camellia v0.0.0-20191119043421-69a8a13fb23d
github.com/dgryski/go-idea v0.0.0-20170306091226-d2fb45a411fb
github.com/dgryski/go-rc2 v0.0.0-20150621095337-8a9021637152
- github.com/insomniacslk/dhcp v0.0.0-20240710054256-ddd8a41251c9
+ github.com/insomniacslk/dhcp v0.0.0-20240812123929-b105c29bd1b5
github.com/nadoo/conflag v0.3.1
github.com/nadoo/ipset v0.5.0
github.com/xtaci/kcp-go/v5 v5.6.12
- golang.org/x/crypto v0.25.0
- golang.org/x/sys v0.23.0
+ golang.org/x/crypto v0.26.0
+ golang.org/x/sys v0.24.0
)
require (
github.com/ebfe/rc2 v0.0.0-20131011165748-24b9757f5521 // indirect
- github.com/josharian/native v1.1.0 // indirect
github.com/klauspost/cpuid/v2 v2.2.8 // indirect
github.com/klauspost/reedsolomon v1.12.3 // indirect
github.com/pierrec/lz4/v4 v4.1.21 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/templexxx/cpu v0.1.1 // indirect
- github.com/templexxx/xorsimd v0.4.2 // indirect
+ github.com/templexxx/xorsimd v0.4.3 // indirect
github.com/tjfoc/gmsm v1.4.1 // indirect
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 // indirect
- golang.org/x/net v0.27.0 // indirect
-)
-
-replace (
- // Go1.20
- github.com/u-root/uio => github.com/u-root/uio v0.0.0-20240207222400-ab2ff1dfd969
- github.com/xtaci/kcp-go/v5 => github.com/xtaci/kcp-go/v5 v5.6.1
+ golang.org/x/net v0.28.0 // indirect
)
diff --git a/glider/go.sum b/glider/go.sum
index c0546fa5f1..ab4f0898dd 100644
--- a/glider/go.sum
+++ b/glider/go.sum
@@ -33,26 +33,22 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/insomniacslk/dhcp v0.0.0-20240710054256-ddd8a41251c9 h1:LZJWucZz7ztCqY6Jsu7N9g124iJ2kt/O62j3+UchZFg=
-github.com/insomniacslk/dhcp v0.0.0-20240710054256-ddd8a41251c9/go.mod h1:KclMyHxX06VrVr0DJmeFSUb1ankt7xTfoOA35pCkoic=
-github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
+github.com/insomniacslk/dhcp v0.0.0-20240812123929-b105c29bd1b5 h1:GkMacU5ftc+IEg1449N3UEy2XLDz58W4fkrRu2fibb8=
+github.com/insomniacslk/dhcp v0.0.0-20240812123929-b105c29bd1b5/go.mod h1:KclMyHxX06VrVr0DJmeFSUb1ankt7xTfoOA35pCkoic=
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
-github.com/klauspost/cpuid v1.2.4/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
-github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4=
github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
-github.com/klauspost/reedsolomon v1.9.9/go.mod h1:O7yFFHiQwDR6b2t63KPUpccPtNdp5ADgh1gg4fd12wo=
github.com/klauspost/reedsolomon v1.12.3 h1:tzUznbfc3OFwJaTebv/QdhnFf2Xvb7gZ24XaHLBPmdc=
github.com/klauspost/reedsolomon v1.12.3/go.mod h1:3K5rXwABAvzGeR01r6pWZieUALXO/Tq7bFKGIb4m4WI=
github.com/mdlayher/packet v1.1.2 h1:3Up1NG6LZrsgDVn6X4L9Ge/iyRyxFEFD9o6Pr3Q1nQY=
+github.com/mdlayher/packet v1.1.2/go.mod h1:GEu1+n9sG5VtiRE4SydOmX5GTwyyYlteZiFU+x0kew4=
github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U=
-github.com/mmcloughlin/avo v0.0.0-20200803215136-443f81d77104/go.mod h1:wqKykBG2QzQDJEzvRkcS8x6MiSJkF52hXZsXcjaB3ls=
+github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=
github.com/nadoo/conflag v0.3.1 h1:4pHkLIz8PUsfg6ajNYRRSY3bt6m2LPsu6KOzn5uIXQw=
github.com/nadoo/conflag v0.3.1/go.mod h1:dzFfDUpXdr2uS2oV+udpy5N2vfNOu/bFzjhX1WI52co=
github.com/nadoo/ipset v0.5.0 h1:5GJUAuZ7ITQQQGne5J96AmFjRtI8Avlbk6CabzYWVUc=
github.com/nadoo/ipset v0.5.0/go.mod h1:rYF5DQLRGGoQ8ZSWeK+6eX5amAuPqwFkWjhQlEITGJQ=
-github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@@ -60,73 +56,52 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
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/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/templexxx/cpu v0.0.1/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk=
-github.com/templexxx/cpu v0.0.7/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk=
-github.com/templexxx/cpu v0.1.0/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk=
github.com/templexxx/cpu v0.1.1 h1:isxHaxBXpYFWnk2DReuKkigaZyrjs2+9ypIdGP4h+HI=
github.com/templexxx/cpu v0.1.1/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk=
-github.com/templexxx/xorsimd v0.4.1/go.mod h1:W+ffZz8jJMH2SXwuKu9WhygqBMbFnp14G2fqEr8qaNo=
-github.com/templexxx/xorsimd v0.4.2 h1:ocZZ+Nvu65LGHmCLZ7OoCtg8Fx8jnHKK37SjvngUoVI=
-github.com/templexxx/xorsimd v0.4.2/go.mod h1:HgwaPoDREdi6OnULpSfxhzaiiSUY4Fi3JPn1wpt28NI=
-github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
+github.com/templexxx/xorsimd v0.4.3 h1:9AQTFHd7Bhk3dIT7Al2XeBX5DWOvsUPZCuhyAtNbHjU=
+github.com/templexxx/xorsimd v0.4.3/go.mod h1:oZQcD6RFDisW2Am58dSAGwwL6rHjbzrlu25VDqfWkQg=
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
-github.com/u-root/uio v0.0.0-20240207222400-ab2ff1dfd969 h1:4+l/zS5QI+CxIMvb02kWu7J4xMbg6nkebX64Y/X6fG8=
-github.com/u-root/uio v0.0.0-20240207222400-ab2ff1dfd969/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264=
-github.com/xtaci/kcp-go/v5 v5.6.1 h1:Pwn0aoeNSPF9dTS7IgiPXn0HEtaIlVb6y5UKWPsx8bI=
-github.com/xtaci/kcp-go/v5 v5.6.1/go.mod h1:W3kVPyNYwZ06p79dNwFWQOVFrdcBpDBsdyvK8moQrYo=
+github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 h1:pyC9PaHYZFgEKFdlp3G8RaCKgVpHZnecvArXvPXcFkM=
+github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701/go.mod h1:P3a5rG4X7tI17Nn3aOIAYr5HbIMukwXG0urG0WuL8OA=
+github.com/xtaci/kcp-go/v5 v5.6.12 h1:49PY4MbRz5CmwNzdZpREgqdAbP4QVQvCmJwjl9gpOeY=
+github.com/xtaci/kcp-go/v5 v5.6.12/go.mod h1:GSs9Z62r41kTb4CxKaRKr6ED+j7I0sUvj7Koql/RN6c=
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae h1:J0GxkO96kL4WF+AIT3M4mfUVinOCPgf2uUWYFUzN0sM=
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae/go.mod h1:gXtu8J62kEgmN++bm9BVICuT/e8yiLI2KFobd/TRFsE=
-github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-golang.org/x/arch v0.0.0-20190909030613-46d78d1859ac/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
-golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
+golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
+golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
-golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
-golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
+golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
+golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
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-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200808120158-1030fc2bf1d9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM=
-golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk=
+golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
+golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU=
+golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -134,13 +109,7 @@ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20200425043458-8463f397d07c/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20200808161706-5bf02b21f123/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
-golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
@@ -155,9 +124,7 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA=
+gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
diff --git a/glider/main.go b/glider/main.go
index c4cc65b223..21862ae669 100644
--- a/glider/main.go
+++ b/glider/main.go
@@ -17,7 +17,7 @@ import (
)
var (
- version = "0.16.4"
+ version = "0.17.0"
config = parseConfig()
)
diff --git a/lede/package/firmware/armbian-firmware/Makefile b/lede/package/firmware/armbian-firmware/Makefile
new file mode 100644
index 0000000000..186c18d06b
--- /dev/null
+++ b/lede/package/firmware/armbian-firmware/Makefile
@@ -0,0 +1,134 @@
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=armbian-firmware
+PKG_RELEASE:=1
+
+PKG_SOURCE_PROTO:=git
+PKG_SOURCE_DATE:=2024-06-26
+PKG_SOURCE_URL:=https://github.com/armbian/firmware.git
+PKG_SOURCE_VERSION:=7f2e18ecac75d50acb843395ad07a0f2b9a12a50
+PKG_MIRROR_HASH:=9967883add1587fb178e3b1a9aecec8e03b67f3554af63b294b771f67b88c934
+
+include $(INCLUDE_DIR)/package.mk
+
+RSTRIP:=:
+STRIP:=:
+
+define Package/armbian-firmware-default
+ SECTION:=firmware
+ CATEGORY:=Firmware
+ URL:=https://github.com/armbian/firmware
+ TITLE:=$(1)
+ DEPENDS:=$(2)
+endef
+
+define Build/Compile
+endef
+
+Package/brcmfmac-firmware-4356-sdio = $(call Package/armbian-firmware-default,Broadcom BCM4356 FullMac SDIO firmware)
+define Package/brcmfmac-firmware-4356-sdio/install
+ $(INSTALL_DIR) $(1)/lib/firmware/brcm
+ $(INSTALL_DATA) \
+ $(PKG_BUILD_DIR)/brcm/BCM4356A2.hcd \
+ $(1)/lib/firmware/brcm/
+ $(INSTALL_DATA) \
+ $(PKG_BUILD_DIR)/brcm/brcmfmac4356-sdio.bin \
+ $(1)/lib/firmware/brcm/
+ $(INSTALL_DATA) \
+ $(PKG_BUILD_DIR)/brcm/brcmfmac4356-sdio.clm_blob \
+ $(1)/lib/firmware/brcm/
+endef
+$(eval $(call BuildPackage,brcmfmac-firmware-4356-sdio))
+
+Package/brcmfmac-firmware-43456-sdio = $(call Package/armbian-firmware-default,Broadcom BCM43456 FullMac SDIO firmware)
+define Package/brcmfmac-firmware-43456-sdio/install
+ $(INSTALL_DIR) $(1)/lib/firmware/brcm
+ $(INSTALL_DATA) \
+ $(PKG_BUILD_DIR)/brcm/brcmfmac43456-sdio.bin \
+ $(1)/lib/firmware/brcm/
+ $(INSTALL_DATA) \
+ $(PKG_BUILD_DIR)/brcm/brcmfmac43456-sdio.clm_blob \
+ $(1)/lib/firmware/brcm/
+endef
+$(eval $(call BuildPackage,brcmfmac-firmware-43456-sdio))
+
+Package/brcmfmac-firmware-43752-pcie = $(call Package/armbian-firmware-default,Broadcom BCM43752 FullMac PCIe firmware)
+define Package/brcmfmac-firmware-43752-pcie/install
+ $(INSTALL_DIR) $(1)/lib/firmware/brcm
+ $(INSTALL_DATA) \
+ $(PKG_BUILD_DIR)/brcm/BCM4362A2.hcd \
+ $(1)/lib/firmware/brcm/
+ $(INSTALL_DATA) \
+ $(PKG_BUILD_DIR)/brcm/brcmfmac43752-pcie.bin \
+ $(1)/lib/firmware/brcm/
+ $(INSTALL_DATA) \
+ $(PKG_BUILD_DIR)/brcm/brcmfmac43752-pcie.clm_blob \
+ $(1)/lib/firmware/brcm/
+endef
+$(eval $(call BuildPackage,brcmfmac-firmware-43752-pcie))
+
+Package/brcmfmac-firmware-43752-sdio = $(call Package/armbian-firmware-default,Broadcom BCM43752 FullMac SDIO firmware)
+define Package/brcmfmac-firmware-43752-sdio/install
+ $(INSTALL_DIR) $(1)/lib/firmware/brcm
+ $(INSTALL_DATA) \
+ $(PKG_BUILD_DIR)/brcm/BCM4362A2.hcd \
+ $(1)/lib/firmware/brcm/
+ $(INSTALL_DATA) \
+ $(PKG_BUILD_DIR)/brcm/brcmfmac43752-sdio.bin \
+ $(1)/lib/firmware/brcm/
+ $(INSTALL_DATA) \
+ $(PKG_BUILD_DIR)/brcm/brcmfmac43752-sdio.clm_blob \
+ $(1)/lib/firmware/brcm/
+endef
+$(eval $(call BuildPackage,brcmfmac-firmware-43752-sdio))
+
+Package/brcmfmac-nvram-4356-sdio = $(call Package/armbian-firmware-default,Broadcom BCM4356 SDIO NVRAM firmware)
+define Package/brcmfmac-nvram-4356-sdio/install
+ $(INSTALL_DIR) $(1)/lib/firmware/brcm
+ $(INSTALL_DATA) \
+ $(PKG_BUILD_DIR)/brcm/brcmfmac4356-sdio.txt \
+ $(1)/lib/firmware/brcm/
+ $(LN) \
+ brcmfmac4356-sdio.txt \
+ $(1)/lib/firmware/brcm/brcmfmac4356-sdio.friendlyarm,nanopc-t4.txt
+endef
+$(eval $(call BuildPackage,brcmfmac-nvram-4356-sdio))
+
+Package/brcmfmac-nvram-43456-sdio = $(call Package/armbian-firmware-default,Broadcom BCM43456 SDIO NVRAM firmware)
+define Package/brcmfmac-nvram-43456-sdio/install
+ $(INSTALL_DIR) $(1)/lib/firmware/brcm
+ $(INSTALL_DATA) \
+ $(PKG_BUILD_DIR)/brcm/brcmfmac43456-sdio.txt \
+ $(1)/lib/firmware/brcm/
+ $(LN) \
+ brcmfmac43456-sdio.txt \
+ $(1)/lib/firmware/brcm/brcmfmac43456-sdio.radxa,rockpi4a.txt
+endef
+$(eval $(call BuildPackage,brcmfmac-nvram-43456-sdio))
+
+Package/brcmfmac-nvram-43752-pcie = $(call Package/armbian-firmware-default,Broadcom BCM43752 PCIe NVRAM firmware)
+define Package/brcmfmac-nvram-43752-pcie/install
+ $(INSTALL_DIR) $(1)/lib/firmware/brcm
+ $(INSTALL_DATA) \
+ $(PKG_BUILD_DIR)/brcm/brcmfmac43752-pcie.txt \
+ $(1)/lib/firmware/brcm/
+ $(LN) \
+ brcmfmac43752-pcie.txt \
+ $(1)/lib/firmware/brcm/brcmfmac43752-pcie.armsom,sige7.txt
+endef
+$(eval $(call BuildPackage,brcmfmac-nvram-43752-pcie))
+
+Package/brcmfmac-nvram-43752-sdio = $(call Package/armbian-firmware-default,Broadcom BCM43752 SDIO NVRAM firmware)
+define Package/brcmfmac-nvram-43752-sdio/install
+ $(INSTALL_DIR) $(1)/lib/firmware/brcm
+ $(INSTALL_DATA) \
+ $(PKG_BUILD_DIR)/brcm/brcmfmac43752-sdio.txt \
+ $(1)/lib/firmware/brcm/
+ $(LN) \
+ brcmfmac43752-sdio.txt \
+ $(1)/lib/firmware/brcm/brcmfmac43752-sdio.armsom,sige1-v1.txt
+ $(LN) \
+ brcmfmac43752-sdio.txt \
+ $(1)/lib/firmware/brcm/brcmfmac43752-sdio.firefly,rk3568-roc-pc.txt
+endef
+$(eval $(call BuildPackage,brcmfmac-nvram-43752-sdio))
diff --git a/lede/target/linux/rockchip/image/rk35xx.mk b/lede/target/linux/rockchip/image/rk35xx.mk
index 35016188e1..dfd568f204 100644
--- a/lede/target/linux/rockchip/image/rk35xx.mk
+++ b/lede/target/linux/rockchip/image/rk35xx.mk
@@ -35,6 +35,30 @@ $(call Device/rk3588)
endef
TARGET_DEVICES += armsom_sige7
+define Device/friendlyarm_nanopi-r6c
+$(call Device/rk3588)
+ DEVICE_VENDOR := FriendlyARM
+ DEVICE_MODEL := NanoPi R6C
+ DEVICE_PACKAGES := kmod-r8125 kmod-nvme kmod-thermal
+endef
+TARGET_DEVICES += friendlyarm_nanopi-r6c
+
+define Device/friendlyarm_nanopi-r6s
+$(call Device/rk3588)
+ DEVICE_VENDOR := FriendlyARM
+ DEVICE_MODEL := NanoPi R6S
+ DEVICE_PACKAGES := kmod-r8125 kmod-nvme kmod-thermal
+endef
+TARGET_DEVICES += friendlyarm_nanopi-r6s
+
+define Device/friendlyarm_nanopi-r6t
+$(call Device/rk3588)
+ DEVICE_VENDOR := FriendlyARM
+ DEVICE_MODEL := NanoPC T6
+ DEVICE_PACKAGES := kmod-r8125 kmod-nvme kmod-thermal
+endef
+TARGET_DEVICES += friendlyarm_nanopi-r6t
+
define Device/mangopi_m28k
$(call Device/rk3528)
DEVICE_VENDOR := MangoPi
@@ -53,3 +77,12 @@ $(call Device/rk3528)
DEVICE_PACKAGES := kmod-r8168 kmod-r8125 kmod-thermal
endef
TARGET_DEVICES += radxa_e20c
+
+define Device/radxa_rock-5c
+$(call Device/rk3588)
+ DEVICE_VENDOR := Radxa
+ DEVICE_MODEL := ROCK 5C / 5C Lite
+ DEVICE_DTS := rk3588-rock-5c
+ DEVICE_PACKAGES := kmod-r8125 kmod-nvme kmod-thermal
+endef
+TARGET_DEVICES += radxa_rock-5c
diff --git a/mihomo/.github/patch_go122/48042aa09c2f878c4faa576948b07fe625c4707a.diff b/mihomo/.github/patch_go122/48042aa09c2f878c4faa576948b07fe625c4707a.diff
deleted file mode 100644
index 2c68233358..0000000000
--- a/mihomo/.github/patch_go122/48042aa09c2f878c4faa576948b07fe625c4707a.diff
+++ /dev/null
@@ -1,54 +0,0 @@
-diff --git a/src/syscall/exec_windows.go b/src/syscall/exec_windows.go
-index 06e684c7116b4..b311a5c74684b 100644
---- a/src/syscall/exec_windows.go
-+++ b/src/syscall/exec_windows.go
-@@ -319,17 +319,6 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle
- }
- }
-
-- var maj, min, build uint32
-- rtlGetNtVersionNumbers(&maj, &min, &build)
-- isWin7 := maj < 6 || (maj == 6 && min <= 1)
-- // NT kernel handles are divisible by 4, with the bottom 3 bits left as
-- // a tag. The fully set tag correlates with the types of handles we're
-- // concerned about here. Except, the kernel will interpret some
-- // special handle values, like -1, -2, and so forth, so kernelbase.dll
-- // checks to see that those bottom three bits are checked, but that top
-- // bit is not checked.
-- isLegacyWin7ConsoleHandle := func(handle Handle) bool { return isWin7 && handle&0x10000003 == 3 }
--
- p, _ := GetCurrentProcess()
- parentProcess := p
- if sys.ParentProcess != 0 {
-@@ -338,15 +327,7 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle
- fd := make([]Handle, len(attr.Files))
- for i := range attr.Files {
- if attr.Files[i] > 0 {
-- destinationProcessHandle := parentProcess
--
-- // On Windows 7, console handles aren't real handles, and can only be duplicated
-- // into the current process, not a parent one, which amounts to the same thing.
-- if parentProcess != p && isLegacyWin7ConsoleHandle(Handle(attr.Files[i])) {
-- destinationProcessHandle = p
-- }
--
-- err := DuplicateHandle(p, Handle(attr.Files[i]), destinationProcessHandle, &fd[i], 0, true, DUPLICATE_SAME_ACCESS)
-+ err := DuplicateHandle(p, Handle(attr.Files[i]), parentProcess, &fd[i], 0, true, DUPLICATE_SAME_ACCESS)
- if err != nil {
- return 0, 0, err
- }
-@@ -377,14 +358,6 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle
-
- fd = append(fd, sys.AdditionalInheritedHandles...)
-
-- // On Windows 7, console handles aren't real handles, so don't pass them
-- // through to PROC_THREAD_ATTRIBUTE_HANDLE_LIST.
-- for i := range fd {
-- if isLegacyWin7ConsoleHandle(fd[i]) {
-- fd[i] = 0
-- }
-- }
--
- // The presence of a NULL handle in the list is enough to cause PROC_THREAD_ATTRIBUTE_HANDLE_LIST
- // to treat the entire list as empty, so remove NULL handles.
- j := 0
\ No newline at end of file
diff --git a/mihomo/.github/patch_go122/693def151adff1af707d82d28f55dba81ceb08e1.diff b/mihomo/.github/patch_go122/693def151adff1af707d82d28f55dba81ceb08e1.diff
deleted file mode 100644
index ca41ec317e..0000000000
--- a/mihomo/.github/patch_go122/693def151adff1af707d82d28f55dba81ceb08e1.diff
+++ /dev/null
@@ -1,158 +0,0 @@
-diff --git a/src/crypto/rand/rand.go b/src/crypto/rand/rand.go
-index 62738e2cb1a7d..d0dcc7cc71fc0 100644
---- a/src/crypto/rand/rand.go
-+++ b/src/crypto/rand/rand.go
-@@ -15,7 +15,7 @@ import "io"
- // available, /dev/urandom otherwise.
- // On OpenBSD and macOS, Reader uses getentropy(2).
- // On other Unix-like systems, Reader reads from /dev/urandom.
--// On Windows systems, Reader uses the RtlGenRandom API.
-+// On Windows systems, Reader uses the ProcessPrng API.
- // On JS/Wasm, Reader uses the Web Crypto API.
- // On WASIP1/Wasm, Reader uses random_get from wasi_snapshot_preview1.
- var Reader io.Reader
-diff --git a/src/crypto/rand/rand_windows.go b/src/crypto/rand/rand_windows.go
-index 6c0655c72b692..7380f1f0f1e6e 100644
---- a/src/crypto/rand/rand_windows.go
-+++ b/src/crypto/rand/rand_windows.go
-@@ -15,11 +15,8 @@ func init() { Reader = &rngReader{} }
-
- type rngReader struct{}
-
--func (r *rngReader) Read(b []byte) (n int, err error) {
-- // RtlGenRandom only returns 1<<32-1 bytes at a time. We only read at
-- // most 1<<31-1 bytes at a time so that this works the same on 32-bit
-- // and 64-bit systems.
-- if err := batched(windows.RtlGenRandom, 1<<31-1)(b); err != nil {
-+func (r *rngReader) Read(b []byte) (int, error) {
-+ if err := windows.ProcessPrng(b); err != nil {
- return 0, err
- }
- return len(b), nil
-diff --git a/src/internal/syscall/windows/syscall_windows.go b/src/internal/syscall/windows/syscall_windows.go
-index ab4ad2ec64108..5854ca60b5cef 100644
---- a/src/internal/syscall/windows/syscall_windows.go
-+++ b/src/internal/syscall/windows/syscall_windows.go
-@@ -373,7 +373,7 @@ func ErrorLoadingGetTempPath2() error {
- //sys DestroyEnvironmentBlock(block *uint16) (err error) = userenv.DestroyEnvironmentBlock
- //sys CreateEvent(eventAttrs *SecurityAttributes, manualReset uint32, initialState uint32, name *uint16) (handle syscall.Handle, err error) = kernel32.CreateEventW
-
--//sys RtlGenRandom(buf []byte) (err error) = advapi32.SystemFunction036
-+//sys ProcessPrng(buf []byte) (err error) = bcryptprimitives.ProcessPrng
-
- type FILE_ID_BOTH_DIR_INFO struct {
- NextEntryOffset uint32
-diff --git a/src/internal/syscall/windows/zsyscall_windows.go b/src/internal/syscall/windows/zsyscall_windows.go
-index e3f6d8d2a2208..5a587ad4f146c 100644
---- a/src/internal/syscall/windows/zsyscall_windows.go
-+++ b/src/internal/syscall/windows/zsyscall_windows.go
-@@ -37,13 +37,14 @@ func errnoErr(e syscall.Errno) error {
- }
-
- var (
-- modadvapi32 = syscall.NewLazyDLL(sysdll.Add("advapi32.dll"))
-- modiphlpapi = syscall.NewLazyDLL(sysdll.Add("iphlpapi.dll"))
-- modkernel32 = syscall.NewLazyDLL(sysdll.Add("kernel32.dll"))
-- modnetapi32 = syscall.NewLazyDLL(sysdll.Add("netapi32.dll"))
-- modpsapi = syscall.NewLazyDLL(sysdll.Add("psapi.dll"))
-- moduserenv = syscall.NewLazyDLL(sysdll.Add("userenv.dll"))
-- modws2_32 = syscall.NewLazyDLL(sysdll.Add("ws2_32.dll"))
-+ modadvapi32 = syscall.NewLazyDLL(sysdll.Add("advapi32.dll"))
-+ modbcryptprimitives = syscall.NewLazyDLL(sysdll.Add("bcryptprimitives.dll"))
-+ modiphlpapi = syscall.NewLazyDLL(sysdll.Add("iphlpapi.dll"))
-+ modkernel32 = syscall.NewLazyDLL(sysdll.Add("kernel32.dll"))
-+ modnetapi32 = syscall.NewLazyDLL(sysdll.Add("netapi32.dll"))
-+ modpsapi = syscall.NewLazyDLL(sysdll.Add("psapi.dll"))
-+ moduserenv = syscall.NewLazyDLL(sysdll.Add("userenv.dll"))
-+ modws2_32 = syscall.NewLazyDLL(sysdll.Add("ws2_32.dll"))
-
- procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges")
- procDuplicateTokenEx = modadvapi32.NewProc("DuplicateTokenEx")
-@@ -55,7 +56,7 @@ var (
- procQueryServiceStatus = modadvapi32.NewProc("QueryServiceStatus")
- procRevertToSelf = modadvapi32.NewProc("RevertToSelf")
- procSetTokenInformation = modadvapi32.NewProc("SetTokenInformation")
-- procSystemFunction036 = modadvapi32.NewProc("SystemFunction036")
-+ procProcessPrng = modbcryptprimitives.NewProc("ProcessPrng")
- procGetAdaptersAddresses = modiphlpapi.NewProc("GetAdaptersAddresses")
- procCreateEventW = modkernel32.NewProc("CreateEventW")
- procGetACP = modkernel32.NewProc("GetACP")
-@@ -179,12 +180,12 @@ func SetTokenInformation(tokenHandle syscall.Token, tokenInformationClass uint32
- return
- }
-
--func RtlGenRandom(buf []byte) (err error) {
-+func ProcessPrng(buf []byte) (err error) {
- var _p0 *byte
- if len(buf) > 0 {
- _p0 = &buf[0]
- }
-- r1, _, e1 := syscall.Syscall(procSystemFunction036.Addr(), 2, uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), 0)
-+ r1, _, e1 := syscall.Syscall(procProcessPrng.Addr(), 2, uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), 0)
- if r1 == 0 {
- err = errnoErr(e1)
- }
-diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go
-index 8ca8d7790909e..3772a864b2ff4 100644
---- a/src/runtime/os_windows.go
-+++ b/src/runtime/os_windows.go
-@@ -127,15 +127,8 @@ var (
- _WriteFile,
- _ stdFunction
-
-- // Use RtlGenRandom to generate cryptographically random data.
-- // This approach has been recommended by Microsoft (see issue
-- // 15589 for details).
-- // The RtlGenRandom is not listed in advapi32.dll, instead
-- // RtlGenRandom function can be found by searching for SystemFunction036.
-- // Also some versions of Mingw cannot link to SystemFunction036
-- // when building executable as Cgo. So load SystemFunction036
-- // manually during runtime startup.
-- _RtlGenRandom stdFunction
-+ // Use ProcessPrng to generate cryptographically random data.
-+ _ProcessPrng stdFunction
-
- // Load ntdll.dll manually during startup, otherwise Mingw
- // links wrong printf function to cgo executable (see issue
-@@ -151,11 +144,11 @@ var (
- )
-
- var (
-- advapi32dll = [...]uint16{'a', 'd', 'v', 'a', 'p', 'i', '3', '2', '.', 'd', 'l', 'l', 0}
-- ntdlldll = [...]uint16{'n', 't', 'd', 'l', 'l', '.', 'd', 'l', 'l', 0}
-- powrprofdll = [...]uint16{'p', 'o', 'w', 'r', 'p', 'r', 'o', 'f', '.', 'd', 'l', 'l', 0}
-- winmmdll = [...]uint16{'w', 'i', 'n', 'm', 'm', '.', 'd', 'l', 'l', 0}
-- ws2_32dll = [...]uint16{'w', 's', '2', '_', '3', '2', '.', 'd', 'l', 'l', 0}
-+ bcryptprimitivesdll = [...]uint16{'b', 'c', 'r', 'y', 'p', 't', 'p', 'r', 'i', 'm', 'i', 't', 'i', 'v', 'e', 's', '.', 'd', 'l', 'l', 0}
-+ ntdlldll = [...]uint16{'n', 't', 'd', 'l', 'l', '.', 'd', 'l', 'l', 0}
-+ powrprofdll = [...]uint16{'p', 'o', 'w', 'r', 'p', 'r', 'o', 'f', '.', 'd', 'l', 'l', 0}
-+ winmmdll = [...]uint16{'w', 'i', 'n', 'm', 'm', '.', 'd', 'l', 'l', 0}
-+ ws2_32dll = [...]uint16{'w', 's', '2', '_', '3', '2', '.', 'd', 'l', 'l', 0}
- )
-
- // Function to be called by windows CreateThread
-@@ -251,11 +244,11 @@ func windowsLoadSystemLib(name []uint16) uintptr {
- }
-
- func loadOptionalSyscalls() {
-- a32 := windowsLoadSystemLib(advapi32dll[:])
-- if a32 == 0 {
-- throw("advapi32.dll not found")
-+ bcryptPrimitives := windowsLoadSystemLib(bcryptprimitivesdll[:])
-+ if bcryptPrimitives == 0 {
-+ throw("bcryptprimitives.dll not found")
- }
-- _RtlGenRandom = windowsFindfunc(a32, []byte("SystemFunction036\000"))
-+ _ProcessPrng = windowsFindfunc(bcryptPrimitives, []byte("ProcessPrng\000"))
-
- n32 := windowsLoadSystemLib(ntdlldll[:])
- if n32 == 0 {
-@@ -531,7 +524,7 @@ func osinit() {
- //go:nosplit
- func readRandom(r []byte) int {
- n := 0
-- if stdcall2(_RtlGenRandom, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 {
-+ if stdcall2(_ProcessPrng, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 {
- n = len(r)
- }
- return n
\ No newline at end of file
diff --git a/mihomo/.github/patch_go122/7c1157f9544922e96945196b47b95664b1e39108.diff b/mihomo/.github/patch_go122/7c1157f9544922e96945196b47b95664b1e39108.diff
deleted file mode 100644
index c1fc5f6d2d..0000000000
--- a/mihomo/.github/patch_go122/7c1157f9544922e96945196b47b95664b1e39108.diff
+++ /dev/null
@@ -1,162 +0,0 @@
-diff --git a/src/net/hook_windows.go b/src/net/hook_windows.go
-index ab8656cbbf343..28c49cc6de7e7 100644
---- a/src/net/hook_windows.go
-+++ b/src/net/hook_windows.go
-@@ -14,7 +14,6 @@ var (
- testHookDialChannel = func() { time.Sleep(time.Millisecond) } // see golang.org/issue/5349
-
- // Placeholders for socket system calls.
-- socketFunc func(int, int, int) (syscall.Handle, error) = syscall.Socket
- wsaSocketFunc func(int32, int32, int32, *syscall.WSAProtocolInfo, uint32, uint32) (syscall.Handle, error) = windows.WSASocket
- connectFunc func(syscall.Handle, syscall.Sockaddr) error = syscall.Connect
- listenFunc func(syscall.Handle, int) error = syscall.Listen
-diff --git a/src/net/internal/socktest/main_test.go b/src/net/internal/socktest/main_test.go
-index 0197feb3f199a..967ce6795aedb 100644
---- a/src/net/internal/socktest/main_test.go
-+++ b/src/net/internal/socktest/main_test.go
-@@ -2,7 +2,7 @@
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
-
--//go:build !js && !plan9 && !wasip1
-+//go:build !js && !plan9 && !wasip1 && !windows
-
- package socktest_test
-
-diff --git a/src/net/internal/socktest/main_windows_test.go b/src/net/internal/socktest/main_windows_test.go
-deleted file mode 100644
-index df1cb97784b51..0000000000000
---- a/src/net/internal/socktest/main_windows_test.go
-+++ /dev/null
-@@ -1,22 +0,0 @@
--// Copyright 2015 The Go Authors. All rights reserved.
--// Use of this source code is governed by a BSD-style
--// license that can be found in the LICENSE file.
--
--package socktest_test
--
--import "syscall"
--
--var (
-- socketFunc func(int, int, int) (syscall.Handle, error)
-- closeFunc func(syscall.Handle) error
--)
--
--func installTestHooks() {
-- socketFunc = sw.Socket
-- closeFunc = sw.Closesocket
--}
--
--func uninstallTestHooks() {
-- socketFunc = syscall.Socket
-- closeFunc = syscall.Closesocket
--}
-diff --git a/src/net/internal/socktest/sys_windows.go b/src/net/internal/socktest/sys_windows.go
-index 8c1c862f33c9b..1c42e5c7f34b7 100644
---- a/src/net/internal/socktest/sys_windows.go
-+++ b/src/net/internal/socktest/sys_windows.go
-@@ -9,38 +9,6 @@ import (
- "syscall"
- )
-
--// Socket wraps syscall.Socket.
--func (sw *Switch) Socket(family, sotype, proto int) (s syscall.Handle, err error) {
-- sw.once.Do(sw.init)
--
-- so := &Status{Cookie: cookie(family, sotype, proto)}
-- sw.fmu.RLock()
-- f, _ := sw.fltab[FilterSocket]
-- sw.fmu.RUnlock()
--
-- af, err := f.apply(so)
-- if err != nil {
-- return syscall.InvalidHandle, err
-- }
-- s, so.Err = syscall.Socket(family, sotype, proto)
-- if err = af.apply(so); err != nil {
-- if so.Err == nil {
-- syscall.Closesocket(s)
-- }
-- return syscall.InvalidHandle, err
-- }
--
-- sw.smu.Lock()
-- defer sw.smu.Unlock()
-- if so.Err != nil {
-- sw.stats.getLocked(so.Cookie).OpenFailed++
-- return syscall.InvalidHandle, so.Err
-- }
-- nso := sw.addLocked(s, family, sotype, proto)
-- sw.stats.getLocked(nso.Cookie).Opened++
-- return s, nil
--}
--
- // WSASocket wraps [syscall.WSASocket].
- func (sw *Switch) WSASocket(family, sotype, proto int32, protinfo *syscall.WSAProtocolInfo, group uint32, flags uint32) (s syscall.Handle, err error) {
- sw.once.Do(sw.init)
-diff --git a/src/net/main_windows_test.go b/src/net/main_windows_test.go
-index 07f21b72eb1fc..bc024c0bbd82d 100644
---- a/src/net/main_windows_test.go
-+++ b/src/net/main_windows_test.go
-@@ -8,7 +8,6 @@ import "internal/poll"
-
- var (
- // Placeholders for saving original socket system calls.
-- origSocket = socketFunc
- origWSASocket = wsaSocketFunc
- origClosesocket = poll.CloseFunc
- origConnect = connectFunc
-@@ -18,7 +17,6 @@ var (
- )
-
- func installTestHooks() {
-- socketFunc = sw.Socket
- wsaSocketFunc = sw.WSASocket
- poll.CloseFunc = sw.Closesocket
- connectFunc = sw.Connect
-@@ -28,7 +26,6 @@ func installTestHooks() {
- }
-
- func uninstallTestHooks() {
-- socketFunc = origSocket
- wsaSocketFunc = origWSASocket
- poll.CloseFunc = origClosesocket
- connectFunc = origConnect
-diff --git a/src/net/sock_windows.go b/src/net/sock_windows.go
-index fa11c7af2e727..5540135a2c43e 100644
---- a/src/net/sock_windows.go
-+++ b/src/net/sock_windows.go
-@@ -19,21 +19,6 @@ func maxListenerBacklog() int {
- func sysSocket(family, sotype, proto int) (syscall.Handle, error) {
- s, err := wsaSocketFunc(int32(family), int32(sotype), int32(proto),
- nil, 0, windows.WSA_FLAG_OVERLAPPED|windows.WSA_FLAG_NO_HANDLE_INHERIT)
-- if err == nil {
-- return s, nil
-- }
-- // WSA_FLAG_NO_HANDLE_INHERIT flag is not supported on some
-- // old versions of Windows, see
-- // https://msdn.microsoft.com/en-us/library/windows/desktop/ms742212(v=vs.85).aspx
-- // for details. Just use syscall.Socket, if windows.WSASocket failed.
--
-- // See ../syscall/exec_unix.go for description of ForkLock.
-- syscall.ForkLock.RLock()
-- s, err = socketFunc(family, sotype, proto)
-- if err == nil {
-- syscall.CloseOnExec(s)
-- }
-- syscall.ForkLock.RUnlock()
- if err != nil {
- return syscall.InvalidHandle, os.NewSyscallError("socket", err)
- }
-diff --git a/src/syscall/exec_windows.go b/src/syscall/exec_windows.go
-index 0a93bc0a80d4e..06e684c7116b4 100644
---- a/src/syscall/exec_windows.go
-+++ b/src/syscall/exec_windows.go
-@@ -14,6 +14,7 @@ import (
- "unsafe"
- )
-
-+// ForkLock is not used on Windows.
- var ForkLock sync.RWMutex
-
- // EscapeArg rewrites command line argument s as prescribed
\ No newline at end of file
diff --git a/mihomo/.github/workflows/build.yml b/mihomo/.github/workflows/build.yml
index cd5202e12e..fb3eb621a4 100644
--- a/mihomo/.github/workflows/build.yml
+++ b/mihomo/.github/workflows/build.yml
@@ -67,6 +67,12 @@ jobs:
- { goos: android, goarch: arm, ndk: armv7a-linux-androideabi34, output: armv7 }
- { goos: android, goarch: arm64, ndk: aarch64-linux-android34, output: arm64-v8 }
+ # Go 1.22 with special patch can work on Windows 7
+ # https://github.com/MetaCubeX/go/commits/release-branch.go1.22/
+ - { goos: windows, goarch: '386', output: '386-go122', goversion: '1.22' }
+ - { goos: windows, goarch: amd64, goamd64: v1, output: amd64-compatible-go122, goversion: '1.22' }
+ - { goos: windows, goarch: amd64, goamd64: v3, output: amd64-go122, goversion: '1.22' }
+
# Go 1.21 can revert commit `9e4385` to work on Windows 7
# https://github.com/golang/go/issues/64622#issuecomment-1847475161
# (OR we can just use golang1.21.4 which unneeded any patch)
@@ -79,6 +85,11 @@ jobs:
- { goos: windows, goarch: amd64, goamd64: v1, output: amd64-compatible-go120, goversion: '1.20' }
- { goos: windows, goarch: amd64, goamd64: v3, output: amd64-go120, goversion: '1.20' }
+ # Go 1.22 is the last release that will run on macOS 10.15 Catalina. Go 1.23 will require macOS 11 Big Sur or later.
+ - { goos: darwin, goarch: arm64, output: arm64-go122, goversion: '1.22' }
+ - { goos: darwin, goarch: amd64, goamd64: v1, output: amd64-compatible-go122, goversion: '1.22' }
+ - { goos: darwin, goarch: amd64, goamd64: v3, output: amd64-go122, goversion: '1.22' }
+
# Go 1.20 is the last release that will run on macOS 10.13 High Sierra or 10.14 Mojave. Go 1.21 will require macOS 10.15 Catalina or later.
- { goos: darwin, goarch: arm64, output: arm64-go120, goversion: '1.20' }
- { goos: darwin, goarch: amd64, goamd64: v1, output: amd64-compatible-go120, goversion: '1.20' }
@@ -96,7 +107,7 @@ jobs:
if: ${{ matrix.jobs.goversion == '' && matrix.jobs.goarch != 'loong64' }}
uses: actions/setup-go@v5
with:
- go-version: '1.22'
+ go-version: '1.23'
- name: Set up Go
if: ${{ matrix.jobs.goversion != '' && matrix.jobs.goarch != 'loong64' }}
@@ -119,19 +130,40 @@ jobs:
echo "/usr/local/go/bin" >> $GITHUB_PATH
# modify from https://github.com/restic/restic/issues/4636#issuecomment-1896455557
- # this patch file only works on golang1.22.x
- # that means after golang1.23 release it must be changed
+ # this patch file only works on golang1.23.x
+ # that means after golang1.24 release it must be changed
+ # see: https://github.com/MetaCubeX/go/commits/release-branch.go1.23/
# revert:
# 693def151adff1af707d82d28f55dba81ceb08e1: "crypto/rand,runtime: switch RtlGenRandom for ProcessPrng"
# 7c1157f9544922e96945196b47b95664b1e39108: "net: remove sysSocket fallback for Windows 7"
# 48042aa09c2f878c4faa576948b07fe625c4707a: "syscall: remove Windows 7 console handle workaround"
- - name: Revert Golang1.22 commit for Windows7/8
+ # a17d959debdb04cd550016a3501dd09d50cd62e7: "runtime: always use LoadLibraryEx to load system libraries"
+ - name: Revert Golang1.23 commit for Windows7/8
if: ${{ matrix.jobs.goos == 'windows' && matrix.jobs.goversion == '' }}
run: |
cd $(go env GOROOT)
- patch --verbose -R -p 1 < $GITHUB_WORKSPACE/.github/patch_go122/693def151adff1af707d82d28f55dba81ceb08e1.diff
- patch --verbose -R -p 1 < $GITHUB_WORKSPACE/.github/patch_go122/7c1157f9544922e96945196b47b95664b1e39108.diff
- patch --verbose -R -p 1 < $GITHUB_WORKSPACE/.github/patch_go122/48042aa09c2f878c4faa576948b07fe625c4707a.diff
+ curl https://github.com/MetaCubeX/go/commit/9ac42137ef6730e8b7daca016ece831297a1d75b.diff | patch --verbose -p 1
+ curl https://github.com/MetaCubeX/go/commit/21290de8a4c91408de7c2b5b68757b1e90af49dd.diff | patch --verbose -p 1
+ curl https://github.com/MetaCubeX/go/commit/6a31d3fa8e47ddabc10bd97bff10d9a85f4cfb76.diff | patch --verbose -p 1
+ curl https://github.com/MetaCubeX/go/commit/69e2eed6dd0f6d815ebf15797761c13f31213dd6.diff | patch --verbose -p 1
+
+ # modify from https://github.com/restic/restic/issues/4636#issuecomment-1896455557
+ # this patch file only works on golang1.22.x
+ # that means after golang1.23 release it must be changed
+ # see: https://github.com/MetaCubeX/go/commits/release-branch.go1.22/
+ # revert:
+ # 693def151adff1af707d82d28f55dba81ceb08e1: "crypto/rand,runtime: switch RtlGenRandom for ProcessPrng"
+ # 7c1157f9544922e96945196b47b95664b1e39108: "net: remove sysSocket fallback for Windows 7"
+ # 48042aa09c2f878c4faa576948b07fe625c4707a: "syscall: remove Windows 7 console handle workaround"
+ # a17d959debdb04cd550016a3501dd09d50cd62e7: "runtime: always use LoadLibraryEx to load system libraries"
+ - name: Revert Golang1.22 commit for Windows7/8
+ if: ${{ matrix.jobs.goos == 'windows' && matrix.jobs.goversion == '1.22' }}
+ run: |
+ cd $(go env GOROOT)
+ curl https://github.com/MetaCubeX/go/commit/9779155f18b6556a034f7bb79fb7fb2aad1e26a9.diff | patch --verbose -p 1
+ curl https://github.com/MetaCubeX/go/commit/ef0606261340e608017860b423ffae5c1ce78239.diff | patch --verbose -p 1
+ curl https://github.com/MetaCubeX/go/commit/7f83badcb925a7e743188041cb6e561fc9b5b642.diff | patch --verbose -p 1
+ curl https://github.com/MetaCubeX/go/commit/83ff9782e024cb328b690cbf0da4e7848a327f4f.diff | patch --verbose -p 1
# modify from https://github.com/restic/restic/issues/4636#issuecomment-1896455557
- name: Revert Golang1.21 commit for Windows7/8
diff --git a/mihomo/common/net/tcp_keepalive.go b/mihomo/common/net/tcp_keepalive.go
new file mode 100644
index 0000000000..047a1c05eb
--- /dev/null
+++ b/mihomo/common/net/tcp_keepalive.go
@@ -0,0 +1,23 @@
+package net
+
+import (
+ "net"
+ "runtime"
+ "time"
+)
+
+var (
+ KeepAliveIdle = 0 * time.Second
+ KeepAliveInterval = 0 * time.Second
+ DisableKeepAlive = false
+)
+
+func TCPKeepAlive(c net.Conn) {
+ if tcp, ok := c.(*net.TCPConn); ok {
+ if runtime.GOOS == "android" || DisableKeepAlive {
+ _ = tcp.SetKeepAlive(false)
+ } else {
+ tcpKeepAlive(tcp)
+ }
+ }
+}
diff --git a/mihomo/common/net/tcp_keepalive_go122.go b/mihomo/common/net/tcp_keepalive_go122.go
new file mode 100644
index 0000000000..1287316868
--- /dev/null
+++ b/mihomo/common/net/tcp_keepalive_go122.go
@@ -0,0 +1,10 @@
+//go:build !go1.23
+
+package net
+
+import "net"
+
+func tcpKeepAlive(tcp *net.TCPConn) {
+ _ = tcp.SetKeepAlive(true)
+ _ = tcp.SetKeepAlivePeriod(KeepAliveInterval)
+}
diff --git a/mihomo/common/net/tcp_keepalive_go123.go b/mihomo/common/net/tcp_keepalive_go123.go
new file mode 100644
index 0000000000..2dd4754bbe
--- /dev/null
+++ b/mihomo/common/net/tcp_keepalive_go123.go
@@ -0,0 +1,19 @@
+//go:build go1.23
+
+package net
+
+import "net"
+
+func tcpKeepAlive(tcp *net.TCPConn) {
+ config := net.KeepAliveConfig{
+ Enable: true,
+ Idle: KeepAliveIdle,
+ Interval: KeepAliveInterval,
+ }
+ if !SupportTCPKeepAliveCount() {
+ // it's recommended to set both Idle and Interval to non-negative values in conjunction with a -1
+ // for Count on those old Windows if you intend to customize the TCP keep-alive settings.
+ config.Count = -1
+ }
+ _ = tcp.SetKeepAliveConfig(config)
+}
diff --git a/mihomo/common/net/tcp_keepalive_go123_unix.go b/mihomo/common/net/tcp_keepalive_go123_unix.go
new file mode 100644
index 0000000000..0ead7ca472
--- /dev/null
+++ b/mihomo/common/net/tcp_keepalive_go123_unix.go
@@ -0,0 +1,15 @@
+//go:build go1.23 && unix
+
+package net
+
+func SupportTCPKeepAliveIdle() bool {
+ return true
+}
+
+func SupportTCPKeepAliveInterval() bool {
+ return true
+}
+
+func SupportTCPKeepAliveCount() bool {
+ return true
+}
diff --git a/mihomo/common/net/tcp_keepalive_go123_windows.go b/mihomo/common/net/tcp_keepalive_go123_windows.go
new file mode 100644
index 0000000000..8f1e61f959
--- /dev/null
+++ b/mihomo/common/net/tcp_keepalive_go123_windows.go
@@ -0,0 +1,63 @@
+//go:build go1.23 && windows
+
+// copy and modify from golang1.23's internal/syscall/windows/version_windows.go
+
+package net
+
+import (
+ "errors"
+ "sync"
+ "syscall"
+
+ "github.com/metacubex/mihomo/constant/features"
+
+ "golang.org/x/sys/windows"
+)
+
+var (
+ supportTCPKeepAliveIdle bool
+ supportTCPKeepAliveInterval bool
+ supportTCPKeepAliveCount bool
+)
+
+var initTCPKeepAlive = sync.OnceFunc(func() {
+ s, err := windows.WSASocket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP, nil, 0, windows.WSA_FLAG_NO_HANDLE_INHERIT)
+ if err != nil {
+ // Fallback to checking the Windows version.
+ major, build := features.WindowsMajorVersion, features.WindowsBuildNumber
+ supportTCPKeepAliveIdle = major >= 10 && build >= 16299
+ supportTCPKeepAliveInterval = major >= 10 && build >= 16299
+ supportTCPKeepAliveCount = major >= 10 && build >= 15063
+ return
+ }
+ defer windows.Closesocket(s)
+ var optSupported = func(opt int) bool {
+ err := windows.SetsockoptInt(s, syscall.IPPROTO_TCP, opt, 1)
+ return !errors.Is(err, syscall.WSAENOPROTOOPT)
+ }
+ supportTCPKeepAliveIdle = optSupported(windows.TCP_KEEPIDLE)
+ supportTCPKeepAliveInterval = optSupported(windows.TCP_KEEPINTVL)
+ supportTCPKeepAliveCount = optSupported(windows.TCP_KEEPCNT)
+})
+
+// SupportTCPKeepAliveIdle indicates whether TCP_KEEPIDLE is supported.
+// The minimal requirement is Windows 10.0.16299.
+func SupportTCPKeepAliveIdle() bool {
+ initTCPKeepAlive()
+ return supportTCPKeepAliveIdle
+}
+
+// SupportTCPKeepAliveInterval indicates whether TCP_KEEPINTVL is supported.
+// The minimal requirement is Windows 10.0.16299.
+func SupportTCPKeepAliveInterval() bool {
+ initTCPKeepAlive()
+ return supportTCPKeepAliveInterval
+}
+
+// SupportTCPKeepAliveCount indicates whether TCP_KEEPCNT is supported.
+// supports TCP_KEEPCNT.
+// The minimal requirement is Windows 10.0.15063.
+func SupportTCPKeepAliveCount() bool {
+ initTCPKeepAlive()
+ return supportTCPKeepAliveCount
+}
diff --git a/mihomo/common/net/tcpip.go b/mihomo/common/net/tcpip.go
index 0499e54c17..a84e7e4c4f 100644
--- a/mihomo/common/net/tcpip.go
+++ b/mihomo/common/net/tcpip.go
@@ -4,11 +4,8 @@ import (
"fmt"
"net"
"strings"
- "time"
)
-var KeepAliveInterval = 15 * time.Second
-
func SplitNetworkType(s string) (string, string, error) {
var (
shecme string
@@ -47,10 +44,3 @@ func SplitHostPort(s string) (host, port string, hasPort bool, err error) {
host, port, err = net.SplitHostPort(temp)
return
}
-
-func TCPKeepAlive(c net.Conn) {
- if tcp, ok := c.(*net.TCPConn); ok {
- _ = tcp.SetKeepAlive(true)
- _ = tcp.SetKeepAlivePeriod(KeepAliveInterval)
- }
-}
diff --git a/mihomo/component/dialer/dialer.go b/mihomo/component/dialer/dialer.go
index ba95c31b81..2a39508f3f 100644
--- a/mihomo/component/dialer/dialer.go
+++ b/mihomo/component/dialer/dialer.go
@@ -13,7 +13,6 @@ import (
"time"
"github.com/metacubex/mihomo/component/resolver"
- "github.com/metacubex/mihomo/constant/features"
"github.com/metacubex/mihomo/log"
)
@@ -79,29 +78,29 @@ func DialContext(ctx context.Context, network, address string, options ...Option
}
func ListenPacket(ctx context.Context, network, address string, rAddrPort netip.AddrPort, options ...Option) (net.PacketConn, error) {
- if features.CMFA && DefaultSocketHook != nil {
- return listenPacketHooked(ctx, network, address)
- }
-
cfg := applyOptions(options...)
lc := &net.ListenConfig{}
- if cfg.interfaceName != "" {
- bind := bindIfaceToListenConfig
- if cfg.fallbackBind {
- bind = fallbackBindIfaceToListenConfig
- }
- addr, err := bind(cfg.interfaceName, lc, network, address, rAddrPort)
- if err != nil {
- return nil, err
- }
- address = addr
- }
if cfg.addrReuse {
addrReuseToListenConfig(lc)
}
- if cfg.routingMark != 0 {
- bindMarkToListenConfig(cfg.routingMark, lc, network, address)
+ if DefaultSocketHook != nil { // ignore interfaceName, routingMark when DefaultSocketHook not null (in CFMA)
+ socketHookToListenConfig(lc)
+ } else {
+ if cfg.interfaceName != "" {
+ bind := bindIfaceToListenConfig
+ if cfg.fallbackBind {
+ bind = fallbackBindIfaceToListenConfig
+ }
+ addr, err := bind(cfg.interfaceName, lc, network, address, rAddrPort)
+ if err != nil {
+ return nil, err
+ }
+ address = addr
+ }
+ if cfg.routingMark != 0 {
+ bindMarkToListenConfig(cfg.routingMark, lc, network, address)
+ }
}
return lc.ListenPacket(ctx, network, address)
@@ -149,25 +148,26 @@ func dialContext(ctx context.Context, network string, destination netip.Addr, po
setMultiPathTCP(dialer)
}
- if features.CMFA && DefaultSocketHook != nil { // ignore interfaceName, routingMark and tfo in CMFA
- return dialContextHooked(ctx, dialer, network, address)
+ if DefaultSocketHook != nil { // ignore interfaceName, routingMark and tfo when DefaultSocketHook not null (in CFMA)
+ socketHookToToDialer(dialer)
+ } else {
+ if opt.interfaceName != "" {
+ bind := bindIfaceToDialer
+ if opt.fallbackBind {
+ bind = fallbackBindIfaceToDialer
+ }
+ if err := bind(opt.interfaceName, dialer, network, destination); err != nil {
+ return nil, err
+ }
+ }
+ if opt.routingMark != 0 {
+ bindMarkToDialer(opt.routingMark, dialer, network, destination)
+ }
+ if opt.tfo && !DisableTFO {
+ return dialTFO(ctx, *dialer, network, address)
+ }
}
- if opt.interfaceName != "" {
- bind := bindIfaceToDialer
- if opt.fallbackBind {
- bind = fallbackBindIfaceToDialer
- }
- if err := bind(opt.interfaceName, dialer, network, destination); err != nil {
- return nil, err
- }
- }
- if opt.routingMark != 0 {
- bindMarkToDialer(opt.routingMark, dialer, network, destination)
- }
- if opt.tfo && !DisableTFO {
- return dialTFO(ctx, *dialer, network, address)
- }
return dialer.DialContext(ctx, network, address)
}
diff --git a/mihomo/component/dialer/patch_android.go b/mihomo/component/dialer/patch_android.go
deleted file mode 100644
index 079b9772ab..0000000000
--- a/mihomo/component/dialer/patch_android.go
+++ /dev/null
@@ -1,38 +0,0 @@
-//go:build android && cmfa
-
-package dialer
-
-import (
- "context"
- "net"
- "syscall"
-)
-
-type SocketControl func(network, address string, conn syscall.RawConn) error
-
-var DefaultSocketHook SocketControl
-
-func dialContextHooked(ctx context.Context, dialer *net.Dialer, network string, address string) (net.Conn, error) {
- addControlToDialer(dialer, func(ctx context.Context, network, address string, c syscall.RawConn) error {
- return DefaultSocketHook(network, address, c)
- })
-
- conn, err := dialer.DialContext(ctx, network, address)
- if err != nil {
- return nil, err
- }
-
- if t, ok := conn.(*net.TCPConn); ok {
- t.SetKeepAlive(false)
- }
-
- return conn, nil
-}
-
-func listenPacketHooked(ctx context.Context, network, address string) (net.PacketConn, error) {
- lc := &net.ListenConfig{
- Control: DefaultSocketHook,
- }
-
- return lc.ListenPacket(ctx, network, address)
-}
diff --git a/mihomo/component/dialer/patch_common.go b/mihomo/component/dialer/patch_common.go
deleted file mode 100644
index 2c96fe60b7..0000000000
--- a/mihomo/component/dialer/patch_common.go
+++ /dev/null
@@ -1,21 +0,0 @@
-//go:build !(android && cmfa)
-
-package dialer
-
-import (
- "context"
- "net"
- "syscall"
-)
-
-type SocketControl func(network, address string, conn syscall.RawConn) error
-
-var DefaultSocketHook SocketControl
-
-func dialContextHooked(ctx context.Context, dialer *net.Dialer, network string, address string) (net.Conn, error) {
- return nil, nil
-}
-
-func listenPacketHooked(ctx context.Context, network, address string) (net.PacketConn, error) {
- return nil, nil
-}
diff --git a/mihomo/component/dialer/socket_hook.go b/mihomo/component/dialer/socket_hook.go
new file mode 100644
index 0000000000..7a2ea43215
--- /dev/null
+++ b/mihomo/component/dialer/socket_hook.go
@@ -0,0 +1,27 @@
+package dialer
+
+import (
+ "context"
+ "net"
+ "syscall"
+)
+
+// SocketControl
+// never change type traits because it's used in CFMA
+type SocketControl func(network, address string, conn syscall.RawConn) error
+
+// DefaultSocketHook
+// never change type traits because it's used in CFMA
+var DefaultSocketHook SocketControl
+
+func socketHookToToDialer(dialer *net.Dialer) {
+ addControlToDialer(dialer, func(ctx context.Context, network, address string, c syscall.RawConn) error {
+ return DefaultSocketHook(network, address, c)
+ })
+}
+
+func socketHookToListenConfig(lc *net.ListenConfig) {
+ addControlToListenConfig(lc, func(ctx context.Context, network, address string, c syscall.RawConn) error {
+ return DefaultSocketHook(network, address, c)
+ })
+}
diff --git a/mihomo/component/fakeip/pool.go b/mihomo/component/fakeip/pool.go
index 2b06fc0bdf..8096a868af 100644
--- a/mihomo/component/fakeip/pool.go
+++ b/mihomo/component/fakeip/pool.go
@@ -8,7 +8,7 @@ import (
"github.com/metacubex/mihomo/common/nnip"
"github.com/metacubex/mihomo/component/profile/cachefile"
- "github.com/metacubex/mihomo/component/trie"
+ C "github.com/metacubex/mihomo/constant"
)
const (
@@ -35,7 +35,7 @@ type Pool struct {
offset netip.Addr
cycle bool
mux sync.Mutex
- host *trie.DomainTrie[struct{}]
+ host []C.Rule
ipnet netip.Prefix
store store
}
@@ -66,10 +66,12 @@ func (p *Pool) LookBack(ip netip.Addr) (string, bool) {
// ShouldSkipped return if domain should be skipped
func (p *Pool) ShouldSkipped(domain string) bool {
- if p.host == nil {
- return false
+ for _, rule := range p.host {
+ if match, _ := rule.Match(&C.Metadata{Host: domain}); match {
+ return true
+ }
}
- return p.host.Search(domain) != nil
+ return false
}
// Exist returns if given ip exists in fake-ip pool
@@ -154,7 +156,7 @@ func (p *Pool) restoreState() {
type Options struct {
IPNet netip.Prefix
- Host *trie.DomainTrie[struct{}]
+ Host []C.Rule
// Size sets the maximum number of entries in memory
// and does not work if Persistence is true
diff --git a/mihomo/component/fakeip/pool_test.go b/mihomo/component/fakeip/pool_test.go
index cc50fcf7b5..9c05a32778 100644
--- a/mihomo/component/fakeip/pool_test.go
+++ b/mihomo/component/fakeip/pool_test.go
@@ -9,6 +9,8 @@ import (
"github.com/metacubex/mihomo/component/profile/cachefile"
"github.com/metacubex/mihomo/component/trie"
+ C "github.com/metacubex/mihomo/constant"
+ RP "github.com/metacubex/mihomo/rules/provider"
"github.com/sagernet/bbolt"
"github.com/stretchr/testify/assert"
@@ -154,7 +156,7 @@ func TestPool_Skip(t *testing.T) {
pools, tempfile, err := createPools(Options{
IPNet: ipnet,
Size: 10,
- Host: tree,
+ Host: []C.Rule{RP.NewDomainSet(tree.NewDomainSet(), "")},
})
assert.Nil(t, err)
defer os.Remove(tempfile)
diff --git a/mihomo/component/sniffer/dispatcher.go b/mihomo/component/sniffer/dispatcher.go
index 4438638dad..c96f5a4b03 100644
--- a/mihomo/component/sniffer/dispatcher.go
+++ b/mihomo/component/sniffer/dispatcher.go
@@ -9,7 +9,6 @@ import (
"github.com/metacubex/mihomo/common/lru"
N "github.com/metacubex/mihomo/common/net"
- "github.com/metacubex/mihomo/component/trie"
C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/constant/sniffer"
"github.com/metacubex/mihomo/log"
@@ -26,17 +25,26 @@ var Dispatcher *SnifferDispatcher
type SnifferDispatcher struct {
enable bool
sniffers map[sniffer.Sniffer]SnifferConfig
- forceDomain *trie.DomainSet
- skipSNI *trie.DomainSet
+ forceDomain []C.Rule
+ skipDomain []C.Rule
skipList *lru.LruCache[string, uint8]
forceDnsMapping bool
parsePureIp bool
}
func (sd *SnifferDispatcher) shouldOverride(metadata *C.Metadata) bool {
- return (metadata.Host == "" && sd.parsePureIp) ||
- sd.forceDomain.Has(metadata.Host) ||
- (metadata.DNSMode == C.DNSMapping && sd.forceDnsMapping)
+ if metadata.Host == "" && sd.parsePureIp {
+ return true
+ }
+ if metadata.DNSMode == C.DNSMapping && sd.forceDnsMapping {
+ return true
+ }
+ for _, rule := range sd.forceDomain {
+ if ok, _ := rule.Match(&C.Metadata{Host: metadata.Host}); ok {
+ return true
+ }
+ }
+ return false
}
func (sd *SnifferDispatcher) UDPSniff(packet C.PacketAdapter) bool {
@@ -94,9 +102,11 @@ func (sd *SnifferDispatcher) TCPSniff(conn *N.BufferedConn, metadata *C.Metadata
log.Debugln("[Sniffer] All sniffing sniff failed with from [%s:%d] to [%s:%d]", metadata.SrcIP, metadata.SrcPort, metadata.String(), metadata.DstPort)
return false
} else {
- if sd.skipSNI.Has(host) {
- log.Debugln("[Sniffer] Skip sni[%s]", host)
- return false
+ for _, rule := range sd.skipDomain {
+ if ok, _ := rule.Match(&C.Metadata{Host: host}); ok {
+ log.Debugln("[Sniffer] Skip sni[%s]", host)
+ return false
+ }
}
sd.skipList.Delete(dst)
@@ -187,12 +197,12 @@ func NewCloseSnifferDispatcher() (*SnifferDispatcher, error) {
}
func NewSnifferDispatcher(snifferConfig map[sniffer.Type]SnifferConfig,
- forceDomain *trie.DomainSet, skipSNI *trie.DomainSet,
+ forceDomain []C.Rule, skipDomain []C.Rule,
forceDnsMapping bool, parsePureIp bool) (*SnifferDispatcher, error) {
dispatcher := SnifferDispatcher{
enable: true,
forceDomain: forceDomain,
- skipSNI: skipSNI,
+ skipDomain: skipDomain,
skipList: lru.New(lru.WithSize[string, uint8](128), lru.WithAge[string, uint8](600)),
forceDnsMapping: forceDnsMapping,
parsePureIp: parsePureIp,
diff --git a/mihomo/component/trie/domain.go b/mihomo/component/trie/domain.go
index db30402ede..6d3e37f70a 100644
--- a/mihomo/component/trie/domain.go
+++ b/mihomo/component/trie/domain.go
@@ -134,6 +134,13 @@ func (t *DomainTrie[T]) Foreach(fn func(domain string, data T) bool) {
}
}
+func (t *DomainTrie[T]) IsEmpty() bool {
+ if t == nil {
+ return true
+ }
+ return t.root.isEmpty()
+}
+
func recursion[T any](items []string, node *Node[T], fn func(domain string, data T) bool) bool {
for key, data := range node.getChildren() {
newItems := append([]string{key}, items...)
diff --git a/mihomo/config/config.go b/mihomo/config/config.go
index 5f2b68453d..74ffdd038c 100644
--- a/mihomo/config/config.go
+++ b/mihomo/config/config.go
@@ -38,6 +38,7 @@ import (
LC "github.com/metacubex/mihomo/listener/config"
"github.com/metacubex/mihomo/log"
R "github.com/metacubex/mihomo/rules"
+ RC "github.com/metacubex/mihomo/rules/common"
RP "github.com/metacubex/mihomo/rules/provider"
T "github.com/metacubex/mihomo/tunnel"
@@ -163,8 +164,8 @@ type IPTables struct {
type Sniffer struct {
Enable bool
Sniffers map[snifferTypes.Type]SNIFF.SnifferConfig
- ForceDomain *trie.DomainSet
- SkipDomain *trie.DomainSet
+ ForceDomain []C.Rule
+ SkipDomain []C.Rule
ForceDnsMapping bool
ParsePureIp bool
}
@@ -338,7 +339,9 @@ type RawConfig struct {
FindProcessMode P.FindProcessMode `yaml:"find-process-mode" json:"find-process-mode"`
GlobalClientFingerprint string `yaml:"global-client-fingerprint"`
GlobalUA string `yaml:"global-ua"`
+ KeepAliveIdle int `yaml:"keep-alive-idle"`
KeepAliveInterval int `yaml:"keep-alive-interval"`
+ DisableKeepAlive bool `yaml:"disable-keep-alive"`
Sniffer RawSniffer `yaml:"sniffer" json:"sniffer"`
ProxyProvider map[string]map[string]any `yaml:"proxy-providers"`
@@ -624,7 +627,7 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) {
}
}
- config.Sniffer, err = parseSniffer(rawCfg.Sniffer)
+ config.Sniffer, err = parseSniffer(rawCfg.Sniffer, rules, ruleProviders)
if err != nil {
return nil, err
}
@@ -649,9 +652,14 @@ func parseGeneral(cfg *RawConfig) (*General, error) {
C.ASNUrl = cfg.GeoXUrl.ASN
C.GeodataMode = cfg.GeodataMode
C.UA = cfg.GlobalUA
+
+ if cfg.KeepAliveIdle != 0 {
+ N.KeepAliveIdle = time.Duration(cfg.KeepAliveIdle) * time.Second
+ }
if cfg.KeepAliveInterval != 0 {
N.KeepAliveInterval = time.Duration(cfg.KeepAliveInterval) * time.Second
}
+ N.DisableKeepAlive = cfg.DisableKeepAlive
updater.ExternalUIPath = cfg.ExternalUI
// checkout externalUI exist
@@ -1400,27 +1408,21 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rul
return nil, err
}
- var host *trie.DomainTrie[struct{}]
- // fake ip skip host filter
- if len(cfg.FakeIPFilter) != 0 {
- host = trie.New[struct{}]()
- for _, domain := range cfg.FakeIPFilter {
- _ = host.Insert(domain, struct{}{})
- }
- host.Optimize()
- }
-
+ var fakeIPTrie *trie.DomainTrie[struct{}]
if len(dnsCfg.Fallback) != 0 {
- if host == nil {
- host = trie.New[struct{}]()
- }
+ fakeIPTrie = trie.New[struct{}]()
for _, fb := range dnsCfg.Fallback {
if net.ParseIP(fb.Addr) != nil {
continue
}
- _ = host.Insert(fb.Addr, struct{}{})
+ _ = fakeIPTrie.Insert(fb.Addr, struct{}{})
}
- host.Optimize()
+ }
+
+ // fake ip skip host filter
+ host, err := parseDomain(cfg.FakeIPFilter, fakeIPTrie, rules, ruleProviders)
+ if err != nil {
+ return nil, err
}
pool, err := fakeip.New(fakeip.Options{
@@ -1547,7 +1549,7 @@ func parseTuicServer(rawTuic RawTuicServer, general *General) error {
return nil
}
-func parseSniffer(snifferRaw RawSniffer) (*Sniffer, error) {
+func parseSniffer(snifferRaw RawSniffer, rules []C.Rule, ruleProviders map[string]providerTypes.RuleProvider) (*Sniffer, error) {
sniffer := &Sniffer{
Enable: snifferRaw.Enable,
ForceDnsMapping: snifferRaw.ForceDnsMapping,
@@ -1610,23 +1612,83 @@ func parseSniffer(snifferRaw RawSniffer) (*Sniffer, error) {
sniffer.Sniffers = loadSniffer
- forceDomainTrie := trie.New[struct{}]()
- for _, domain := range snifferRaw.ForceDomain {
- err := forceDomainTrie.Insert(domain, struct{}{})
- if err != nil {
- return nil, fmt.Errorf("error domian[%s] in force-domain, error:%v", domain, err)
- }
+ forceDomain, err := parseDomain(snifferRaw.ForceDomain, nil, rules, ruleProviders)
+ if err != nil {
+ return nil, fmt.Errorf("error in force-domain, error:%w", err)
}
- sniffer.ForceDomain = forceDomainTrie.NewDomainSet()
+ sniffer.ForceDomain = forceDomain
- skipDomainTrie := trie.New[struct{}]()
- for _, domain := range snifferRaw.SkipDomain {
- err := skipDomainTrie.Insert(domain, struct{}{})
- if err != nil {
- return nil, fmt.Errorf("error domian[%s] in force-domain, error:%v", domain, err)
- }
+ skipDomain, err := parseDomain(snifferRaw.SkipDomain, nil, rules, ruleProviders)
+ if err != nil {
+ return nil, fmt.Errorf("error in skip-domain, error:%w", err)
}
- sniffer.SkipDomain = skipDomainTrie.NewDomainSet()
+ sniffer.SkipDomain = skipDomain
return sniffer, nil
}
+
+func parseDomain(domains []string, domainTrie *trie.DomainTrie[struct{}], rules []C.Rule, ruleProviders map[string]providerTypes.RuleProvider) (domainRules []C.Rule, err error) {
+ var rule C.Rule
+ for _, domain := range domains {
+ domainLower := strings.ToLower(domain)
+ if strings.Contains(domainLower, "geosite:") {
+ subkeys := strings.Split(domain, ":")
+ subkeys = subkeys[1:]
+ subkeys = strings.Split(subkeys[0], ",")
+ for _, country := range subkeys {
+ found := false
+ for _, rule = range rules {
+ if rule.RuleType() == C.GEOSITE {
+ if strings.EqualFold(country, rule.Payload()) {
+ found = true
+ domainRules = append(domainRules, rule)
+ }
+ }
+ }
+ if !found {
+ rule, err = RC.NewGEOSITE(country, "")
+ if err != nil {
+ return nil, err
+ }
+ domainRules = append(domainRules, rule)
+ }
+ }
+ } else if strings.Contains(domainLower, "rule-set:") {
+ subkeys := strings.Split(domain, ":")
+ subkeys = subkeys[1:]
+ subkeys = strings.Split(subkeys[0], ",")
+ for _, domainSetName := range subkeys {
+ if rp, ok := ruleProviders[domainSetName]; !ok {
+ return nil, fmt.Errorf("not found rule-set: %s", domainSetName)
+ } else {
+ switch rp.Behavior() {
+ case providerTypes.IPCIDR:
+ return nil, fmt.Errorf("rule provider type error, except domain,actual %s", rp.Behavior())
+ case providerTypes.Classical:
+ log.Warnln("%s provider is %s, only matching it contain domain rule", rp.Name(), rp.Behavior())
+ default:
+ }
+ }
+ rule, err = RP.NewRuleSet(domainSetName, "", true)
+ if err != nil {
+ return nil, err
+ }
+
+ domainRules = append(domainRules, rule)
+ }
+ } else {
+ if domainTrie == nil {
+ domainTrie = trie.New[struct{}]()
+ }
+ err = domainTrie.Insert(domain, struct{}{})
+ if err != nil {
+ return nil, err
+ }
+ }
+ }
+ if !domainTrie.IsEmpty() {
+ rule = RP.NewDomainSet(domainTrie.NewDomainSet(), "")
+ domainRules = append(domainRules, rule)
+ }
+ return
+}
diff --git a/mihomo/constant/rule.go b/mihomo/constant/rule.go
index a91ee6cb07..f9f987e672 100644
--- a/mihomo/constant/rule.go
+++ b/mihomo/constant/rule.go
@@ -27,6 +27,7 @@ const (
ProcessNameRegex
ProcessPathRegex
RuleSet
+ DomainSet
Network
Uid
SubRules
@@ -90,6 +91,8 @@ func (rt RuleType) String() string {
return "Match"
case RuleSet:
return "RuleSet"
+ case DomainSet:
+ return "DomainSet"
case Network:
return "Network"
case DSCP:
diff --git a/mihomo/docs/config.yaml b/mihomo/docs/config.yaml
index d7c686d01f..8db06b6d7d 100644
--- a/mihomo/docs/config.yaml
+++ b/mihomo/docs/config.yaml
@@ -82,7 +82,9 @@ external-doh-server: /dns-query
global-client-fingerprint: chrome
# TCP keep alive interval
-keep-alive-interval: 15
+# disable-keep-alive: false #目前在android端强制为true
+# keep-alive-idle: 15
+# keep-alive-interval: 15
# routing-mark:6666 # 配置 fwmark 仅用于 Linux
experimental:
@@ -241,6 +243,16 @@ dns:
fake-ip-range: 198.18.0.1/16 # fake-ip 池设置
+ # 配置不使用 fake-ip 的域名
+ fake-ip-filter:
+ - '*.lan'
+ - localhost.ptlogin2.qq.com
+ # fakeip-filter 为 rule-providers 中的名为 fakeip-filter 规则订阅,
+ # 且 behavior 必须为 domain/classical,当为 classical 时仅会生效域名类规则
+ - rule-set:fakeip-filter
+ # fakeip-filter 为 geosite 中名为 fakeip-filter 的分类(需要自行保证该分类存在)
+ - geosite:fakeip-filter
+
# use-hosts: true # 查询 hosts
# 配置后面的nameserver、fallback和nameserver-policy向dns服务器的连接过程是否遵守遵守rules规则
@@ -250,11 +262,6 @@ dns:
# 此外,这三者配置中的dns服务器如果出现域名会采用default-nameserver配置项解析,也请确保正确配置default-nameserver
respect-rules: false
- # 配置不使用 fake-ip 的域名
- # fake-ip-filter:
- # - '*.lan'
- # - localhost.ptlogin2.qq.com
-
# DNS 主要域名配置
# 支持 UDP,TCP,DoT,DoH,DoQ
# 这部分为主要 DNS 配置,影响所有直连,确保使用对大陆解析精准的 DNS
diff --git a/mihomo/listener/http/server.go b/mihomo/listener/http/server.go
index 77e10f0841..f61dd03609 100644
--- a/mihomo/listener/http/server.go
+++ b/mihomo/listener/http/server.go
@@ -4,9 +4,9 @@ import (
"net"
"github.com/metacubex/mihomo/adapter/inbound"
+ N "github.com/metacubex/mihomo/common/net"
"github.com/metacubex/mihomo/component/auth"
C "github.com/metacubex/mihomo/constant"
- "github.com/metacubex/mihomo/constant/features"
authStore "github.com/metacubex/mihomo/listener/auth"
)
@@ -74,11 +74,7 @@ func NewWithAuthenticator(addr string, tunnel C.Tunnel, authenticator auth.Authe
}
continue
}
- if features.CMFA {
- if t, ok := conn.(*net.TCPConn); ok {
- t.SetKeepAlive(false)
- }
- }
+ N.TCPKeepAlive(conn)
if isDefault { // only apply on default listener
if !inbound.IsRemoteAddrDisAllowed(conn.RemoteAddr()) {
_ = conn.Close()
diff --git a/mihomo/rules/provider/domain_set.go b/mihomo/rules/provider/domain_set.go
new file mode 100644
index 0000000000..372b438ea8
--- /dev/null
+++ b/mihomo/rules/provider/domain_set.go
@@ -0,0 +1,43 @@
+package provider
+
+import (
+ "github.com/metacubex/mihomo/component/trie"
+ C "github.com/metacubex/mihomo/constant"
+)
+
+type DomainSet struct {
+ *domainStrategy
+ adapter string
+}
+
+func (d *DomainSet) ProviderNames() []string {
+ return nil
+}
+
+func (d *DomainSet) RuleType() C.RuleType {
+ return C.DomainSet
+}
+
+func (d *DomainSet) Match(metadata *C.Metadata) (bool, string) {
+ if d.domainSet == nil {
+ return false, ""
+ }
+ return d.domainSet.Has(metadata.RuleHost()), d.adapter
+}
+
+func (d *DomainSet) Adapter() string {
+ return d.adapter
+}
+
+func (d *DomainSet) Payload() string {
+ return ""
+}
+
+func NewDomainSet(domainSet *trie.DomainSet, adapter string) *DomainSet {
+ return &DomainSet{
+ domainStrategy: &domainStrategy{domainSet: domainSet},
+ adapter: adapter,
+ }
+}
+
+var _ C.Rule = (*DomainSet)(nil)
diff --git a/openwrt-packages/alist/files/alist.init b/openwrt-packages/alist/files/alist.init
index a1703bb2b0..157aa47924 100755
--- a/openwrt-packages/alist/files/alist.init
+++ b/openwrt-packages/alist/files/alist.init
@@ -188,7 +188,7 @@ start_service() {
json_dump > $data_dir/config.json
- procd_open_instance
+ procd_open_instance alist
procd_set_param command $PROG
procd_append_param command server --data $data_dir
procd_set_param stdout 0
@@ -196,7 +196,13 @@ start_service() {
procd_set_param respawn
procd_set_param limits core="unlimited"
procd_set_param limits nofile="200000 200000"
- procd_close_instance
+ procd_close_instance alist
+}
+
+reload_service() {
+ stop
+ sleep 3
+ start
}
service_triggers() {
diff --git a/openwrt-packages/luci-app-alist/htdocs/luci-static/resources/view/alist/basic.js b/openwrt-packages/luci-app-alist/htdocs/luci-static/resources/view/alist/basic.js
index 23c2416b1b..bb40c41c71 100644
--- a/openwrt-packages/luci-app-alist/htdocs/luci-static/resources/view/alist/basic.js
+++ b/openwrt-packages/luci-app-alist/htdocs/luci-static/resources/view/alist/basic.js
@@ -17,18 +17,18 @@ function getServiceStatus() {
return L.resolveDefault(callServiceList('alist'), {}).then(function (res) {
var isRunning = false;
try {
- isRunning = res['alist']['instances']['instance1']['running'];
+ isRunning = res['alist']['instances']['alist']['running'];
} catch (e) { }
return isRunning;
});
}
-function renderStatus(isRunning, webport) {
+function renderStatus(isRunning, protocol, webport) {
var spanTemp = '%s %s';
var renderHTML;
if (isRunning) {
- var button = String.format('',
- _('Open Web Interface'), window.location.hostname, webport);
+ var button = String.format('',
+ _('Open Web Interface'), protocol, window.location.hostname, webport);
renderHTML = spanTemp.format('green', 'Alist', _('RUNNING')) + button;
} else {
renderHTML = spanTemp.format('red', 'Alist', _('NOT RUNNING'));
@@ -64,6 +64,13 @@ return view.extend({
render: function (data) {
var m, s, o;
var webport = uci.get(data[0], '@alist[0]', 'port') || '5244';
+ var ssl = uci.get(data[0], '@alist[0]', 'ssl') || '0';
+ var protocol;
+ if (ssl === '0') {
+ protocol = 'http:';
+ } else if (ssl === '1') {
+ protocol = 'https:';
+ }
m = new form.Map('alist', _('Alist'),
_('A file list program that supports multiple storage.') +
@@ -79,7 +86,7 @@ return view.extend({
poll.add(function () {
return L.resolveDefault(getServiceStatus()).then(function (res) {
var view = document.getElementById('service_status');
- view.innerHTML = renderStatus(res, webport);
+ view.innerHTML = renderStatus(res, protocol, webport);
});
});
@@ -88,7 +95,7 @@ return view.extend({
]);
}
- s = m.section(form.TypedSection);
+ s = m.section(form.NamedSection, '@alist[0]', 'alist');
o = s.option(form.Flag, 'enabled', _('Enabled'));
o.default = o.disabled;
@@ -108,10 +115,12 @@ return view.extend({
o = s.option(form.Value, 'ssl_cert', _('SSL cert'),
_('SSL certificate file path'));
+ o.rmempty = false;
o.depends('ssl', '1');
o = s.option(form.Value, 'ssl_key', _('SSL key'),
_('SSL key file path'));
+ o.rmempty = false;
o.depends('ssl', '1');
o = s.option(form.Flag, 'mysql', _('Enable Database'));
diff --git a/openwrt-packages/luci-app-alist/po/zh_Hans/alist.po b/openwrt-packages/luci-app-alist/po/zh_Hans/alist.po
index ec63260c61..c01cf1bab0 100644
--- a/openwrt-packages/luci-app-alist/po/zh_Hans/alist.po
+++ b/openwrt-packages/luci-app-alist/po/zh_Hans/alist.po
@@ -73,10 +73,10 @@ msgstr "随机生成一个新密码。"
msgid "Username:"
msgstr "用户名:"
-msgstr "New Password:"
+msgid "New Password:"
msgstr "新密码:"
-msgstr "New password has been copied to clipboard."
+msgid "New password has been copied to clipboard."
msgstr "新密码已复制到剪贴板。"
msgid "Login Validity Period (hours)"
diff --git a/openwrt-packages/luci-app-wechatpush/root/usr/share/serverchan/serverchan b/openwrt-packages/luci-app-wechatpush/root/usr/share/serverchan/serverchan
index 157f211e0f..5a19c62f03 100755
--- a/openwrt-packages/luci-app-wechatpush/root/usr/share/serverchan/serverchan
+++ b/openwrt-packages/luci-app-wechatpush/root/usr/share/serverchan/serverchan
@@ -678,9 +678,9 @@ function rand_geturl(){
echo `curl -k -s -w "%{http_code}" -m 5 ${url_str} -A "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36 Edg/112.0.1722.58" -o /dev/null`
}
local check=`getcheck`
- while [ -z "$check" ] || [[ $check -ne 200 && $check -ne 301 && $check -ne 302 ]]; do
+ while [ -z "$check" ] || [[ $check -ne 200 && $check -ne 202 && $check -ne 301 && $check -ne 302 ]]; do
local check=`getcheck`
- if [ ! -z "$check" ] && [[ $check -eq 200 || $check -eq 301 || $check -eq 302 ]]; then
+ if [ ! -z "$check" ] && [[ $check -eq 200 || $check -eq 202 || $check -eq 301 || $check -eq 302 ]]; then
[ ! -z "$network_enable" ] && [ "$network_enable" -eq "404" ] && echo "`date "+%Y-%m-%d %H:%M:%S"` 【网络状态】网络恢复正常.." >> ${logfile}
local network_enable="200"
else
diff --git a/pingtunnel/Dockerfile b/pingtunnel/Dockerfile
index b14358cb94..cce8c7aa1c 100644
--- a/pingtunnel/Dockerfile
+++ b/pingtunnel/Dockerfile
@@ -5,7 +5,8 @@ WORKDIR /app
COPY go.* ./
RUN go mod download
COPY . ./
-RUN go build -v -o pingtunnel
+RUN go mod tidy
+RUN cd cmd && go build -v -o pingtunnel && mv pingtunnel ../
FROM debian
COPY --from=build-env /app/pingtunnel .
diff --git a/shadowsocks-rust/crates/shadowsocks-service/src/server/server.rs b/shadowsocks-rust/crates/shadowsocks-service/src/server/server.rs
index 2bd0d03371..ae9102bdf8 100644
--- a/shadowsocks-rust/crates/shadowsocks-service/src/server/server.rs
+++ b/shadowsocks-rust/crates/shadowsocks-service/src/server/server.rs
@@ -2,12 +2,15 @@
use std::{
collections::HashMap,
+ future::Future,
io::{self, ErrorKind},
+ pin::Pin,
sync::Arc,
+ task::{Context, Poll},
time::Duration,
};
-use futures::{stream::FuturesUnordered, FutureExt, StreamExt};
+use futures::{future, ready};
use log::{error, trace};
use shadowsocks::{
config::{ManagerAddr, ServerConfig},
@@ -16,7 +19,7 @@ use shadowsocks::{
plugin::{Plugin, PluginMode},
ManagerClient,
};
-use tokio::time;
+use tokio::{task::JoinHandle, time};
use crate::{acl::AccessControl, config::SecurityConfig, net::FlowStat};
@@ -159,6 +162,27 @@ impl ServerBuilder {
}
}
+struct ServerHandle(JoinHandle>);
+
+impl Drop for ServerHandle {
+ #[inline]
+ fn drop(&mut self) {
+ self.0.abort();
+ }
+}
+
+impl Future for ServerHandle {
+ type Output = io::Result<()>;
+
+ #[inline]
+ fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll {
+ match ready!(Pin::new(&mut self.0).poll(cx)) {
+ Ok(res) => res.into(),
+ Err(err) => Err(io::Error::new(ErrorKind::Other, err)).into(),
+ }
+ }
+}
+
/// Shadowsocks Server instance
pub struct Server {
context: Arc,
@@ -187,36 +211,33 @@ impl Server {
/// Start serving
pub async fn run(self) -> io::Result<()> {
- let vfut = FuturesUnordered::new();
+ let mut vfut = Vec::new();
if let Some(plugin) = self.plugin {
- vfut.push(
- async move {
- match plugin.join().await {
- Ok(status) => {
- error!("plugin exited with status: {}", status);
- Ok(())
- }
- Err(err) => {
- error!("plugin exited with error: {}", err);
- Err(err)
- }
+ vfut.push(ServerHandle(tokio::spawn(async move {
+ match plugin.join().await {
+ Ok(status) => {
+ error!("plugin exited with status: {}", status);
+ Ok(())
+ }
+ Err(err) => {
+ error!("plugin exited with error: {}", err);
+ Err(err)
}
}
- .boxed(),
- );
+ })));
}
if let Some(tcp_server) = self.tcp_server {
- vfut.push(tcp_server.run().boxed());
+ vfut.push(ServerHandle(tokio::spawn(tcp_server.run())));
}
if let Some(udp_server) = self.udp_server {
- vfut.push(udp_server.run().boxed())
+ vfut.push(ServerHandle(tokio::spawn(udp_server.run())));
}
if let Some(manager_addr) = self.manager_addr {
- let manager_fut = async move {
+ vfut.push(ServerHandle(tokio::spawn(async move {
loop {
match ManagerClient::connect(
self.context.context_ref(),
@@ -251,13 +272,10 @@ impl Server {
// Report every 10 seconds
time::sleep(Duration::from_secs(10)).await;
}
- }
- .boxed();
- vfut.push(manager_fut);
+ })));
}
- let (res, _) = vfut.into_future().await;
- if let Some(Err(err)) = res {
+ if let (Err(err), ..) = future::select_all(vfut).await {
error!("servers exited with error: {}", err);
}
diff --git a/small/v2ray-geodata/Makefile b/small/v2ray-geodata/Makefile
index 05c67a1f17..5f9549403b 100644
--- a/small/v2ray-geodata/Makefile
+++ b/small/v2ray-geodata/Makefile
@@ -21,13 +21,13 @@ define Download/geoip
HASH:=0080f836cd88744fc37a8ec13f36e189814d8f95d8072c557bdab04f700f51e8
endef
-GEOSITE_VER:=20240810010807
+GEOSITE_VER:=20240814034058
GEOSITE_FILE:=dlc.dat.$(GEOSITE_VER)
define Download/geosite
URL:=https://github.com/v2fly/domain-list-community/releases/download/$(GEOSITE_VER)/
URL_FILE:=dlc.dat
FILE:=$(GEOSITE_FILE)
- HASH:=f9e7fab895dc57e2c40d0e12a774dc45a3dc83f43eab5185757bb3f9e5b08010
+ HASH:=cdb9e804886774f683f80c4f2819df8eebf67fd40f48406ac368286ae360ac0a
endef
GEOSITE_IRAN_VER:=202408120030
diff --git a/v2rayng/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AngApplication.kt b/v2rayng/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AngApplication.kt
index 6416d201df..516fe1ed4f 100644
--- a/v2rayng/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AngApplication.kt
+++ b/v2rayng/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AngApplication.kt
@@ -17,9 +17,6 @@ class AngApplication : MultiDexApplication(), Configuration.Provider {
application = this
}
- //var firstRun = false
- // private set
-
override fun onCreate() {
super.onCreate()
diff --git a/v2rayng/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt b/v2rayng/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt
index 023d2b7260..23fa921299 100644
--- a/v2rayng/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt
+++ b/v2rayng/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt
@@ -18,6 +18,7 @@ import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.appcompat.app.AlertDialog
+import androidx.appcompat.widget.SearchView
import androidx.core.content.ContextCompat
import androidx.core.view.GravityCompat
import androidx.core.view.isVisible
@@ -27,7 +28,6 @@ import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.navigation.NavigationView
import com.google.android.material.tabs.TabLayout
import com.tbruyelle.rxpermissions3.RxPermissions
-import com.tencent.mmkv.MMKV
import com.v2ray.ang.AppConfig
import com.v2ray.ang.R
import com.v2ray.ang.databinding.ActivityMainBinding
@@ -54,8 +54,6 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
}
private val adapter by lazy { MainRecyclerAdapter(this) }
- private val mainStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_MAIN, MMKV.MULTI_PROCESS_MODE) }
- private val settingsStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SETTING, MMKV.MULTI_PROCESS_MODE) }
private val requestVpnPermission = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (it.resultCode == RESULT_OK) {
startV2Ray()
@@ -90,7 +88,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
binding.fab.setOnClickListener {
if (mainViewModel.isRunning.value == true) {
Utils.stopVService(this)
- } else if ((settingsStorage?.decodeString(AppConfig.PREF_MODE) ?: "VPN") == "VPN") {
+ } else if ((MmkvManager.settingsStorage?.decodeString(AppConfig.PREF_MODE) ?: "VPN") == "VPN") {
val intent = VpnService.prepare(this)
if (intent == null) {
startV2Ray()
@@ -120,14 +118,14 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
val toggle = ActionBarDrawerToggle(
- this, binding.drawerLayout, binding.toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close)
+ this, binding.drawerLayout, binding.toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close
+ )
binding.drawerLayout.addDrawerListener(toggle)
toggle.syncState()
binding.navView.setNavigationItemSelectedListener(this)
initGroupTab()
setupViewModel()
- mainViewModel.copyAssets(assets)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
RxPermissions(this)
@@ -174,6 +172,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
}
}
mainViewModel.startListenBroadcast()
+ mainViewModel.copyAssets(assets)
}
private fun initGroupTab() {
@@ -201,7 +200,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
fun startV2Ray() {
if (isNetworkConnected) {
- if (mainStorage?.decodeString(MmkvManager.KEY_SELECTED_SERVER).isNullOrEmpty()) {
+ if (MmkvManager.mainStorage?.decodeString(MmkvManager.KEY_SELECTED_SERVER).isNullOrEmpty()) {
return
}
V2RayServiceManager.startV2Ray(this)
@@ -215,10 +214,10 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
Utils.stopVService(this)
}
Observable.timer(500, TimeUnit.MILLISECONDS)
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe {
- startV2Ray()
- }
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe {
+ startV2Ray()
+ }
}
public override fun onResume() {
@@ -232,7 +231,25 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu_main, menu)
- return true
+
+ val searchItem = menu.findItem(R.id.search_view)
+ if (searchItem != null) {
+ val searchView = searchItem.actionView as SearchView
+ searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
+ override fun onQueryTextSubmit(query: String?): Boolean {
+ mainViewModel.filterConfig(query.orEmpty())
+ return false
+ }
+
+ override fun onQueryTextChange(newText: String?): Boolean = false
+ })
+
+ searchView.setOnCloseListener {
+ mainViewModel.filterConfig("")
+ false
+ }
+ }
+ return super.onCreateOptionsMenu(menu)
}
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
@@ -240,46 +257,57 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
importQRcode(true)
true
}
+
R.id.import_clipboard -> {
importClipboard()
true
}
+
R.id.import_manually_vmess -> {
importManually(EConfigType.VMESS.value)
true
}
+
R.id.import_manually_vless -> {
importManually(EConfigType.VLESS.value)
true
}
+
R.id.import_manually_ss -> {
importManually(EConfigType.SHADOWSOCKS.value)
true
}
+
R.id.import_manually_socks -> {
importManually(EConfigType.SOCKS.value)
true
}
+
R.id.import_manually_trojan -> {
importManually(EConfigType.TROJAN.value)
true
}
+
R.id.import_manually_wireguard -> {
importManually(EConfigType.WIREGUARD.value)
true
}
+
R.id.import_config_custom_clipboard -> {
importConfigCustomClipboard()
true
}
+
R.id.import_config_custom_local -> {
importConfigCustomLocal()
true
}
+
R.id.import_config_custom_url -> {
importConfigCustomUrlClipboard()
true
}
+
R.id.import_config_custom_url_scan -> {
importQRcode(false)
true
@@ -291,11 +319,18 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
}
R.id.export_all -> {
- if (AngConfigManager.shareNonCustomConfigsToClipboard(this, mainViewModel.serverList) == 0) {
- toast(R.string.toast_success)
- } else {
- toast(R.string.toast_failure)
+ binding.pbWaiting.show()
+ lifecycleScope.launch(Dispatchers.IO) {
+ val ret = mainViewModel.exportAllServer()
+ launch(Dispatchers.Main) {
+ if (ret == 0)
+ toast(R.string.toast_success)
+ else
+ toast(R.string.toast_failure)
+ binding.pbWaiting.hide()
+ }
}
+
true
}
@@ -315,49 +350,79 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
}
R.id.del_all_config -> {
- AlertDialog.Builder(this).setMessage(R.string.del_config_comfirm)
- .setPositiveButton(android.R.string.ok) { _, _ ->
- MmkvManager.removeAllServer()
- mainViewModel.reloadServerList()
- }
- .setNegativeButton(android.R.string.no) {_, _ ->
- //do noting
- }
- .show()
- true
- }
- R.id.del_duplicate_config-> {
AlertDialog.Builder(this).setMessage(R.string.del_config_comfirm)
.setPositiveButton(android.R.string.ok) { _, _ ->
- mainViewModel.removeDuplicateServer()
+ binding.pbWaiting.show()
+ lifecycleScope.launch(Dispatchers.IO) {
+ mainViewModel.removeAllServer()
+ launch(Dispatchers.Main) {
+ mainViewModel.reloadServerList()
+ binding.pbWaiting.hide()
+ }
+ }
}
- .setNegativeButton(android.R.string.no) {_, _ ->
+ .setNegativeButton(android.R.string.no) { _, _ ->
//do noting
}
.show()
true
}
+
+ R.id.del_duplicate_config -> {
+ AlertDialog.Builder(this).setMessage(R.string.del_config_comfirm)
+ .setPositiveButton(android.R.string.ok) { _, _ ->
+ binding.pbWaiting.show()
+ lifecycleScope.launch(Dispatchers.IO) {
+ val ret = mainViewModel.removeDuplicateServer()
+ launch(Dispatchers.Main) {
+ mainViewModel.reloadServerList()
+ toast(getString(R.string.title_del_duplicate_config_count, ret))
+ binding.pbWaiting.hide()
+ }
+ }
+ }
+ .setNegativeButton(android.R.string.no) { _, _ ->
+ //do noting
+ }
+ .show()
+ true
+ }
+
R.id.del_invalid_config -> {
AlertDialog.Builder(this).setMessage(R.string.del_config_comfirm)
.setPositiveButton(android.R.string.ok) { _, _ ->
- MmkvManager.removeInvalidServer()
- mainViewModel.reloadServerList()
+ binding.pbWaiting.show()
+ lifecycleScope.launch(Dispatchers.IO) {
+ mainViewModel.removeInvalidServer()
+ launch(Dispatchers.Main) {
+ mainViewModel.reloadServerList()
+ binding.pbWaiting.hide()
+ }
+ }
}
- .setNegativeButton(android.R.string.no) {_, _ ->
+ .setNegativeButton(android.R.string.no) { _, _ ->
//do noting
}
.show()
true
}
+
R.id.sort_by_test_results -> {
- MmkvManager.sortByTestResults()
- mainViewModel.reloadServerList()
+ binding.pbWaiting.show()
+ lifecycleScope.launch(Dispatchers.IO) {
+ mainViewModel.sortByTestResults()
+ launch(Dispatchers.Main) {
+ mainViewModel.reloadServerList()
+ binding.pbWaiting.hide()
+ }
+ }
true
}
+
else -> super.onOptionsItemSelected(item)
}
- private fun importManually(createConfigType : Int) {
+ private fun importManually(createConfigType: Int) {
startActivity(
Intent()
.putExtra("createConfigType", createConfigType)
@@ -376,16 +441,16 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
// .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP), requestCode)
// } catch (e: Exception) {
RxPermissions(this)
- .request(Manifest.permission.CAMERA)
- .subscribe {
- if (it)
- if (forConfig)
- scanQRCodeForConfig.launch(Intent(this, ScannerActivity::class.java))
- else
- scanQRCodeForUrlToCustomConfig.launch(Intent(this, ScannerActivity::class.java))
+ .request(Manifest.permission.CAMERA)
+ .subscribe {
+ if (it)
+ if (forConfig)
+ scanQRCodeForConfig.launch(Intent(this, ScannerActivity::class.java))
else
- toast(R.string.toast_permission_denied)
- }
+ scanQRCodeForUrlToCustomConfig.launch(Intent(this, ScannerActivity::class.java))
+ else
+ toast(R.string.toast_permission_denied)
+ }
// }
return true
}
@@ -439,7 +504,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
//dialog.dismiss()
binding.pbWaiting.hide()
}
- }
+ }
}
private fun importConfigCustomClipboard()
@@ -516,7 +581,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
/**
* import config from sub
*/
- private fun importConfigViaSub() : Boolean {
+ private fun importConfigViaSub(): Boolean {
// val dialog = AlertDialog.Builder(this)
// .setView(LayoutProgressBinding.inflate(layoutInflater).root)
// .setCancelable(false)
@@ -524,7 +589,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
binding.pbWaiting.show()
lifecycleScope.launch(Dispatchers.IO) {
- val count = AngConfigManager.updateConfigViaSubAll()
+ val count = mainViewModel.updateConfigViaSubAll()
delay(500L)
launch(Dispatchers.Main) {
if (count > 0) {
@@ -572,19 +637,19 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
Manifest.permission.READ_EXTERNAL_STORAGE
}
RxPermissions(this)
- .request(permission)
- .subscribe {
- if (it) {
- try {
- contentResolver.openInputStream(uri).use { input ->
- importCustomizeConfig(input?.bufferedReader()?.readText())
- }
- } catch (e: Exception) {
- e.printStackTrace()
+ .request(permission)
+ .subscribe {
+ if (it) {
+ try {
+ contentResolver.openInputStream(uri).use { input ->
+ importCustomizeConfig(input?.bufferedReader()?.readText())
}
- } else
- toast(R.string.toast_permission_denied)
- }
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ } else
+ toast(R.string.toast_permission_denied)
+ }
}
/**
@@ -632,27 +697,33 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
}
-
override fun onNavigationItemSelected(item: MenuItem): Boolean {
// Handle navigation view item clicks here.
when (item.itemId) {
R.id.sub_setting -> {
- requestSubSettingActivity.launch(Intent(this,SubSettingActivity::class.java))
+ requestSubSettingActivity.launch(Intent(this, SubSettingActivity::class.java))
}
+
R.id.settings -> {
- startActivity(Intent(this, SettingsActivity::class.java)
- .putExtra("isRunning", mainViewModel.isRunning.value == true))
+ startActivity(
+ Intent(this, SettingsActivity::class.java)
+ .putExtra("isRunning", mainViewModel.isRunning.value == true)
+ )
}
+
R.id.user_asset_setting -> {
startActivity(Intent(this, UserAssetActivity::class.java))
}
+
R.id.promotion -> {
Utils.openUri(this, "${Utils.decode(AppConfig.PromotionUrl)}?t=${System.currentTimeMillis()}")
}
+
R.id.logcat -> {
startActivity(Intent(this, LogcatActivity::class.java))
}
- R.id.about-> {
+
+ R.id.about -> {
startActivity(Intent(this, AboutActivity::class.java))
}
}
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 cc9a062f3f..b1e8911259 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
@@ -10,7 +10,6 @@ import androidx.appcompat.app.AlertDialog
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import com.google.gson.Gson
-import com.tencent.mmkv.MMKV
import com.v2ray.ang.AngApplication.Companion.application
import com.v2ray.ang.AppConfig
import com.v2ray.ang.R
@@ -38,9 +37,6 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter by lazy {
mActivity.resources.getStringArray(R.array.share_method)
}
@@ -71,13 +67,13 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter {
holder.itemMainBinding.tvType.text = profile.configType.name
}
+
else -> {
holder.itemMainBinding.tvType.text = profile.configType.name.lowercase()
}
@@ -114,6 +112,7 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter {
if (AngConfigManager.share2Clipboard(mActivity, guid) == 0) {
mActivity.toast(R.string.toast_success)
@@ -121,6 +120,7 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter shareFullContent(guid)
else -> mActivity.toast("else")
}
@@ -132,7 +132,7 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter
removeServer(guid, position)
}
- .setNegativeButton(android.R.string.no) {_, _ ->
+ .setNegativeButton(android.R.string.no) { _, _ ->
//do noting
}
.show()
@@ -159,9 +159,9 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter
MainViewHolder(ItemRecyclerMainBinding.inflate(LayoutInflater.from(parent.context), parent, false))
+
else ->
FooterViewHolder(ItemRecyclerFooterBinding.inflate(LayoutInflater.from(parent.context), parent, false))
}
@@ -231,14 +232,14 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter
- MmkvManager.removeSubscription(editSubId)
- finish()
+ lifecycleScope.launch(Dispatchers.IO) {
+ MmkvManager.removeSubscription(editSubId)
+ launch(Dispatchers.Main) {
+ finish()
+ }
+ }
}
.setNegativeButton(android.R.string.no) {_, _ ->
// do nothing
diff --git a/v2rayng/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/UrlSchemeActivity.kt b/v2rayng/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/UrlSchemeActivity.kt
index c7b71e5980..83cc4a76ac 100644
--- a/v2rayng/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/UrlSchemeActivity.kt
+++ b/v2rayng/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/UrlSchemeActivity.kt
@@ -22,7 +22,7 @@ class UrlSchemeActivity : BaseActivity() {
if (action == Intent.ACTION_SEND) {
if ("text/plain" == type) {
intent.getStringExtra(Intent.EXTRA_TEXT)?.let {
- parseUri(it)
+ parseUri(it, null)
}
}
} else if (action == Intent.ACTION_VIEW) {
@@ -30,13 +30,13 @@ class UrlSchemeActivity : BaseActivity() {
"install-config" -> {
val uri: Uri? = intent.data
val shareUrl = uri?.getQueryParameter("url") ?: ""
- parseUri(shareUrl)
+ parseUri(shareUrl, uri?.fragment)
}
"install-sub" -> {
val uri: Uri? = intent.data
val shareUrl = uri?.getQueryParameter("url") ?: ""
- parseUri(shareUrl)
+ parseUri(shareUrl, uri?.fragment)
}
else -> {
@@ -53,15 +53,19 @@ class UrlSchemeActivity : BaseActivity() {
}
}
- private fun parseUri(uriString: String?) {
+ private fun parseUri(uriString: String?, fragment: String?) {
if (uriString.isNullOrEmpty()) {
return
}
Log.d("UrlScheme", uriString)
- val decodedUrl = URLDecoder.decode(uriString, "UTF-8")
+ var decodedUrl = URLDecoder.decode(uriString, "UTF-8")
val uri = Uri.parse(decodedUrl)
if (uri != null) {
+ if (uri.fragment.isNullOrEmpty() && !fragment.isNullOrEmpty()) {
+ decodedUrl += "#${fragment}"
+ }
+ Log.d("UrlScheme-decodedUrl", decodedUrl)
val (count, countSub) = AngConfigManager.importBatchConfig(decodedUrl, "", false)
if (count + countSub > 0) {
toast(R.string.import_subscription_success)
diff --git a/v2rayng/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/AngConfigManager.kt b/v2rayng/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/AngConfigManager.kt
index 0b60190a38..60317225c6 100644
--- a/v2rayng/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/AngConfigManager.kt
+++ b/v2rayng/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/AngConfigManager.kt
@@ -535,7 +535,7 @@ object AngConfigManager {
return count
}
- private fun updateConfigViaSub(it: Pair): Int {
+ fun updateConfigViaSub(it: Pair): Int {
try {
if (TextUtils.isEmpty(it.first)
|| TextUtils.isEmpty(it.second.remarks)
diff --git a/v2rayng/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/MmkvManager.kt b/v2rayng/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/MmkvManager.kt
index b81c56fa0b..c04bdfc36a 100644
--- a/v2rayng/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/MmkvManager.kt
+++ b/v2rayng/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/MmkvManager.kt
@@ -21,12 +21,14 @@ object MmkvManager {
const val KEY_SELECTED_SERVER = "SELECTED_SERVER"
const val KEY_ANG_CONFIGS = "ANG_CONFIGS"
- private val mainStorage by lazy { MMKV.mmkvWithID(ID_MAIN, MMKV.MULTI_PROCESS_MODE) }
+ val mainStorage by lazy { MMKV.mmkvWithID(ID_MAIN, MMKV.MULTI_PROCESS_MODE) }
+ val settingsStorage by lazy { MMKV.mmkvWithID(ID_SETTING, MMKV.MULTI_PROCESS_MODE) }
private val serverStorage by lazy { MMKV.mmkvWithID(ID_SERVER_CONFIG, MMKV.MULTI_PROCESS_MODE) }
private val profileStorage by lazy { MMKV.mmkvWithID(ID_PROFILE_CONFIG, MMKV.MULTI_PROCESS_MODE) }
private val serverAffStorage by lazy { MMKV.mmkvWithID(ID_SERVER_AFF, MMKV.MULTI_PROCESS_MODE) }
- private val subStorage by lazy { MMKV.mmkvWithID(ID_SUB, MMKV.MULTI_PROCESS_MODE) }
+ val subStorage by lazy { MMKV.mmkvWithID(ID_SUB, MMKV.MULTI_PROCESS_MODE) }
private val assetStorage by lazy { MMKV.mmkvWithID(ID_ASSET, MMKV.MULTI_PROCESS_MODE) }
+ val serverRawStorage by lazy { MMKV.mmkvWithID(ID_SERVER_RAW, MMKV.MULTI_PROCESS_MODE) }
fun decodeServerList(): MutableList {
val json = mainStorage?.decodeString(KEY_ANG_CONFIGS)
@@ -147,7 +149,7 @@ object MmkvManager {
}
val uri = URI(Utils.fixIllegalUrl(url))
val subItem = SubscriptionItem()
- subItem.remarks = Utils.urlDecode(uri.fragment ?: "import sub")
+ subItem.remarks = uri.fragment ?: "import sub"
subItem.url = url
subStorage?.encode(Utils.getUuid(), Gson().toJson(subItem))
return 1
@@ -191,11 +193,19 @@ object MmkvManager {
serverAffStorage?.clearAll()
}
- fun removeInvalidServer() {
- serverAffStorage?.allKeys()?.forEach { key ->
- decodeServerAffiliationInfo(key)?.let { aff ->
+ fun removeInvalidServer(guid: String) {
+ if (guid.isNotEmpty()) {
+ decodeServerAffiliationInfo(guid)?.let { aff ->
if (aff.testDelayMillis < 0L) {
- removeServer(key)
+ removeServer(guid)
+ }
+ }
+ } else {
+ serverAffStorage?.allKeys()?.forEach { key ->
+ decodeServerAffiliationInfo(key)?.let { aff ->
+ if (aff.testDelayMillis < 0L) {
+ removeServer(key)
+ }
}
}
}
diff --git a/v2rayng/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/MainViewModel.kt b/v2rayng/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/MainViewModel.kt
index d0d4e589de..98700451fa 100644
--- a/v2rayng/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/MainViewModel.kt
+++ b/v2rayng/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/MainViewModel.kt
@@ -12,7 +12,6 @@ import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.google.gson.Gson
-import com.tencent.mmkv.MMKV
import com.v2ray.ang.AngApplication
import com.v2ray.ang.AppConfig
import com.v2ray.ang.AppConfig.ANG_PACKAGE
@@ -21,11 +20,15 @@ import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.dto.ProfileItem
import com.v2ray.ang.dto.ServerConfig
import com.v2ray.ang.dto.ServersCache
+import com.v2ray.ang.dto.SubscriptionItem
import com.v2ray.ang.dto.V2rayConfig
import com.v2ray.ang.extension.toast
+import com.v2ray.ang.util.AngConfigManager
+import com.v2ray.ang.util.AngConfigManager.updateConfigViaSub
import com.v2ray.ang.util.MessageUtil
import com.v2ray.ang.util.MmkvManager
import com.v2ray.ang.util.MmkvManager.KEY_ANG_CONFIGS
+import com.v2ray.ang.util.MmkvManager.subStorage
import com.v2ray.ang.util.SpeedtestUtil
import com.v2ray.ang.util.Utils
import com.v2ray.ang.util.V2rayConfigUtil
@@ -39,34 +42,15 @@ import java.io.FileOutputStream
import java.util.Collections
class MainViewModel(application: Application) : AndroidViewModel(application) {
- private val mainStorage by lazy {
- MMKV.mmkvWithID(
- MmkvManager.ID_MAIN,
- MMKV.MULTI_PROCESS_MODE
- )
- }
- private val serverRawStorage by lazy {
- MMKV.mmkvWithID(
- MmkvManager.ID_SERVER_RAW,
- MMKV.MULTI_PROCESS_MODE
- )
- }
- private val settingsStorage by lazy {
- MMKV.mmkvWithID(
- MmkvManager.ID_SETTING,
- MMKV.MULTI_PROCESS_MODE
- )
- }
+ private var serverList = MmkvManager.decodeServerList()
+ var subscriptionId: String = MmkvManager.settingsStorage.decodeString(AppConfig.CACHE_SUBSCRIPTION_ID, "") ?: ""
- var serverList = MmkvManager.decodeServerList()
- var subscriptionId: String = settingsStorage.decodeString(AppConfig.CACHE_SUBSCRIPTION_ID, "")?:""
- //var keywordFilter: String = settingsStorage.decodeString(AppConfig.CACHE_KEYWORD_FILTER, "")?:""
- private set
+ //var keywordFilter: String = MmkvManager.settingsStorage.decodeString(AppConfig.CACHE_KEYWORD_FILTER, "")?:""
+ private var keywordFilter = ""
val serversCache = mutableListOf()
val isRunning by lazy { MutableLiveData() }
val updateListAction by lazy { MutableLiveData() }
val updateTestResultAction by lazy { MutableLiveData() }
-
private val tcpingTestScope by lazy { CoroutineScope(Dispatchers.IO) }
fun startListenBroadcast() {
@@ -120,7 +104,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
config.fullConfig = Gson().fromJson(server, V2rayConfig::class.java)
config.remarks = config.fullConfig?.remarks ?: System.currentTimeMillis().toString()
val key = MmkvManager.encodeServerConfig("", config)
- serverRawStorage?.encode(key, server)
+ MmkvManager.serverRawStorage?.encode(key, server)
serverList.add(0, key)
val profile = ProfileItem(
configType = config.configType,
@@ -141,7 +125,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
fun swapServer(fromPosition: Int, toPosition: Int) {
Collections.swap(serverList, fromPosition, toPosition)
Collections.swap(serversCache, fromPosition, toPosition)
- mainStorage?.encode(KEY_ANG_CONFIGS, Gson().toJson(serverList))
+ MmkvManager.mainStorage?.encode(KEY_ANG_CONFIGS, Gson().toJson(serverList))
}
@Synchronized
@@ -165,11 +149,41 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
continue
}
-// if (keywordFilter.isEmpty() || config.remarks.contains(keywordFilter)) {
- serversCache.add(ServersCache(guid, profile))
+ if (keywordFilter.isEmpty() || profile.remarks.contains(keywordFilter)) {
+ serversCache.add(ServersCache(guid, profile))
+ }
}
}
+ fun updateConfigViaSubAll(): Int {
+ if (subscriptionId.isNullOrEmpty()) {
+ return AngConfigManager.updateConfigViaSubAll()
+ } else {
+ val json = subStorage?.decodeString(subscriptionId)
+ if (!json.isNullOrBlank()) {
+ return updateConfigViaSub(Pair(subscriptionId, Gson().fromJson(json, SubscriptionItem::class.java)))
+ } else {
+ return 0
+ }
+ }
+ }
+
+ fun exportAllServer(): Int {
+ val serverListCopy =
+ if (subscriptionId.isNullOrEmpty()) {
+ serverList
+ } else {
+ serversCache.map { it.guid }.toList()
+ }
+
+ val ret = AngConfigManager.shareNonCustomConfigsToClipboard(
+ getApplication(),
+ serverListCopy
+ )
+ return ret
+ }
+
+
fun testAllTcping() {
tcpingTestScope.coroutineContext[Job]?.cancelChildren()
SpeedtestUtil.closeAllTcpSockets()
@@ -223,12 +237,12 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
fun subscriptionIdChanged(id: String) {
if (subscriptionId != id) {
subscriptionId = id
- settingsStorage.encode(AppConfig.CACHE_SUBSCRIPTION_ID, subscriptionId)
+ MmkvManager.settingsStorage.encode(AppConfig.CACHE_SUBSCRIPTION_ID, subscriptionId)
reloadServerList()
}
}
- fun getSubscriptions(context: Context) : Pair?, MutableList?> {
+ fun getSubscriptions(context: Context): Pair?, MutableList?> {
val subscriptions = MmkvManager.decodeSubscriptions()
if (subscriptionId.isNotEmpty()
&& !subscriptions.map { it.first }.contains(subscriptionId)
@@ -254,36 +268,59 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
return -1
}
- fun removeDuplicateServer() {
- viewModelScope.launch(Dispatchers.Default) {
- val deleteServer = mutableListOf()
- serversCache.forEachIndexed { index, it ->
- val outbound = MmkvManager.decodeServerConfig(it.guid)?.getProxyOutbound()
- serversCache.forEachIndexed { index2, it2 ->
- if (index2 > index) {
- val outbound2 = MmkvManager.decodeServerConfig(it2.guid)?.getProxyOutbound()
- if (outbound == outbound2 && !deleteServer.contains(it2.guid)) {
- deleteServer.add(it2.guid)
- }
+ fun removeDuplicateServer(): Int {
+ val serversCacheCopy = mutableListOf>()
+ for (it in serversCache) {
+ val config = MmkvManager.decodeServerConfig(it.guid) ?: continue
+ serversCacheCopy.add(Pair(it.guid, config))
+ }
+
+ val deleteServer = mutableListOf()
+ serversCacheCopy.forEachIndexed { index, it ->
+ val outbound = it.second.getProxyOutbound()
+ serversCacheCopy.forEachIndexed { index2, it2 ->
+ if (index2 > index) {
+ val outbound2 = it2.second.getProxyOutbound()
+ if (outbound == outbound2 && !deleteServer.contains(it2.first)) {
+ deleteServer.add(it2.first)
}
}
}
- for (it in deleteServer) {
- MmkvManager.removeServer(it)
- }
- launch(Dispatchers.Main) {
- reloadServerList()
- getApplication().toast(
- getApplication().getString(
- R.string.title_del_duplicate_config_count,
- deleteServer.count()
- )
- )
- }
+ }
+ for (it in deleteServer) {
+ MmkvManager.removeServer(it)
+ }
+ return deleteServer.count()
+ }
+
+ fun removeAllServer() {
+ if (subscriptionId.isNullOrEmpty()) {
+ MmkvManager.removeAllServer()
+ } else {
+ val serversCopy = serversCache.toList()
+ for (item in serversCopy) {
+ MmkvManager.removeServer(item.guid)
+ }
}
}
+ fun removeInvalidServer() {
+ if (subscriptionId.isNullOrEmpty()) {
+ MmkvManager.removeInvalidServer("")
+ } else {
+ val serversCopy = serversCache.toList()
+ for (item in serversCopy) {
+ MmkvManager.removeInvalidServer(item.guid)
+ }
+ }
+ }
+
+ fun sortByTestResults() {
+ MmkvManager.sortByTestResults()
+ }
+
+
fun copyAssets(assets: AssetManager) {
val extFolder = Utils.userAssetPath(getApplication())
viewModelScope.launch(Dispatchers.Default) {
@@ -310,6 +347,12 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
}
}
+ fun filterConfig(keyword: String) {
+ keywordFilter = keyword
+ MmkvManager.settingsStorage.encode(AppConfig.CACHE_KEYWORD_FILTER, keywordFilter)
+ reloadServerList()
+ }
+
private val mMsgReceiver = object : BroadcastReceiver() {
override fun onReceive(ctx: Context?, intent: Intent?) {
when (intent?.getIntExtra("key", 0)) {
@@ -347,4 +390,4 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
}
}
}
-}
+}
\ No newline at end of file
diff --git a/v2rayng/V2rayNG/app/src/main/res/menu/menu_main.xml b/v2rayng/V2rayNG/app/src/main/res/menu/menu_main.xml
index 0db2486925..8edaf8a102 100644
--- a/v2rayng/V2rayNG/app/src/main/res/menu/menu_main.xml
+++ b/v2rayng/V2rayNG/app/src/main/res/menu/menu_main.xml
@@ -1,6 +1,12 @@