diff --git a/.github/update.log b/.github/update.log index 624bd89b4b..6f2948eefb 100644 --- a/.github/update.log +++ b/.github/update.log @@ -1311,3 +1311,4 @@ Update On Mon Mar 23 20:06:04 CET 2026 Update On Tue Mar 24 20:16:12 CET 2026 Update On Wed Mar 25 20:07:22 CET 2026 Update On Thu Mar 26 20:20:07 CET 2026 +Update On Fri Mar 27 20:08:05 CET 2026 diff --git a/clash-meta-android/.github/workflows/update-dependencies.yaml b/clash-meta-android/.github/workflows/update-dependencies.yaml index e2b4897b64..74deb3b0b0 100644 --- a/clash-meta-android/.github/workflows/update-dependencies.yaml +++ b/clash-meta-android/.github/workflows/update-dependencies.yaml @@ -65,7 +65,7 @@ jobs: - name: Create Pull Request id: cpr - uses: peter-evans/create-pull-request@v6 + uses: peter-evans/create-pull-request@v8 with: token: ${{ steps.generate-token.outputs.token }} commit-message: Update Dependencies diff --git a/clash-meta/.github/patch/go1.21.patch b/clash-meta/.github/patch/go1.21.patch deleted file mode 100644 index 2ada77813a..0000000000 --- a/clash-meta/.github/patch/go1.21.patch +++ /dev/null @@ -1,182 +0,0 @@ -Subject: [PATCH] Revert "[release-branch.go1.21] crypto/rand,runtime: switch RtlGenRandom for ProcessPrng" ---- -Index: src/crypto/rand/rand.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/crypto/rand/rand.go b/src/crypto/rand/rand.go ---- a/src/crypto/rand/rand.go (revision 8bba868de983dd7bf55fcd121495ba8d6e2734e7) -+++ b/src/crypto/rand/rand.go (revision 7e6c963d81e14ee394402671d4044b2940c8d2c1) -@@ -15,7 +15,7 @@ - // 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 ProcessPrng API. -+// On Windows systems, Reader uses the RtlGenRandom 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 -Index: src/crypto/rand/rand_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/crypto/rand/rand_windows.go b/src/crypto/rand/rand_windows.go ---- a/src/crypto/rand/rand_windows.go (revision 8bba868de983dd7bf55fcd121495ba8d6e2734e7) -+++ b/src/crypto/rand/rand_windows.go (revision 7e6c963d81e14ee394402671d4044b2940c8d2c1) -@@ -15,8 +15,11 @@ - - type rngReader struct{} - --func (r *rngReader) Read(b []byte) (int, error) { -- if err := windows.ProcessPrng(b); err != nil { -+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 { - return 0, err - } - return len(b), nil -Index: src/internal/syscall/windows/syscall_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/internal/syscall/windows/syscall_windows.go b/src/internal/syscall/windows/syscall_windows.go ---- a/src/internal/syscall/windows/syscall_windows.go (revision 8bba868de983dd7bf55fcd121495ba8d6e2734e7) -+++ b/src/internal/syscall/windows/syscall_windows.go (revision 7e6c963d81e14ee394402671d4044b2940c8d2c1) -@@ -384,7 +384,7 @@ - //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 ProcessPrng(buf []byte) (err error) = bcryptprimitives.ProcessPrng -+//sys RtlGenRandom(buf []byte) (err error) = advapi32.SystemFunction036 - - //sys RtlLookupFunctionEntry(pc uintptr, baseAddress *uintptr, table *byte) (ret uintptr) = kernel32.RtlLookupFunctionEntry - //sys RtlVirtualUnwind(handlerType uint32, baseAddress uintptr, pc uintptr, entry uintptr, ctxt uintptr, data *uintptr, frame *uintptr, ctxptrs *byte) (ret uintptr) = kernel32.RtlVirtualUnwind -Index: src/internal/syscall/windows/zsyscall_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/internal/syscall/windows/zsyscall_windows.go b/src/internal/syscall/windows/zsyscall_windows.go ---- a/src/internal/syscall/windows/zsyscall_windows.go (revision 8bba868de983dd7bf55fcd121495ba8d6e2734e7) -+++ b/src/internal/syscall/windows/zsyscall_windows.go (revision 7e6c963d81e14ee394402671d4044b2940c8d2c1) -@@ -37,14 +37,13 @@ - } - - var ( -- 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")) -+ 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")) - - procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges") - procDuplicateTokenEx = modadvapi32.NewProc("DuplicateTokenEx") -@@ -53,7 +52,7 @@ - procOpenThreadToken = modadvapi32.NewProc("OpenThreadToken") - procRevertToSelf = modadvapi32.NewProc("RevertToSelf") - procSetTokenInformation = modadvapi32.NewProc("SetTokenInformation") -- procProcessPrng = modbcryptprimitives.NewProc("ProcessPrng") -+ procSystemFunction036 = modadvapi32.NewProc("SystemFunction036") - procGetAdaptersAddresses = modiphlpapi.NewProc("GetAdaptersAddresses") - procCreateEventW = modkernel32.NewProc("CreateEventW") - procGetACP = modkernel32.NewProc("GetACP") -@@ -149,12 +148,12 @@ - return - } - --func ProcessPrng(buf []byte) (err error) { -+func RtlGenRandom(buf []byte) (err error) { - var _p0 *byte - if len(buf) > 0 { - _p0 = &buf[0] - } -- r1, _, e1 := syscall.Syscall(procProcessPrng.Addr(), 2, uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), 0) -+ r1, _, e1 := syscall.Syscall(procSystemFunction036.Addr(), 2, uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), 0) - if r1 == 0 { - err = errnoErr(e1) - } -Index: src/runtime/os_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go ---- a/src/runtime/os_windows.go (revision 8bba868de983dd7bf55fcd121495ba8d6e2734e7) -+++ b/src/runtime/os_windows.go (revision 7e6c963d81e14ee394402671d4044b2940c8d2c1) -@@ -127,8 +127,15 @@ - _AddVectoredContinueHandler, - _ stdFunction - -- // Use ProcessPrng to generate cryptographically random data. -- _ProcessPrng 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 - - // Load ntdll.dll manually during startup, otherwise Mingw - // links wrong printf function to cgo executable (see issue -@@ -145,12 +152,12 @@ - ) - - var ( -- bcryptprimitivesdll = [...]uint16{'b', 'c', 'r', 'y', 'p', 't', 'p', 'r', 'i', 'm', 'i', 't', 'i', 'v', 'e', 's', '.', 'd', 'l', 'l', 0} -- kernel32dll = [...]uint16{'k', 'e', 'r', 'n', 'e', 'l', '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} -+ advapi32dll = [...]uint16{'a', 'd', 'v', 'a', 'p', 'i', '3', '2', '.', 'd', 'l', 'l', 0} -+ kernel32dll = [...]uint16{'k', 'e', 'r', 'n', 'e', 'l', '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} - ) - - // Function to be called by windows CreateThread -@@ -249,11 +256,11 @@ - } - _AddVectoredContinueHandler = windowsFindfunc(k32, []byte("AddVectoredContinueHandler\000")) - -- bcryptPrimitives := windowsLoadSystemLib(bcryptprimitivesdll[:]) -- if bcryptPrimitives == 0 { -- throw("bcryptprimitives.dll not found") -+ a32 := windowsLoadSystemLib(advapi32dll[:]) -+ if a32 == 0 { -+ throw("advapi32.dll not found") - } -- _ProcessPrng = windowsFindfunc(bcryptPrimitives, []byte("ProcessPrng\000")) -+ _RtlGenRandom = windowsFindfunc(a32, []byte("SystemFunction036\000")) - - n32 := windowsLoadSystemLib(ntdlldll[:]) - if n32 == 0 { -@@ -610,7 +617,7 @@ - //go:nosplit - func getRandomData(r []byte) { - n := 0 -- if stdcall2(_ProcessPrng, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 { -+ if stdcall2(_RtlGenRandom, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 { - n = len(r) - } - extendRandom(r, n) diff --git a/clash-meta/.github/patch/go1.22.patch b/clash-meta/.github/patch/go1.22.patch deleted file mode 100644 index 3c0c66e363..0000000000 --- a/clash-meta/.github/patch/go1.22.patch +++ /dev/null @@ -1,645 +0,0 @@ -Subject: [PATCH] Revert "runtime: always use LoadLibraryEx to load system libraries" -Revert "syscall: remove Windows 7 console handle workaround" -Revert "net: remove sysSocket fallback for Windows 7" -Revert "crypto/rand,runtime: switch RtlGenRandom for ProcessPrng" ---- -Index: src/crypto/rand/rand.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/crypto/rand/rand.go b/src/crypto/rand/rand.go ---- a/src/crypto/rand/rand.go (revision cb4eee693c382bea4222f20837e26501d40ed892) -+++ b/src/crypto/rand/rand.go (revision 9779155f18b6556a034f7bb79fb7fb2aad1e26a9) -@@ -15,7 +15,7 @@ - // 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 ProcessPrng API. -+// On Windows systems, Reader uses the RtlGenRandom 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 -Index: src/crypto/rand/rand_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/crypto/rand/rand_windows.go b/src/crypto/rand/rand_windows.go ---- a/src/crypto/rand/rand_windows.go (revision cb4eee693c382bea4222f20837e26501d40ed892) -+++ b/src/crypto/rand/rand_windows.go (revision 9779155f18b6556a034f7bb79fb7fb2aad1e26a9) -@@ -15,8 +15,11 @@ - - type rngReader struct{} - --func (r *rngReader) Read(b []byte) (int, error) { -- if err := windows.ProcessPrng(b); err != nil { -+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 { - return 0, err - } - return len(b), nil -Index: src/internal/syscall/windows/syscall_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/internal/syscall/windows/syscall_windows.go b/src/internal/syscall/windows/syscall_windows.go ---- a/src/internal/syscall/windows/syscall_windows.go (revision cb4eee693c382bea4222f20837e26501d40ed892) -+++ b/src/internal/syscall/windows/syscall_windows.go (revision 9779155f18b6556a034f7bb79fb7fb2aad1e26a9) -@@ -384,7 +384,7 @@ - //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 ProcessPrng(buf []byte) (err error) = bcryptprimitives.ProcessPrng -+//sys RtlGenRandom(buf []byte) (err error) = advapi32.SystemFunction036 - - type FILE_ID_BOTH_DIR_INFO struct { - NextEntryOffset uint32 -Index: src/internal/syscall/windows/zsyscall_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/internal/syscall/windows/zsyscall_windows.go b/src/internal/syscall/windows/zsyscall_windows.go ---- a/src/internal/syscall/windows/zsyscall_windows.go (revision cb4eee693c382bea4222f20837e26501d40ed892) -+++ b/src/internal/syscall/windows/zsyscall_windows.go (revision 9779155f18b6556a034f7bb79fb7fb2aad1e26a9) -@@ -37,14 +37,13 @@ - } - - var ( -- 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")) -+ 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")) - - procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges") - procDuplicateTokenEx = modadvapi32.NewProc("DuplicateTokenEx") -@@ -56,7 +55,7 @@ - procQueryServiceStatus = modadvapi32.NewProc("QueryServiceStatus") - procRevertToSelf = modadvapi32.NewProc("RevertToSelf") - procSetTokenInformation = modadvapi32.NewProc("SetTokenInformation") -- procProcessPrng = modbcryptprimitives.NewProc("ProcessPrng") -+ procSystemFunction036 = modadvapi32.NewProc("SystemFunction036") - procGetAdaptersAddresses = modiphlpapi.NewProc("GetAdaptersAddresses") - procCreateEventW = modkernel32.NewProc("CreateEventW") - procGetACP = modkernel32.NewProc("GetACP") -@@ -180,12 +179,12 @@ - return - } - --func ProcessPrng(buf []byte) (err error) { -+func RtlGenRandom(buf []byte) (err error) { - var _p0 *byte - if len(buf) > 0 { - _p0 = &buf[0] - } -- r1, _, e1 := syscall.Syscall(procProcessPrng.Addr(), 2, uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), 0) -+ r1, _, e1 := syscall.Syscall(procSystemFunction036.Addr(), 2, uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), 0) - if r1 == 0 { - err = errnoErr(e1) - } -Index: src/runtime/os_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go ---- a/src/runtime/os_windows.go (revision cb4eee693c382bea4222f20837e26501d40ed892) -+++ b/src/runtime/os_windows.go (revision 83ff9782e024cb328b690cbf0da4e7848a327f4f) -@@ -40,8 +40,8 @@ - //go:cgo_import_dynamic runtime._GetSystemInfo GetSystemInfo%1 "kernel32.dll" - //go:cgo_import_dynamic runtime._GetThreadContext GetThreadContext%2 "kernel32.dll" - //go:cgo_import_dynamic runtime._SetThreadContext SetThreadContext%2 "kernel32.dll" --//go:cgo_import_dynamic runtime._LoadLibraryExW LoadLibraryExW%3 "kernel32.dll" - //go:cgo_import_dynamic runtime._LoadLibraryW LoadLibraryW%1 "kernel32.dll" -+//go:cgo_import_dynamic runtime._LoadLibraryA LoadLibraryA%1 "kernel32.dll" - //go:cgo_import_dynamic runtime._PostQueuedCompletionStatus PostQueuedCompletionStatus%4 "kernel32.dll" - //go:cgo_import_dynamic runtime._QueryPerformanceCounter QueryPerformanceCounter%1 "kernel32.dll" - //go:cgo_import_dynamic runtime._RaiseFailFastException RaiseFailFastException%3 "kernel32.dll" -@@ -74,7 +74,6 @@ - // Following syscalls are available on every Windows PC. - // All these variables are set by the Windows executable - // loader before the Go program starts. -- _AddVectoredContinueHandler, - _AddVectoredExceptionHandler, - _CloseHandle, - _CreateEventA, -@@ -98,8 +97,8 @@ - _GetSystemInfo, - _GetThreadContext, - _SetThreadContext, -- _LoadLibraryExW, - _LoadLibraryW, -+ _LoadLibraryA, - _PostQueuedCompletionStatus, - _QueryPerformanceCounter, - _RaiseFailFastException, -@@ -127,8 +126,23 @@ - _WriteFile, - _ stdFunction - -- // Use ProcessPrng to generate cryptographically random data. -- _ProcessPrng stdFunction -+ // Following syscalls are only available on some Windows PCs. -+ // We will load syscalls, if available, before using them. -+ _AddDllDirectory, -+ _AddVectoredContinueHandler, -+ _LoadLibraryExA, -+ _LoadLibraryExW, -+ _ 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 - - // Load ntdll.dll manually during startup, otherwise Mingw - // links wrong printf function to cgo executable (see issue -@@ -143,14 +157,6 @@ - _ stdFunction - ) - --var ( -- 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 - // to start new os thread. - func tstart_stdcall(newm *m) -@@ -239,25 +245,51 @@ - return unsafe.String(&sysDirectory[0], sysDirectoryLen) - } - --func windowsLoadSystemLib(name []uint16) uintptr { -- return stdcall3(_LoadLibraryExW, uintptr(unsafe.Pointer(&name[0])), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32) -+//go:linkname syscall_getSystemDirectory syscall.getSystemDirectory -+func syscall_getSystemDirectory() string { -+ return unsafe.String(&sysDirectory[0], sysDirectoryLen) -+} -+ -+func windowsLoadSystemLib(name []byte) uintptr { -+ if useLoadLibraryEx { -+ return stdcall3(_LoadLibraryExA, uintptr(unsafe.Pointer(&name[0])), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32) -+ } else { -+ absName := append(sysDirectory[:sysDirectoryLen], name...) -+ return stdcall1(_LoadLibraryA, uintptr(unsafe.Pointer(&absName[0]))) -+ } - } - - func loadOptionalSyscalls() { -- bcryptPrimitives := windowsLoadSystemLib(bcryptprimitivesdll[:]) -- if bcryptPrimitives == 0 { -- throw("bcryptprimitives.dll not found") -+ var kernel32dll = []byte("kernel32.dll\000") -+ k32 := stdcall1(_LoadLibraryA, uintptr(unsafe.Pointer(&kernel32dll[0]))) -+ if k32 == 0 { -+ throw("kernel32.dll not found") - } -- _ProcessPrng = windowsFindfunc(bcryptPrimitives, []byte("ProcessPrng\000")) -+ _AddDllDirectory = windowsFindfunc(k32, []byte("AddDllDirectory\000")) -+ _AddVectoredContinueHandler = windowsFindfunc(k32, []byte("AddVectoredContinueHandler\000")) -+ _LoadLibraryExA = windowsFindfunc(k32, []byte("LoadLibraryExA\000")) -+ _LoadLibraryExW = windowsFindfunc(k32, []byte("LoadLibraryExW\000")) -+ useLoadLibraryEx = (_LoadLibraryExW != nil && _LoadLibraryExA != nil && _AddDllDirectory != nil) -+ -+ initSysDirectory() - -- n32 := windowsLoadSystemLib(ntdlldll[:]) -+ var advapi32dll = []byte("advapi32.dll\000") -+ a32 := windowsLoadSystemLib(advapi32dll) -+ if a32 == 0 { -+ throw("advapi32.dll not found") -+ } -+ _RtlGenRandom = windowsFindfunc(a32, []byte("SystemFunction036\000")) -+ -+ var ntdll = []byte("ntdll.dll\000") -+ n32 := windowsLoadSystemLib(ntdll) - if n32 == 0 { - throw("ntdll.dll not found") - } - _RtlGetCurrentPeb = windowsFindfunc(n32, []byte("RtlGetCurrentPeb\000")) - _RtlGetNtVersionNumbers = windowsFindfunc(n32, []byte("RtlGetNtVersionNumbers\000")) - -- m32 := windowsLoadSystemLib(winmmdll[:]) -+ var winmmdll = []byte("winmm.dll\000") -+ m32 := windowsLoadSystemLib(winmmdll) - if m32 == 0 { - throw("winmm.dll not found") - } -@@ -267,7 +299,8 @@ - throw("timeBegin/EndPeriod not found") - } - -- ws232 := windowsLoadSystemLib(ws2_32dll[:]) -+ var ws232dll = []byte("ws2_32.dll\000") -+ ws232 := windowsLoadSystemLib(ws232dll) - if ws232 == 0 { - throw("ws2_32.dll not found") - } -@@ -286,7 +319,7 @@ - context uintptr - } - -- powrprof := windowsLoadSystemLib(powrprofdll[:]) -+ powrprof := windowsLoadSystemLib([]byte("powrprof.dll\000")) - if powrprof == 0 { - return // Running on Windows 7, where we don't need it anyway. - } -@@ -360,6 +393,22 @@ - // in sys_windows_386.s and sys_windows_amd64.s: - func getlasterror() uint32 - -+// When loading DLLs, we prefer to use LoadLibraryEx with -+// LOAD_LIBRARY_SEARCH_* flags, if available. LoadLibraryEx is not -+// available on old Windows, though, and the LOAD_LIBRARY_SEARCH_* -+// flags are not available on some versions of Windows without a -+// security patch. -+// -+// https://msdn.microsoft.com/en-us/library/ms684179(v=vs.85).aspx says: -+// "Windows 7, Windows Server 2008 R2, Windows Vista, and Windows -+// Server 2008: The LOAD_LIBRARY_SEARCH_* flags are available on -+// systems that have KB2533623 installed. To determine whether the -+// flags are available, use GetProcAddress to get the address of the -+// AddDllDirectory, RemoveDllDirectory, or SetDefaultDllDirectories -+// function. If GetProcAddress succeeds, the LOAD_LIBRARY_SEARCH_* -+// flags can be used with LoadLibraryEx." -+var useLoadLibraryEx bool -+ - var timeBeginPeriodRetValue uint32 - - // osRelaxMinNS indicates that sysmon shouldn't osRelax if the next -@@ -507,7 +556,6 @@ - initHighResTimer() - timeBeginPeriodRetValue = osRelax(false) - -- initSysDirectory() - initLongPathSupport() - - ncpu = getproccount() -@@ -524,7 +572,7 @@ - //go:nosplit - func readRandom(r []byte) int { - n := 0 -- if stdcall2(_ProcessPrng, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 { -+ if stdcall2(_RtlGenRandom, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 { - n = len(r) - } - return n -Index: src/net/hook_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/hook_windows.go b/src/net/hook_windows.go ---- a/src/net/hook_windows.go (revision 9779155f18b6556a034f7bb79fb7fb2aad1e26a9) -+++ b/src/net/hook_windows.go (revision ef0606261340e608017860b423ffae5c1ce78239) -@@ -13,6 +13,7 @@ - hostsFilePath = windows.GetSystemDirectory() + "/Drivers/etc/hosts" - - // 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 -Index: src/net/internal/socktest/main_test.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/internal/socktest/main_test.go b/src/net/internal/socktest/main_test.go ---- a/src/net/internal/socktest/main_test.go (revision 9779155f18b6556a034f7bb79fb7fb2aad1e26a9) -+++ b/src/net/internal/socktest/main_test.go (revision ef0606261340e608017860b423ffae5c1ce78239) -@@ -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 && !windows -+//go:build !js && !plan9 && !wasip1 - - package socktest_test - -Index: src/net/internal/socktest/main_windows_test.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/internal/socktest/main_windows_test.go b/src/net/internal/socktest/main_windows_test.go -new file mode 100644 ---- /dev/null (revision ef0606261340e608017860b423ffae5c1ce78239) -+++ b/src/net/internal/socktest/main_windows_test.go (revision ef0606261340e608017860b423ffae5c1ce78239) -@@ -0,0 +1,22 @@ -+// 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 -+} -Index: src/net/internal/socktest/sys_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/internal/socktest/sys_windows.go b/src/net/internal/socktest/sys_windows.go ---- a/src/net/internal/socktest/sys_windows.go (revision 9779155f18b6556a034f7bb79fb7fb2aad1e26a9) -+++ b/src/net/internal/socktest/sys_windows.go (revision ef0606261340e608017860b423ffae5c1ce78239) -@@ -9,6 +9,38 @@ - "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) -Index: src/net/main_windows_test.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/main_windows_test.go b/src/net/main_windows_test.go ---- a/src/net/main_windows_test.go (revision 9779155f18b6556a034f7bb79fb7fb2aad1e26a9) -+++ b/src/net/main_windows_test.go (revision ef0606261340e608017860b423ffae5c1ce78239) -@@ -8,6 +8,7 @@ - - var ( - // Placeholders for saving original socket system calls. -+ origSocket = socketFunc - origWSASocket = wsaSocketFunc - origClosesocket = poll.CloseFunc - origConnect = connectFunc -@@ -17,6 +18,7 @@ - ) - - func installTestHooks() { -+ socketFunc = sw.Socket - wsaSocketFunc = sw.WSASocket - poll.CloseFunc = sw.Closesocket - connectFunc = sw.Connect -@@ -26,6 +28,7 @@ - } - - func uninstallTestHooks() { -+ socketFunc = origSocket - wsaSocketFunc = origWSASocket - poll.CloseFunc = origClosesocket - connectFunc = origConnect -Index: src/net/sock_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/sock_windows.go b/src/net/sock_windows.go ---- a/src/net/sock_windows.go (revision 9779155f18b6556a034f7bb79fb7fb2aad1e26a9) -+++ b/src/net/sock_windows.go (revision ef0606261340e608017860b423ffae5c1ce78239) -@@ -20,6 +20,21 @@ - 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) - } -Index: src/syscall/exec_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/syscall/exec_windows.go b/src/syscall/exec_windows.go ---- a/src/syscall/exec_windows.go (revision 9779155f18b6556a034f7bb79fb7fb2aad1e26a9) -+++ b/src/syscall/exec_windows.go (revision 7f83badcb925a7e743188041cb6e561fc9b5b642) -@@ -14,7 +14,6 @@ - "unsafe" - ) - --// ForkLock is not used on Windows. - var ForkLock sync.RWMutex - - // EscapeArg rewrites command line argument s as prescribed -@@ -317,6 +316,17 @@ - } - } - -+ 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 { -@@ -325,7 +335,15 @@ - fd := make([]Handle, len(attr.Files)) - for i := range attr.Files { - if attr.Files[i] > 0 { -- err := DuplicateHandle(p, Handle(attr.Files[i]), parentProcess, &fd[i], 0, true, DUPLICATE_SAME_ACCESS) -+ 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) - if err != nil { - return 0, 0, err - } -@@ -356,6 +374,14 @@ - - 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 -Index: src/runtime/syscall_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/runtime/syscall_windows.go b/src/runtime/syscall_windows.go ---- a/src/runtime/syscall_windows.go (revision 7f83badcb925a7e743188041cb6e561fc9b5b642) -+++ b/src/runtime/syscall_windows.go (revision 83ff9782e024cb328b690cbf0da4e7848a327f4f) -@@ -413,23 +413,36 @@ - - const _LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800 - -+// When available, this function will use LoadLibraryEx with the filename -+// parameter and the important SEARCH_SYSTEM32 argument. But on systems that -+// do not have that option, absoluteFilepath should contain a fallback -+// to the full path inside of system32 for use with vanilla LoadLibrary. -+// - //go:linkname syscall_loadsystemlibrary syscall.loadsystemlibrary - //go:nosplit - //go:cgo_unsafe_args --func syscall_loadsystemlibrary(filename *uint16) (handle, err uintptr) { -+func syscall_loadsystemlibrary(filename *uint16, absoluteFilepath *uint16) (handle, err uintptr) { - lockOSThread() - c := &getg().m.syscall -- c.fn = getLoadLibraryEx() -- c.n = 3 -- args := struct { -- lpFileName *uint16 -- hFile uintptr // always 0 -- flags uint32 -- }{filename, 0, _LOAD_LIBRARY_SEARCH_SYSTEM32} -- c.args = uintptr(noescape(unsafe.Pointer(&args))) -+ -+ if useLoadLibraryEx { -+ c.fn = getLoadLibraryEx() -+ c.n = 3 -+ args := struct { -+ lpFileName *uint16 -+ hFile uintptr // always 0 -+ flags uint32 -+ }{filename, 0, _LOAD_LIBRARY_SEARCH_SYSTEM32} -+ c.args = uintptr(noescape(unsafe.Pointer(&args))) -+ } else { -+ c.fn = getLoadLibrary() -+ c.n = 1 -+ c.args = uintptr(noescape(unsafe.Pointer(&absoluteFilepath))) -+ } - - cgocall(asmstdcallAddr, unsafe.Pointer(c)) - KeepAlive(filename) -+ KeepAlive(absoluteFilepath) - handle = c.r1 - if handle == 0 { - err = c.err -Index: src/syscall/dll_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/syscall/dll_windows.go b/src/syscall/dll_windows.go ---- a/src/syscall/dll_windows.go (revision 7f83badcb925a7e743188041cb6e561fc9b5b642) -+++ b/src/syscall/dll_windows.go (revision 83ff9782e024cb328b690cbf0da4e7848a327f4f) -@@ -44,7 +44,7 @@ - - func SyscallN(trap uintptr, args ...uintptr) (r1, r2 uintptr, err Errno) - func loadlibrary(filename *uint16) (handle uintptr, err Errno) --func loadsystemlibrary(filename *uint16) (handle uintptr, err Errno) -+func loadsystemlibrary(filename *uint16, absoluteFilepath *uint16) (handle uintptr, err Errno) - func getprocaddress(handle uintptr, procname *uint8) (proc uintptr, err Errno) - - // A DLL implements access to a single DLL. -@@ -53,6 +53,9 @@ - Handle Handle - } - -+//go:linkname getSystemDirectory -+func getSystemDirectory() string // Implemented in runtime package. -+ - // LoadDLL loads the named DLL file into memory. - // - // If name is not an absolute path and is not a known system DLL used by -@@ -69,7 +72,11 @@ - var h uintptr - var e Errno - if sysdll.IsSystemDLL[name] { -- h, e = loadsystemlibrary(namep) -+ absoluteFilepathp, err := UTF16PtrFromString(getSystemDirectory() + name) -+ if err != nil { -+ return nil, err -+ } -+ h, e = loadsystemlibrary(namep, absoluteFilepathp) - } else { - h, e = loadlibrary(namep) - } diff --git a/clash-meta/.github/patch/go1.23.patch b/clash-meta/.github/patch/go1.23.patch deleted file mode 100644 index e8597548f7..0000000000 --- a/clash-meta/.github/patch/go1.23.patch +++ /dev/null @@ -1,643 +0,0 @@ -Subject: [PATCH] Revert "runtime: always use LoadLibraryEx to load system libraries" -Revert "syscall: remove Windows 7 console handle workaround" -Revert "net: remove sysSocket fallback for Windows 7" -Revert "crypto/rand,runtime: switch RtlGenRandom for ProcessPrng" ---- -Index: src/crypto/rand/rand.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/crypto/rand/rand.go b/src/crypto/rand/rand.go ---- a/src/crypto/rand/rand.go (revision 6885bad7dd86880be6929c02085e5c7a67ff2887) -+++ b/src/crypto/rand/rand.go (revision 9ac42137ef6730e8b7daca016ece831297a1d75b) -@@ -16,7 +16,7 @@ - // - On macOS and iOS, Reader uses arc4random_buf(3). - // - On OpenBSD and NetBSD, Reader uses getentropy(2). - // - On other Unix-like systems, Reader reads from /dev/urandom. --// - On Windows, Reader uses the ProcessPrng API. -+// - On Windows systems, Reader uses the RtlGenRandom 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 -Index: src/crypto/rand/rand_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/crypto/rand/rand_windows.go b/src/crypto/rand/rand_windows.go ---- a/src/crypto/rand/rand_windows.go (revision 6885bad7dd86880be6929c02085e5c7a67ff2887) -+++ b/src/crypto/rand/rand_windows.go (revision 9ac42137ef6730e8b7daca016ece831297a1d75b) -@@ -15,8 +15,11 @@ - - type rngReader struct{} - --func (r *rngReader) Read(b []byte) (int, error) { -- if err := windows.ProcessPrng(b); err != nil { -+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 { - return 0, err - } - return len(b), nil -Index: src/internal/syscall/windows/syscall_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/internal/syscall/windows/syscall_windows.go b/src/internal/syscall/windows/syscall_windows.go ---- a/src/internal/syscall/windows/syscall_windows.go (revision 6885bad7dd86880be6929c02085e5c7a67ff2887) -+++ b/src/internal/syscall/windows/syscall_windows.go (revision 9ac42137ef6730e8b7daca016ece831297a1d75b) -@@ -414,7 +414,7 @@ - //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 ProcessPrng(buf []byte) (err error) = bcryptprimitives.ProcessPrng -+//sys RtlGenRandom(buf []byte) (err error) = advapi32.SystemFunction036 - - type FILE_ID_BOTH_DIR_INFO struct { - NextEntryOffset uint32 -Index: src/internal/syscall/windows/zsyscall_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/internal/syscall/windows/zsyscall_windows.go b/src/internal/syscall/windows/zsyscall_windows.go ---- a/src/internal/syscall/windows/zsyscall_windows.go (revision 6885bad7dd86880be6929c02085e5c7a67ff2887) -+++ b/src/internal/syscall/windows/zsyscall_windows.go (revision 9ac42137ef6730e8b7daca016ece831297a1d75b) -@@ -38,7 +38,6 @@ - - var ( - 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")) -@@ -57,7 +56,7 @@ - procQueryServiceStatus = modadvapi32.NewProc("QueryServiceStatus") - procRevertToSelf = modadvapi32.NewProc("RevertToSelf") - procSetTokenInformation = modadvapi32.NewProc("SetTokenInformation") -- procProcessPrng = modbcryptprimitives.NewProc("ProcessPrng") -+ procSystemFunction036 = modadvapi32.NewProc("SystemFunction036") - procGetAdaptersAddresses = modiphlpapi.NewProc("GetAdaptersAddresses") - procCreateEventW = modkernel32.NewProc("CreateEventW") - procGetACP = modkernel32.NewProc("GetACP") -@@ -183,12 +182,12 @@ - return - } - --func ProcessPrng(buf []byte) (err error) { -+func RtlGenRandom(buf []byte) (err error) { - var _p0 *byte - if len(buf) > 0 { - _p0 = &buf[0] - } -- r1, _, e1 := syscall.Syscall(procProcessPrng.Addr(), 2, uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), 0) -+ r1, _, e1 := syscall.Syscall(procSystemFunction036.Addr(), 2, uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), 0) - if r1 == 0 { - err = errnoErr(e1) - } -Index: src/runtime/os_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go ---- a/src/runtime/os_windows.go (revision 6885bad7dd86880be6929c02085e5c7a67ff2887) -+++ b/src/runtime/os_windows.go (revision 69e2eed6dd0f6d815ebf15797761c13f31213dd6) -@@ -39,8 +39,8 @@ - //go:cgo_import_dynamic runtime._GetSystemInfo GetSystemInfo%1 "kernel32.dll" - //go:cgo_import_dynamic runtime._GetThreadContext GetThreadContext%2 "kernel32.dll" - //go:cgo_import_dynamic runtime._SetThreadContext SetThreadContext%2 "kernel32.dll" --//go:cgo_import_dynamic runtime._LoadLibraryExW LoadLibraryExW%3 "kernel32.dll" - //go:cgo_import_dynamic runtime._LoadLibraryW LoadLibraryW%1 "kernel32.dll" -+//go:cgo_import_dynamic runtime._LoadLibraryA LoadLibraryA%1 "kernel32.dll" - //go:cgo_import_dynamic runtime._PostQueuedCompletionStatus PostQueuedCompletionStatus%4 "kernel32.dll" - //go:cgo_import_dynamic runtime._QueryPerformanceCounter QueryPerformanceCounter%1 "kernel32.dll" - //go:cgo_import_dynamic runtime._QueryPerformanceFrequency QueryPerformanceFrequency%1 "kernel32.dll" -@@ -74,7 +74,6 @@ - // Following syscalls are available on every Windows PC. - // All these variables are set by the Windows executable - // loader before the Go program starts. -- _AddVectoredContinueHandler, - _AddVectoredExceptionHandler, - _CloseHandle, - _CreateEventA, -@@ -97,8 +96,8 @@ - _GetSystemInfo, - _GetThreadContext, - _SetThreadContext, -- _LoadLibraryExW, - _LoadLibraryW, -+ _LoadLibraryA, - _PostQueuedCompletionStatus, - _QueryPerformanceCounter, - _QueryPerformanceFrequency, -@@ -127,8 +126,23 @@ - _WriteFile, - _ stdFunction - -- // Use ProcessPrng to generate cryptographically random data. -- _ProcessPrng stdFunction -+ // Following syscalls are only available on some Windows PCs. -+ // We will load syscalls, if available, before using them. -+ _AddDllDirectory, -+ _AddVectoredContinueHandler, -+ _LoadLibraryExA, -+ _LoadLibraryExW, -+ _ 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 - - // Load ntdll.dll manually during startup, otherwise Mingw - // links wrong printf function to cgo executable (see issue -@@ -145,13 +159,6 @@ - _ stdFunction - ) - --var ( -- 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} --) -- - // Function to be called by windows CreateThread - // to start new os thread. - func tstart_stdcall(newm *m) -@@ -244,8 +251,18 @@ - return unsafe.String(&sysDirectory[0], sysDirectoryLen) - } - --func windowsLoadSystemLib(name []uint16) uintptr { -- return stdcall3(_LoadLibraryExW, uintptr(unsafe.Pointer(&name[0])), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32) -+//go:linkname syscall_getSystemDirectory syscall.getSystemDirectory -+func syscall_getSystemDirectory() string { -+ return unsafe.String(&sysDirectory[0], sysDirectoryLen) -+} -+ -+func windowsLoadSystemLib(name []byte) uintptr { -+ if useLoadLibraryEx { -+ return stdcall3(_LoadLibraryExA, uintptr(unsafe.Pointer(&name[0])), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32) -+ } else { -+ absName := append(sysDirectory[:sysDirectoryLen], name...) -+ return stdcall1(_LoadLibraryA, uintptr(unsafe.Pointer(&absName[0]))) -+ } - } - - //go:linkname windows_QueryPerformanceCounter internal/syscall/windows.QueryPerformanceCounter -@@ -263,13 +280,28 @@ - } - - func loadOptionalSyscalls() { -- bcryptPrimitives := windowsLoadSystemLib(bcryptprimitivesdll[:]) -- if bcryptPrimitives == 0 { -- throw("bcryptprimitives.dll not found") -+ var kernel32dll = []byte("kernel32.dll\000") -+ k32 := stdcall1(_LoadLibraryA, uintptr(unsafe.Pointer(&kernel32dll[0]))) -+ if k32 == 0 { -+ throw("kernel32.dll not found") - } -- _ProcessPrng = windowsFindfunc(bcryptPrimitives, []byte("ProcessPrng\000")) -+ _AddDllDirectory = windowsFindfunc(k32, []byte("AddDllDirectory\000")) -+ _AddVectoredContinueHandler = windowsFindfunc(k32, []byte("AddVectoredContinueHandler\000")) -+ _LoadLibraryExA = windowsFindfunc(k32, []byte("LoadLibraryExA\000")) -+ _LoadLibraryExW = windowsFindfunc(k32, []byte("LoadLibraryExW\000")) -+ useLoadLibraryEx = (_LoadLibraryExW != nil && _LoadLibraryExA != nil && _AddDllDirectory != nil) -+ -+ initSysDirectory() - -- n32 := windowsLoadSystemLib(ntdlldll[:]) -+ var advapi32dll = []byte("advapi32.dll\000") -+ a32 := windowsLoadSystemLib(advapi32dll) -+ if a32 == 0 { -+ throw("advapi32.dll not found") -+ } -+ _RtlGenRandom = windowsFindfunc(a32, []byte("SystemFunction036\000")) -+ -+ var ntdll = []byte("ntdll.dll\000") -+ n32 := windowsLoadSystemLib(ntdll) - if n32 == 0 { - throw("ntdll.dll not found") - } -@@ -298,7 +330,7 @@ - context uintptr - } - -- powrprof := windowsLoadSystemLib(powrprofdll[:]) -+ powrprof := windowsLoadSystemLib([]byte("powrprof.dll\000")) - if powrprof == 0 { - return // Running on Windows 7, where we don't need it anyway. - } -@@ -357,6 +389,22 @@ - // in sys_windows_386.s and sys_windows_amd64.s: - func getlasterror() uint32 - -+// When loading DLLs, we prefer to use LoadLibraryEx with -+// LOAD_LIBRARY_SEARCH_* flags, if available. LoadLibraryEx is not -+// available on old Windows, though, and the LOAD_LIBRARY_SEARCH_* -+// flags are not available on some versions of Windows without a -+// security patch. -+// -+// https://msdn.microsoft.com/en-us/library/ms684179(v=vs.85).aspx says: -+// "Windows 7, Windows Server 2008 R2, Windows Vista, and Windows -+// Server 2008: The LOAD_LIBRARY_SEARCH_* flags are available on -+// systems that have KB2533623 installed. To determine whether the -+// flags are available, use GetProcAddress to get the address of the -+// AddDllDirectory, RemoveDllDirectory, or SetDefaultDllDirectories -+// function. If GetProcAddress succeeds, the LOAD_LIBRARY_SEARCH_* -+// flags can be used with LoadLibraryEx." -+var useLoadLibraryEx bool -+ - var timeBeginPeriodRetValue uint32 - - // osRelaxMinNS indicates that sysmon shouldn't osRelax if the next -@@ -430,7 +478,8 @@ - // Only load winmm.dll if we need it. - // This avoids a dependency on winmm.dll for Go programs - // that run on new Windows versions. -- m32 := windowsLoadSystemLib(winmmdll[:]) -+ var winmmdll = []byte("winmm.dll\000") -+ m32 := windowsLoadSystemLib(winmmdll) - if m32 == 0 { - print("runtime: LoadLibraryExW failed; errno=", getlasterror(), "\n") - throw("winmm.dll not found") -@@ -471,6 +520,28 @@ - canUseLongPaths = true - } - -+var osVersionInfo struct { -+ majorVersion uint32 -+ minorVersion uint32 -+ buildNumber uint32 -+} -+ -+func initOsVersionInfo() { -+ info := _OSVERSIONINFOW{} -+ info.osVersionInfoSize = uint32(unsafe.Sizeof(info)) -+ stdcall1(_RtlGetVersion, uintptr(unsafe.Pointer(&info))) -+ osVersionInfo.majorVersion = info.majorVersion -+ osVersionInfo.minorVersion = info.minorVersion -+ osVersionInfo.buildNumber = info.buildNumber -+} -+ -+//go:linkname rtlGetNtVersionNumbers syscall.rtlGetNtVersionNumbers -+func rtlGetNtVersionNumbers(majorVersion *uint32, minorVersion *uint32, buildNumber *uint32) { -+ *majorVersion = osVersionInfo.majorVersion -+ *minorVersion = osVersionInfo.minorVersion -+ *buildNumber = osVersionInfo.buildNumber -+} -+ - func osinit() { - asmstdcallAddr = unsafe.Pointer(abi.FuncPCABI0(asmstdcall)) - -@@ -483,8 +554,8 @@ - initHighResTimer() - timeBeginPeriodRetValue = osRelax(false) - -- initSysDirectory() - initLongPathSupport() -+ initOsVersionInfo() - - ncpu = getproccount() - -@@ -500,7 +571,7 @@ - //go:nosplit - func readRandom(r []byte) int { - n := 0 -- if stdcall2(_ProcessPrng, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 { -+ if stdcall2(_RtlGenRandom, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 { - n = len(r) - } - return n -Index: src/net/hook_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/hook_windows.go b/src/net/hook_windows.go ---- a/src/net/hook_windows.go (revision 9ac42137ef6730e8b7daca016ece831297a1d75b) -+++ b/src/net/hook_windows.go (revision 21290de8a4c91408de7c2b5b68757b1e90af49dd) -@@ -13,6 +13,7 @@ - hostsFilePath = windows.GetSystemDirectory() + "/Drivers/etc/hosts" - - // 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 -Index: src/net/internal/socktest/main_test.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/internal/socktest/main_test.go b/src/net/internal/socktest/main_test.go ---- a/src/net/internal/socktest/main_test.go (revision 9ac42137ef6730e8b7daca016ece831297a1d75b) -+++ b/src/net/internal/socktest/main_test.go (revision 21290de8a4c91408de7c2b5b68757b1e90af49dd) -@@ -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 && !windows -+//go:build !js && !plan9 && !wasip1 - - package socktest_test - -Index: src/net/internal/socktest/main_windows_test.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/internal/socktest/main_windows_test.go b/src/net/internal/socktest/main_windows_test.go -new file mode 100644 ---- /dev/null (revision 21290de8a4c91408de7c2b5b68757b1e90af49dd) -+++ b/src/net/internal/socktest/main_windows_test.go (revision 21290de8a4c91408de7c2b5b68757b1e90af49dd) -@@ -0,0 +1,22 @@ -+// 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 -+} -Index: src/net/internal/socktest/sys_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/internal/socktest/sys_windows.go b/src/net/internal/socktest/sys_windows.go ---- a/src/net/internal/socktest/sys_windows.go (revision 9ac42137ef6730e8b7daca016ece831297a1d75b) -+++ b/src/net/internal/socktest/sys_windows.go (revision 21290de8a4c91408de7c2b5b68757b1e90af49dd) -@@ -9,6 +9,38 @@ - "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) -Index: src/net/main_windows_test.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/main_windows_test.go b/src/net/main_windows_test.go ---- a/src/net/main_windows_test.go (revision 9ac42137ef6730e8b7daca016ece831297a1d75b) -+++ b/src/net/main_windows_test.go (revision 21290de8a4c91408de7c2b5b68757b1e90af49dd) -@@ -8,6 +8,7 @@ - - var ( - // Placeholders for saving original socket system calls. -+ origSocket = socketFunc - origWSASocket = wsaSocketFunc - origClosesocket = poll.CloseFunc - origConnect = connectFunc -@@ -17,6 +18,7 @@ - ) - - func installTestHooks() { -+ socketFunc = sw.Socket - wsaSocketFunc = sw.WSASocket - poll.CloseFunc = sw.Closesocket - connectFunc = sw.Connect -@@ -26,6 +28,7 @@ - } - - func uninstallTestHooks() { -+ socketFunc = origSocket - wsaSocketFunc = origWSASocket - poll.CloseFunc = origClosesocket - connectFunc = origConnect -Index: src/net/sock_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/sock_windows.go b/src/net/sock_windows.go ---- a/src/net/sock_windows.go (revision 9ac42137ef6730e8b7daca016ece831297a1d75b) -+++ b/src/net/sock_windows.go (revision 21290de8a4c91408de7c2b5b68757b1e90af49dd) -@@ -20,6 +20,21 @@ - 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) - } -Index: src/syscall/exec_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/syscall/exec_windows.go b/src/syscall/exec_windows.go ---- a/src/syscall/exec_windows.go (revision 9ac42137ef6730e8b7daca016ece831297a1d75b) -+++ b/src/syscall/exec_windows.go (revision 6a31d3fa8e47ddabc10bd97bff10d9a85f4cfb76) -@@ -14,7 +14,6 @@ - "unsafe" - ) - --// ForkLock is not used on Windows. - var ForkLock sync.RWMutex - - // EscapeArg rewrites command line argument s as prescribed -@@ -254,6 +253,9 @@ - var zeroProcAttr ProcAttr - var zeroSysProcAttr SysProcAttr - -+//go:linkname rtlGetNtVersionNumbers -+func rtlGetNtVersionNumbers(majorVersion *uint32, minorVersion *uint32, buildNumber *uint32) -+ - func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle uintptr, err error) { - if len(argv0) == 0 { - return 0, 0, EWINDOWS -@@ -317,6 +319,17 @@ - } - } - -+ 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 { -@@ -325,7 +338,15 @@ - fd := make([]Handle, len(attr.Files)) - for i := range attr.Files { - if attr.Files[i] > 0 { -- err := DuplicateHandle(p, Handle(attr.Files[i]), parentProcess, &fd[i], 0, true, DUPLICATE_SAME_ACCESS) -+ 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) - if err != nil { - return 0, 0, err - } -@@ -356,6 +377,14 @@ - - 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 -Index: src/runtime/syscall_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/runtime/syscall_windows.go b/src/runtime/syscall_windows.go ---- a/src/runtime/syscall_windows.go (revision 6a31d3fa8e47ddabc10bd97bff10d9a85f4cfb76) -+++ b/src/runtime/syscall_windows.go (revision 69e2eed6dd0f6d815ebf15797761c13f31213dd6) -@@ -413,10 +413,20 @@ - - const _LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800 - -+// When available, this function will use LoadLibraryEx with the filename -+// parameter and the important SEARCH_SYSTEM32 argument. But on systems that -+// do not have that option, absoluteFilepath should contain a fallback -+// to the full path inside of system32 for use with vanilla LoadLibrary. -+// - //go:linkname syscall_loadsystemlibrary syscall.loadsystemlibrary --func syscall_loadsystemlibrary(filename *uint16) (handle, err uintptr) { -- handle, _, err = syscall_SyscallN(uintptr(unsafe.Pointer(_LoadLibraryExW)), uintptr(unsafe.Pointer(filename)), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32) -+func syscall_loadsystemlibrary(filename *uint16, absoluteFilepath *uint16) (handle, err uintptr) { -+ if useLoadLibraryEx { -+ handle, _, err = syscall_SyscallN(uintptr(unsafe.Pointer(_LoadLibraryExW)), uintptr(unsafe.Pointer(filename)), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32) -+ } else { -+ handle, _, err = syscall_SyscallN(uintptr(unsafe.Pointer(_LoadLibraryW)), uintptr(unsafe.Pointer(absoluteFilepath))) -+ } - KeepAlive(filename) -+ KeepAlive(absoluteFilepath) - if handle != 0 { - err = 0 - } -Index: src/syscall/dll_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/syscall/dll_windows.go b/src/syscall/dll_windows.go ---- a/src/syscall/dll_windows.go (revision 6a31d3fa8e47ddabc10bd97bff10d9a85f4cfb76) -+++ b/src/syscall/dll_windows.go (revision 69e2eed6dd0f6d815ebf15797761c13f31213dd6) -@@ -44,7 +44,7 @@ - - func SyscallN(trap uintptr, args ...uintptr) (r1, r2 uintptr, err Errno) - func loadlibrary(filename *uint16) (handle uintptr, err Errno) --func loadsystemlibrary(filename *uint16) (handle uintptr, err Errno) -+func loadsystemlibrary(filename *uint16, absoluteFilepath *uint16) (handle uintptr, err Errno) - func getprocaddress(handle uintptr, procname *uint8) (proc uintptr, err Errno) - - // A DLL implements access to a single DLL. -@@ -53,6 +53,9 @@ - Handle Handle - } - -+//go:linkname getSystemDirectory -+func getSystemDirectory() string // Implemented in runtime package. -+ - // LoadDLL loads the named DLL file into memory. - // - // If name is not an absolute path and is not a known system DLL used by -@@ -69,7 +72,11 @@ - var h uintptr - var e Errno - if sysdll.IsSystemDLL[name] { -- h, e = loadsystemlibrary(namep) -+ absoluteFilepathp, err := UTF16PtrFromString(getSystemDirectory() + name) -+ if err != nil { -+ return nil, err -+ } -+ h, e = loadsystemlibrary(namep, absoluteFilepathp) - } else { - h, e = loadlibrary(namep) - } diff --git a/clash-meta/.github/patch/go1.24.patch b/clash-meta/.github/patch/go1.24.patch deleted file mode 100644 index ecfc99ff73..0000000000 --- a/clash-meta/.github/patch/go1.24.patch +++ /dev/null @@ -1,657 +0,0 @@ -Subject: [PATCH] Revert "runtime: always use LoadLibraryEx to load system libraries" -Revert "syscall: remove Windows 7 console handle workaround" -Revert "net: remove sysSocket fallback for Windows 7" -Revert "crypto/rand,runtime: switch RtlGenRandom for ProcessPrng" ---- -Index: src/crypto/internal/sysrand/rand_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/crypto/internal/sysrand/rand_windows.go b/src/crypto/internal/sysrand/rand_windows.go ---- a/src/crypto/internal/sysrand/rand_windows.go (revision 3901409b5d0fb7c85a3e6730a59943cc93b2835c) -+++ b/src/crypto/internal/sysrand/rand_windows.go (revision 2a406dc9f1ea7323d6ca9fccb2fe9ddebb6b1cc8) -@@ -7,5 +7,26 @@ - import "internal/syscall/windows" - - func read(b []byte) error { -- return windows.ProcessPrng(b) -+ // 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. -+ return batched(windows.RtlGenRandom, 1<<31-1)(b) -+} -+ -+// batched returns a function that calls f to populate a []byte by chunking it -+// into subslices of, at most, readMax bytes. -+func batched(f func([]byte) error, readMax int) func([]byte) error { -+ return func(out []byte) error { -+ for len(out) > 0 { -+ read := len(out) -+ if read > readMax { -+ read = readMax -+ } -+ if err := f(out[:read]); err != nil { -+ return err -+ } -+ out = out[read:] -+ } -+ return nil -+ } - } -Index: src/crypto/rand/rand.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/crypto/rand/rand.go b/src/crypto/rand/rand.go ---- a/src/crypto/rand/rand.go (revision 3901409b5d0fb7c85a3e6730a59943cc93b2835c) -+++ b/src/crypto/rand/rand.go (revision 2a406dc9f1ea7323d6ca9fccb2fe9ddebb6b1cc8) -@@ -22,7 +22,7 @@ - // - On legacy Linux (< 3.17), Reader opens /dev/urandom on first use. - // - On macOS, iOS, and OpenBSD Reader, uses arc4random_buf(3). - // - On NetBSD, Reader uses the kern.arandom sysctl. --// - On Windows, Reader uses the ProcessPrng API. -+// - On Windows systems, Reader uses the RtlGenRandom API. - // - On js/wasm, Reader uses the Web Crypto API. - // - On wasip1/wasm, Reader uses random_get. - // -Index: src/internal/syscall/windows/syscall_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/internal/syscall/windows/syscall_windows.go b/src/internal/syscall/windows/syscall_windows.go ---- a/src/internal/syscall/windows/syscall_windows.go (revision 3901409b5d0fb7c85a3e6730a59943cc93b2835c) -+++ b/src/internal/syscall/windows/syscall_windows.go (revision 2a406dc9f1ea7323d6ca9fccb2fe9ddebb6b1cc8) -@@ -416,7 +416,7 @@ - //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 ProcessPrng(buf []byte) (err error) = bcryptprimitives.ProcessPrng -+//sys RtlGenRandom(buf []byte) (err error) = advapi32.SystemFunction036 - - type FILE_ID_BOTH_DIR_INFO struct { - NextEntryOffset uint32 -Index: src/internal/syscall/windows/zsyscall_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/internal/syscall/windows/zsyscall_windows.go b/src/internal/syscall/windows/zsyscall_windows.go ---- a/src/internal/syscall/windows/zsyscall_windows.go (revision 3901409b5d0fb7c85a3e6730a59943cc93b2835c) -+++ b/src/internal/syscall/windows/zsyscall_windows.go (revision 2a406dc9f1ea7323d6ca9fccb2fe9ddebb6b1cc8) -@@ -38,7 +38,6 @@ - - var ( - 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")) -@@ -63,7 +62,7 @@ - procQueryServiceStatus = modadvapi32.NewProc("QueryServiceStatus") - procRevertToSelf = modadvapi32.NewProc("RevertToSelf") - procSetTokenInformation = modadvapi32.NewProc("SetTokenInformation") -- procProcessPrng = modbcryptprimitives.NewProc("ProcessPrng") -+ procSystemFunction036 = modadvapi32.NewProc("SystemFunction036") - procGetAdaptersAddresses = modiphlpapi.NewProc("GetAdaptersAddresses") - procCreateEventW = modkernel32.NewProc("CreateEventW") - procGetACP = modkernel32.NewProc("GetACP") -@@ -236,12 +235,12 @@ - return - } - --func ProcessPrng(buf []byte) (err error) { -+func RtlGenRandom(buf []byte) (err error) { - var _p0 *byte - if len(buf) > 0 { - _p0 = &buf[0] - } -- r1, _, e1 := syscall.Syscall(procProcessPrng.Addr(), 2, uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), 0) -+ r1, _, e1 := syscall.Syscall(procSystemFunction036.Addr(), 2, uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), 0) - if r1 == 0 { - err = errnoErr(e1) - } -Index: src/runtime/os_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go ---- a/src/runtime/os_windows.go (revision 3901409b5d0fb7c85a3e6730a59943cc93b2835c) -+++ b/src/runtime/os_windows.go (revision ac3e93c061779dfefc0dd13a5b6e6f764a25621e) -@@ -40,8 +40,8 @@ - //go:cgo_import_dynamic runtime._GetSystemInfo GetSystemInfo%1 "kernel32.dll" - //go:cgo_import_dynamic runtime._GetThreadContext GetThreadContext%2 "kernel32.dll" - //go:cgo_import_dynamic runtime._SetThreadContext SetThreadContext%2 "kernel32.dll" --//go:cgo_import_dynamic runtime._LoadLibraryExW LoadLibraryExW%3 "kernel32.dll" - //go:cgo_import_dynamic runtime._LoadLibraryW LoadLibraryW%1 "kernel32.dll" -+//go:cgo_import_dynamic runtime._LoadLibraryA LoadLibraryA%1 "kernel32.dll" - //go:cgo_import_dynamic runtime._PostQueuedCompletionStatus PostQueuedCompletionStatus%4 "kernel32.dll" - //go:cgo_import_dynamic runtime._QueryPerformanceCounter QueryPerformanceCounter%1 "kernel32.dll" - //go:cgo_import_dynamic runtime._QueryPerformanceFrequency QueryPerformanceFrequency%1 "kernel32.dll" -@@ -75,7 +75,6 @@ - // Following syscalls are available on every Windows PC. - // All these variables are set by the Windows executable - // loader before the Go program starts. -- _AddVectoredContinueHandler, - _AddVectoredExceptionHandler, - _CloseHandle, - _CreateEventA, -@@ -98,8 +97,8 @@ - _GetSystemInfo, - _GetThreadContext, - _SetThreadContext, -- _LoadLibraryExW, - _LoadLibraryW, -+ _LoadLibraryA, - _PostQueuedCompletionStatus, - _QueryPerformanceCounter, - _QueryPerformanceFrequency, -@@ -128,8 +127,23 @@ - _WriteFile, - _ stdFunction - -- // Use ProcessPrng to generate cryptographically random data. -- _ProcessPrng stdFunction -+ // Following syscalls are only available on some Windows PCs. -+ // We will load syscalls, if available, before using them. -+ _AddDllDirectory, -+ _AddVectoredContinueHandler, -+ _LoadLibraryExA, -+ _LoadLibraryExW, -+ _ 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 - - // Load ntdll.dll manually during startup, otherwise Mingw - // links wrong printf function to cgo executable (see issue -@@ -146,13 +160,6 @@ - _ stdFunction - ) - --var ( -- 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} --) -- - // Function to be called by windows CreateThread - // to start new os thread. - func tstart_stdcall(newm *m) -@@ -245,8 +252,18 @@ - return unsafe.String(&sysDirectory[0], sysDirectoryLen) - } - --func windowsLoadSystemLib(name []uint16) uintptr { -- return stdcall3(_LoadLibraryExW, uintptr(unsafe.Pointer(&name[0])), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32) -+//go:linkname syscall_getSystemDirectory syscall.getSystemDirectory -+func syscall_getSystemDirectory() string { -+ return unsafe.String(&sysDirectory[0], sysDirectoryLen) -+} -+ -+func windowsLoadSystemLib(name []byte) uintptr { -+ if useLoadLibraryEx { -+ return stdcall3(_LoadLibraryExA, uintptr(unsafe.Pointer(&name[0])), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32) -+ } else { -+ absName := append(sysDirectory[:sysDirectoryLen], name...) -+ return stdcall1(_LoadLibraryA, uintptr(unsafe.Pointer(&absName[0]))) -+ } - } - - //go:linkname windows_QueryPerformanceCounter internal/syscall/windows.QueryPerformanceCounter -@@ -264,13 +281,28 @@ - } - - func loadOptionalSyscalls() { -- bcryptPrimitives := windowsLoadSystemLib(bcryptprimitivesdll[:]) -- if bcryptPrimitives == 0 { -- throw("bcryptprimitives.dll not found") -+ var kernel32dll = []byte("kernel32.dll\000") -+ k32 := stdcall1(_LoadLibraryA, uintptr(unsafe.Pointer(&kernel32dll[0]))) -+ if k32 == 0 { -+ throw("kernel32.dll not found") - } -- _ProcessPrng = windowsFindfunc(bcryptPrimitives, []byte("ProcessPrng\000")) -+ _AddDllDirectory = windowsFindfunc(k32, []byte("AddDllDirectory\000")) -+ _AddVectoredContinueHandler = windowsFindfunc(k32, []byte("AddVectoredContinueHandler\000")) -+ _LoadLibraryExA = windowsFindfunc(k32, []byte("LoadLibraryExA\000")) -+ _LoadLibraryExW = windowsFindfunc(k32, []byte("LoadLibraryExW\000")) -+ useLoadLibraryEx = (_LoadLibraryExW != nil && _LoadLibraryExA != nil && _AddDllDirectory != nil) -+ -+ initSysDirectory() - -- n32 := windowsLoadSystemLib(ntdlldll[:]) -+ var advapi32dll = []byte("advapi32.dll\000") -+ a32 := windowsLoadSystemLib(advapi32dll) -+ if a32 == 0 { -+ throw("advapi32.dll not found") -+ } -+ _RtlGenRandom = windowsFindfunc(a32, []byte("SystemFunction036\000")) -+ -+ var ntdll = []byte("ntdll.dll\000") -+ n32 := windowsLoadSystemLib(ntdll) - if n32 == 0 { - throw("ntdll.dll not found") - } -@@ -299,7 +331,7 @@ - context uintptr - } - -- powrprof := windowsLoadSystemLib(powrprofdll[:]) -+ powrprof := windowsLoadSystemLib([]byte("powrprof.dll\000")) - if powrprof == 0 { - return // Running on Windows 7, where we don't need it anyway. - } -@@ -358,6 +390,22 @@ - // in sys_windows_386.s and sys_windows_amd64.s: - func getlasterror() uint32 - -+// When loading DLLs, we prefer to use LoadLibraryEx with -+// LOAD_LIBRARY_SEARCH_* flags, if available. LoadLibraryEx is not -+// available on old Windows, though, and the LOAD_LIBRARY_SEARCH_* -+// flags are not available on some versions of Windows without a -+// security patch. -+// -+// https://msdn.microsoft.com/en-us/library/ms684179(v=vs.85).aspx says: -+// "Windows 7, Windows Server 2008 R2, Windows Vista, and Windows -+// Server 2008: The LOAD_LIBRARY_SEARCH_* flags are available on -+// systems that have KB2533623 installed. To determine whether the -+// flags are available, use GetProcAddress to get the address of the -+// AddDllDirectory, RemoveDllDirectory, or SetDefaultDllDirectories -+// function. If GetProcAddress succeeds, the LOAD_LIBRARY_SEARCH_* -+// flags can be used with LoadLibraryEx." -+var useLoadLibraryEx bool -+ - var timeBeginPeriodRetValue uint32 - - // osRelaxMinNS indicates that sysmon shouldn't osRelax if the next -@@ -431,7 +479,8 @@ - // Only load winmm.dll if we need it. - // This avoids a dependency on winmm.dll for Go programs - // that run on new Windows versions. -- m32 := windowsLoadSystemLib(winmmdll[:]) -+ var winmmdll = []byte("winmm.dll\000") -+ m32 := windowsLoadSystemLib(winmmdll) - if m32 == 0 { - print("runtime: LoadLibraryExW failed; errno=", getlasterror(), "\n") - throw("winmm.dll not found") -@@ -472,6 +521,28 @@ - canUseLongPaths = true - } - -+var osVersionInfo struct { -+ majorVersion uint32 -+ minorVersion uint32 -+ buildNumber uint32 -+} -+ -+func initOsVersionInfo() { -+ info := _OSVERSIONINFOW{} -+ info.osVersionInfoSize = uint32(unsafe.Sizeof(info)) -+ stdcall1(_RtlGetVersion, uintptr(unsafe.Pointer(&info))) -+ osVersionInfo.majorVersion = info.majorVersion -+ osVersionInfo.minorVersion = info.minorVersion -+ osVersionInfo.buildNumber = info.buildNumber -+} -+ -+//go:linkname rtlGetNtVersionNumbers syscall.rtlGetNtVersionNumbers -+func rtlGetNtVersionNumbers(majorVersion *uint32, minorVersion *uint32, buildNumber *uint32) { -+ *majorVersion = osVersionInfo.majorVersion -+ *minorVersion = osVersionInfo.minorVersion -+ *buildNumber = osVersionInfo.buildNumber -+} -+ - func osinit() { - asmstdcallAddr = unsafe.Pointer(abi.FuncPCABI0(asmstdcall)) - -@@ -484,8 +555,8 @@ - initHighResTimer() - timeBeginPeriodRetValue = osRelax(false) - -- initSysDirectory() - initLongPathSupport() -+ initOsVersionInfo() - - ncpu = getproccount() - -@@ -501,7 +572,7 @@ - //go:nosplit - func readRandom(r []byte) int { - n := 0 -- if stdcall2(_ProcessPrng, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 { -+ if stdcall2(_RtlGenRandom, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 { - n = len(r) - } - return n -Index: src/net/hook_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/hook_windows.go b/src/net/hook_windows.go ---- a/src/net/hook_windows.go (revision 2a406dc9f1ea7323d6ca9fccb2fe9ddebb6b1cc8) -+++ b/src/net/hook_windows.go (revision 7b1fd7d39c6be0185fbe1d929578ab372ac5c632) -@@ -13,6 +13,7 @@ - hostsFilePath = windows.GetSystemDirectory() + "/Drivers/etc/hosts" - - // 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 -Index: src/net/internal/socktest/main_test.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/internal/socktest/main_test.go b/src/net/internal/socktest/main_test.go ---- a/src/net/internal/socktest/main_test.go (revision 2a406dc9f1ea7323d6ca9fccb2fe9ddebb6b1cc8) -+++ b/src/net/internal/socktest/main_test.go (revision 7b1fd7d39c6be0185fbe1d929578ab372ac5c632) -@@ -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 && !windows -+//go:build !js && !plan9 && !wasip1 - - package socktest_test - -Index: src/net/internal/socktest/main_windows_test.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/internal/socktest/main_windows_test.go b/src/net/internal/socktest/main_windows_test.go -new file mode 100644 ---- /dev/null (revision 7b1fd7d39c6be0185fbe1d929578ab372ac5c632) -+++ b/src/net/internal/socktest/main_windows_test.go (revision 7b1fd7d39c6be0185fbe1d929578ab372ac5c632) -@@ -0,0 +1,22 @@ -+// 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 -+} -Index: src/net/internal/socktest/sys_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/internal/socktest/sys_windows.go b/src/net/internal/socktest/sys_windows.go ---- a/src/net/internal/socktest/sys_windows.go (revision 2a406dc9f1ea7323d6ca9fccb2fe9ddebb6b1cc8) -+++ b/src/net/internal/socktest/sys_windows.go (revision 7b1fd7d39c6be0185fbe1d929578ab372ac5c632) -@@ -9,6 +9,38 @@ - "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) -Index: src/net/main_windows_test.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/main_windows_test.go b/src/net/main_windows_test.go ---- a/src/net/main_windows_test.go (revision 2a406dc9f1ea7323d6ca9fccb2fe9ddebb6b1cc8) -+++ b/src/net/main_windows_test.go (revision 7b1fd7d39c6be0185fbe1d929578ab372ac5c632) -@@ -8,6 +8,7 @@ - - var ( - // Placeholders for saving original socket system calls. -+ origSocket = socketFunc - origWSASocket = wsaSocketFunc - origClosesocket = poll.CloseFunc - origConnect = connectFunc -@@ -17,6 +18,7 @@ - ) - - func installTestHooks() { -+ socketFunc = sw.Socket - wsaSocketFunc = sw.WSASocket - poll.CloseFunc = sw.Closesocket - connectFunc = sw.Connect -@@ -26,6 +28,7 @@ - } - - func uninstallTestHooks() { -+ socketFunc = origSocket - wsaSocketFunc = origWSASocket - poll.CloseFunc = origClosesocket - connectFunc = origConnect -Index: src/net/sock_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/sock_windows.go b/src/net/sock_windows.go ---- a/src/net/sock_windows.go (revision 2a406dc9f1ea7323d6ca9fccb2fe9ddebb6b1cc8) -+++ b/src/net/sock_windows.go (revision 7b1fd7d39c6be0185fbe1d929578ab372ac5c632) -@@ -20,6 +20,21 @@ - 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) - } -Index: src/syscall/exec_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/syscall/exec_windows.go b/src/syscall/exec_windows.go ---- a/src/syscall/exec_windows.go (revision 2a406dc9f1ea7323d6ca9fccb2fe9ddebb6b1cc8) -+++ b/src/syscall/exec_windows.go (revision 979d6d8bab3823ff572ace26767fd2ce3cf351ae) -@@ -14,7 +14,6 @@ - "unsafe" - ) - --// ForkLock is not used on Windows. - var ForkLock sync.RWMutex - - // EscapeArg rewrites command line argument s as prescribed -@@ -254,6 +253,9 @@ - var zeroProcAttr ProcAttr - var zeroSysProcAttr SysProcAttr - -+//go:linkname rtlGetNtVersionNumbers -+func rtlGetNtVersionNumbers(majorVersion *uint32, minorVersion *uint32, buildNumber *uint32) -+ - func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle uintptr, err error) { - if len(argv0) == 0 { - return 0, 0, EWINDOWS -@@ -317,6 +319,17 @@ - } - } - -+ 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 { -@@ -325,7 +338,15 @@ - fd := make([]Handle, len(attr.Files)) - for i := range attr.Files { - if attr.Files[i] > 0 { -- err := DuplicateHandle(p, Handle(attr.Files[i]), parentProcess, &fd[i], 0, true, DUPLICATE_SAME_ACCESS) -+ 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) - if err != nil { - return 0, 0, err - } -@@ -356,6 +377,14 @@ - - 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 -Index: src/runtime/syscall_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/runtime/syscall_windows.go b/src/runtime/syscall_windows.go ---- a/src/runtime/syscall_windows.go (revision 979d6d8bab3823ff572ace26767fd2ce3cf351ae) -+++ b/src/runtime/syscall_windows.go (revision ac3e93c061779dfefc0dd13a5b6e6f764a25621e) -@@ -413,10 +413,20 @@ - - const _LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800 - -+// When available, this function will use LoadLibraryEx with the filename -+// parameter and the important SEARCH_SYSTEM32 argument. But on systems that -+// do not have that option, absoluteFilepath should contain a fallback -+// to the full path inside of system32 for use with vanilla LoadLibrary. -+// - //go:linkname syscall_loadsystemlibrary syscall.loadsystemlibrary --func syscall_loadsystemlibrary(filename *uint16) (handle, err uintptr) { -- handle, _, err = syscall_SyscallN(uintptr(unsafe.Pointer(_LoadLibraryExW)), uintptr(unsafe.Pointer(filename)), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32) -+func syscall_loadsystemlibrary(filename *uint16, absoluteFilepath *uint16) (handle, err uintptr) { -+ if useLoadLibraryEx { -+ handle, _, err = syscall_SyscallN(uintptr(unsafe.Pointer(_LoadLibraryExW)), uintptr(unsafe.Pointer(filename)), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32) -+ } else { -+ handle, _, err = syscall_SyscallN(uintptr(unsafe.Pointer(_LoadLibraryW)), uintptr(unsafe.Pointer(absoluteFilepath))) -+ } - KeepAlive(filename) -+ KeepAlive(absoluteFilepath) - if handle != 0 { - err = 0 - } -Index: src/syscall/dll_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/syscall/dll_windows.go b/src/syscall/dll_windows.go ---- a/src/syscall/dll_windows.go (revision 979d6d8bab3823ff572ace26767fd2ce3cf351ae) -+++ b/src/syscall/dll_windows.go (revision ac3e93c061779dfefc0dd13a5b6e6f764a25621e) -@@ -45,7 +45,7 @@ - //go:noescape - func SyscallN(trap uintptr, args ...uintptr) (r1, r2 uintptr, err Errno) - func loadlibrary(filename *uint16) (handle uintptr, err Errno) --func loadsystemlibrary(filename *uint16) (handle uintptr, err Errno) -+func loadsystemlibrary(filename *uint16, absoluteFilepath *uint16) (handle uintptr, err Errno) - func getprocaddress(handle uintptr, procname *uint8) (proc uintptr, err Errno) - - // A DLL implements access to a single DLL. -@@ -54,6 +54,9 @@ - Handle Handle - } - -+//go:linkname getSystemDirectory -+func getSystemDirectory() string // Implemented in runtime package. -+ - // LoadDLL loads the named DLL file into memory. - // - // If name is not an absolute path and is not a known system DLL used by -@@ -70,7 +73,11 @@ - var h uintptr - var e Errno - if sysdll.IsSystemDLL[name] { -- h, e = loadsystemlibrary(namep) -+ absoluteFilepathp, err := UTF16PtrFromString(getSystemDirectory() + name) -+ if err != nil { -+ return nil, err -+ } -+ h, e = loadsystemlibrary(namep, absoluteFilepathp) - } else { - h, e = loadlibrary(namep) - } diff --git a/clash-meta/.github/patch/go1.25.patch b/clash-meta/.github/patch/go1.25.patch deleted file mode 100644 index 3e5ebed357..0000000000 --- a/clash-meta/.github/patch/go1.25.patch +++ /dev/null @@ -1,884 +0,0 @@ -Subject: [PATCH] Revert "os: remove 5ms sleep on Windows in (*Process).Wait" -Fix os.RemoveAll not working on Windows7 -Revert "runtime: always use LoadLibraryEx to load system libraries" -Revert "syscall: remove Windows 7 console handle workaround" -Revert "net: remove sysSocket fallback for Windows 7" -Revert "crypto/rand,runtime: switch RtlGenRandom for ProcessPrng" ---- -Index: src/crypto/internal/sysrand/rand_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/crypto/internal/sysrand/rand_windows.go b/src/crypto/internal/sysrand/rand_windows.go ---- a/src/crypto/internal/sysrand/rand_windows.go (revision 439ff996f0ee506fc2eb84b7f11ffc360a6299f2) -+++ b/src/crypto/internal/sysrand/rand_windows.go (revision 466f6c7a29bc098b0d4c987b803c779222894a11) -@@ -7,5 +7,26 @@ - import "internal/syscall/windows" - - func read(b []byte) error { -- return windows.ProcessPrng(b) -+ // 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. -+ return batched(windows.RtlGenRandom, 1<<31-1)(b) -+} -+ -+// batched returns a function that calls f to populate a []byte by chunking it -+// into subslices of, at most, readMax bytes. -+func batched(f func([]byte) error, readMax int) func([]byte) error { -+ return func(out []byte) error { -+ for len(out) > 0 { -+ read := len(out) -+ if read > readMax { -+ read = readMax -+ } -+ if err := f(out[:read]); err != nil { -+ return err -+ } -+ out = out[read:] -+ } -+ return nil -+ } - } -Index: src/crypto/rand/rand.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/crypto/rand/rand.go b/src/crypto/rand/rand.go ---- a/src/crypto/rand/rand.go (revision 439ff996f0ee506fc2eb84b7f11ffc360a6299f2) -+++ b/src/crypto/rand/rand.go (revision 466f6c7a29bc098b0d4c987b803c779222894a11) -@@ -22,7 +22,7 @@ - // - On legacy Linux (< 3.17), Reader opens /dev/urandom on first use. - // - On macOS, iOS, and OpenBSD Reader, uses arc4random_buf(3). - // - On NetBSD, Reader uses the kern.arandom sysctl. --// - On Windows, Reader uses the ProcessPrng API. -+// - On Windows systems, Reader uses the RtlGenRandom API. - // - On js/wasm, Reader uses the Web Crypto API. - // - On wasip1/wasm, Reader uses random_get. - // -Index: src/internal/syscall/windows/syscall_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/internal/syscall/windows/syscall_windows.go b/src/internal/syscall/windows/syscall_windows.go ---- a/src/internal/syscall/windows/syscall_windows.go (revision 439ff996f0ee506fc2eb84b7f11ffc360a6299f2) -+++ b/src/internal/syscall/windows/syscall_windows.go (revision 466f6c7a29bc098b0d4c987b803c779222894a11) -@@ -419,7 +419,7 @@ - //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 ProcessPrng(buf []byte) (err error) = bcryptprimitives.ProcessPrng -+//sys RtlGenRandom(buf []byte) (err error) = advapi32.SystemFunction036 - - type FILE_ID_BOTH_DIR_INFO struct { - NextEntryOffset uint32 -Index: src/internal/syscall/windows/zsyscall_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/internal/syscall/windows/zsyscall_windows.go b/src/internal/syscall/windows/zsyscall_windows.go ---- a/src/internal/syscall/windows/zsyscall_windows.go (revision 439ff996f0ee506fc2eb84b7f11ffc360a6299f2) -+++ b/src/internal/syscall/windows/zsyscall_windows.go (revision 466f6c7a29bc098b0d4c987b803c779222894a11) -@@ -38,7 +38,6 @@ - - var ( - 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")) -@@ -65,7 +64,7 @@ - procSetEntriesInAclW = modadvapi32.NewProc("SetEntriesInAclW") - procSetNamedSecurityInfoW = modadvapi32.NewProc("SetNamedSecurityInfoW") - procSetTokenInformation = modadvapi32.NewProc("SetTokenInformation") -- procProcessPrng = modbcryptprimitives.NewProc("ProcessPrng") -+ procSystemFunction036 = modadvapi32.NewProc("SystemFunction036") - procGetAdaptersAddresses = modiphlpapi.NewProc("GetAdaptersAddresses") - procCreateEventW = modkernel32.NewProc("CreateEventW") - procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort") -@@ -270,12 +269,12 @@ - return - } - --func ProcessPrng(buf []byte) (err error) { -+func RtlGenRandom(buf []byte) (err error) { - var _p0 *byte - if len(buf) > 0 { - _p0 = &buf[0] - } -- r1, _, e1 := syscall.SyscallN(procProcessPrng.Addr(), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf))) -+ r1, _, e1 := syscall.SyscallN(procSystemFunction036.Addr(), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf))) - if r1 == 0 { - err = errnoErr(e1) - } -Index: src/runtime/os_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go ---- a/src/runtime/os_windows.go (revision 439ff996f0ee506fc2eb84b7f11ffc360a6299f2) -+++ b/src/runtime/os_windows.go (revision f6bddda4e8ff58a957462a1a09562924d5f3d05c) -@@ -39,8 +39,8 @@ - //go:cgo_import_dynamic runtime._GetSystemInfo GetSystemInfo%1 "kernel32.dll" - //go:cgo_import_dynamic runtime._GetThreadContext GetThreadContext%2 "kernel32.dll" - //go:cgo_import_dynamic runtime._SetThreadContext SetThreadContext%2 "kernel32.dll" --//go:cgo_import_dynamic runtime._LoadLibraryExW LoadLibraryExW%3 "kernel32.dll" - //go:cgo_import_dynamic runtime._LoadLibraryW LoadLibraryW%1 "kernel32.dll" -+//go:cgo_import_dynamic runtime._LoadLibraryA LoadLibraryA%1 "kernel32.dll" - //go:cgo_import_dynamic runtime._PostQueuedCompletionStatus PostQueuedCompletionStatus%4 "kernel32.dll" - //go:cgo_import_dynamic runtime._QueryPerformanceCounter QueryPerformanceCounter%1 "kernel32.dll" - //go:cgo_import_dynamic runtime._QueryPerformanceFrequency QueryPerformanceFrequency%1 "kernel32.dll" -@@ -74,7 +74,6 @@ - // Following syscalls are available on every Windows PC. - // All these variables are set by the Windows executable - // loader before the Go program starts. -- _AddVectoredContinueHandler, - _AddVectoredExceptionHandler, - _CloseHandle, - _CreateEventA, -@@ -97,8 +96,8 @@ - _GetSystemInfo, - _GetThreadContext, - _SetThreadContext, -- _LoadLibraryExW, - _LoadLibraryW, -+ _LoadLibraryA, - _PostQueuedCompletionStatus, - _QueryPerformanceCounter, - _QueryPerformanceFrequency, -@@ -127,8 +126,23 @@ - _WriteFile, - _ stdFunction - -- // Use ProcessPrng to generate cryptographically random data. -- _ProcessPrng stdFunction -+ // Following syscalls are only available on some Windows PCs. -+ // We will load syscalls, if available, before using them. -+ _AddDllDirectory, -+ _AddVectoredContinueHandler, -+ _LoadLibraryExA, -+ _LoadLibraryExW, -+ _ 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 - - // Load ntdll.dll manually during startup, otherwise Mingw - // links wrong printf function to cgo executable (see issue -@@ -145,13 +159,6 @@ - _ stdFunction - ) - --var ( -- 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} --) -- - // Function to be called by windows CreateThread - // to start new os thread. - func tstart_stdcall(newm *m) -@@ -244,8 +251,18 @@ - return unsafe.String(&sysDirectory[0], sysDirectoryLen) - } - --func windowsLoadSystemLib(name []uint16) uintptr { -- return stdcall3(_LoadLibraryExW, uintptr(unsafe.Pointer(&name[0])), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32) -+//go:linkname syscall_getSystemDirectory syscall.getSystemDirectory -+func syscall_getSystemDirectory() string { -+ return unsafe.String(&sysDirectory[0], sysDirectoryLen) -+} -+ -+func windowsLoadSystemLib(name []byte) uintptr { -+ if useLoadLibraryEx { -+ return stdcall3(_LoadLibraryExA, uintptr(unsafe.Pointer(&name[0])), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32) -+ } else { -+ absName := append(sysDirectory[:sysDirectoryLen], name...) -+ return stdcall1(_LoadLibraryA, uintptr(unsafe.Pointer(&absName[0]))) -+ } - } - - //go:linkname windows_QueryPerformanceCounter internal/syscall/windows.QueryPerformanceCounter -@@ -263,13 +280,28 @@ - } - - func loadOptionalSyscalls() { -- bcryptPrimitives := windowsLoadSystemLib(bcryptprimitivesdll[:]) -- if bcryptPrimitives == 0 { -- throw("bcryptprimitives.dll not found") -+ var kernel32dll = []byte("kernel32.dll\000") -+ k32 := stdcall1(_LoadLibraryA, uintptr(unsafe.Pointer(&kernel32dll[0]))) -+ if k32 == 0 { -+ throw("kernel32.dll not found") - } -- _ProcessPrng = windowsFindfunc(bcryptPrimitives, []byte("ProcessPrng\000")) -+ _AddDllDirectory = windowsFindfunc(k32, []byte("AddDllDirectory\000")) -+ _AddVectoredContinueHandler = windowsFindfunc(k32, []byte("AddVectoredContinueHandler\000")) -+ _LoadLibraryExA = windowsFindfunc(k32, []byte("LoadLibraryExA\000")) -+ _LoadLibraryExW = windowsFindfunc(k32, []byte("LoadLibraryExW\000")) -+ useLoadLibraryEx = (_LoadLibraryExW != nil && _LoadLibraryExA != nil && _AddDllDirectory != nil) -+ -+ initSysDirectory() - -- n32 := windowsLoadSystemLib(ntdlldll[:]) -+ var advapi32dll = []byte("advapi32.dll\000") -+ a32 := windowsLoadSystemLib(advapi32dll) -+ if a32 == 0 { -+ throw("advapi32.dll not found") -+ } -+ _RtlGenRandom = windowsFindfunc(a32, []byte("SystemFunction036\000")) -+ -+ var ntdll = []byte("ntdll.dll\000") -+ n32 := windowsLoadSystemLib(ntdll) - if n32 == 0 { - throw("ntdll.dll not found") - } -@@ -298,7 +330,7 @@ - context uintptr - } - -- powrprof := windowsLoadSystemLib(powrprofdll[:]) -+ powrprof := windowsLoadSystemLib([]byte("powrprof.dll\000")) - if powrprof == 0 { - return // Running on Windows 7, where we don't need it anyway. - } -@@ -357,6 +389,22 @@ - // in sys_windows_386.s and sys_windows_amd64.s: - func getlasterror() uint32 - -+// When loading DLLs, we prefer to use LoadLibraryEx with -+// LOAD_LIBRARY_SEARCH_* flags, if available. LoadLibraryEx is not -+// available on old Windows, though, and the LOAD_LIBRARY_SEARCH_* -+// flags are not available on some versions of Windows without a -+// security patch. -+// -+// https://msdn.microsoft.com/en-us/library/ms684179(v=vs.85).aspx says: -+// "Windows 7, Windows Server 2008 R2, Windows Vista, and Windows -+// Server 2008: The LOAD_LIBRARY_SEARCH_* flags are available on -+// systems that have KB2533623 installed. To determine whether the -+// flags are available, use GetProcAddress to get the address of the -+// AddDllDirectory, RemoveDllDirectory, or SetDefaultDllDirectories -+// function. If GetProcAddress succeeds, the LOAD_LIBRARY_SEARCH_* -+// flags can be used with LoadLibraryEx." -+var useLoadLibraryEx bool -+ - var timeBeginPeriodRetValue uint32 - - // osRelaxMinNS indicates that sysmon shouldn't osRelax if the next -@@ -430,7 +478,8 @@ - // Only load winmm.dll if we need it. - // This avoids a dependency on winmm.dll for Go programs - // that run on new Windows versions. -- m32 := windowsLoadSystemLib(winmmdll[:]) -+ var winmmdll = []byte("winmm.dll\000") -+ m32 := windowsLoadSystemLib(winmmdll) - if m32 == 0 { - print("runtime: LoadLibraryExW failed; errno=", getlasterror(), "\n") - throw("winmm.dll not found") -@@ -471,6 +520,28 @@ - canUseLongPaths = true - } - -+var osVersionInfo struct { -+ majorVersion uint32 -+ minorVersion uint32 -+ buildNumber uint32 -+} -+ -+func initOsVersionInfo() { -+ info := _OSVERSIONINFOW{} -+ info.osVersionInfoSize = uint32(unsafe.Sizeof(info)) -+ stdcall1(_RtlGetVersion, uintptr(unsafe.Pointer(&info))) -+ osVersionInfo.majorVersion = info.majorVersion -+ osVersionInfo.minorVersion = info.minorVersion -+ osVersionInfo.buildNumber = info.buildNumber -+} -+ -+//go:linkname rtlGetNtVersionNumbers syscall.rtlGetNtVersionNumbers -+func rtlGetNtVersionNumbers(majorVersion *uint32, minorVersion *uint32, buildNumber *uint32) { -+ *majorVersion = osVersionInfo.majorVersion -+ *minorVersion = osVersionInfo.minorVersion -+ *buildNumber = osVersionInfo.buildNumber -+} -+ - func osinit() { - asmstdcallAddr = unsafe.Pointer(abi.FuncPCABI0(asmstdcall)) - -@@ -483,8 +554,8 @@ - initHighResTimer() - timeBeginPeriodRetValue = osRelax(false) - -- initSysDirectory() - initLongPathSupport() -+ initOsVersionInfo() - - numCPUStartup = getCPUCount() - -@@ -500,7 +571,7 @@ - //go:nosplit - func readRandom(r []byte) int { - n := 0 -- if stdcall2(_ProcessPrng, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 { -+ if stdcall2(_RtlGenRandom, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 { - n = len(r) - } - return n -Index: src/net/hook_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/hook_windows.go b/src/net/hook_windows.go ---- a/src/net/hook_windows.go (revision 466f6c7a29bc098b0d4c987b803c779222894a11) -+++ b/src/net/hook_windows.go (revision 1bdabae205052afe1dadb2ad6f1ba612cdbc532a) -@@ -13,6 +13,7 @@ - hostsFilePath = windows.GetSystemDirectory() + "/Drivers/etc/hosts" - - // 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 -Index: src/net/internal/socktest/main_test.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/internal/socktest/main_test.go b/src/net/internal/socktest/main_test.go ---- a/src/net/internal/socktest/main_test.go (revision 466f6c7a29bc098b0d4c987b803c779222894a11) -+++ b/src/net/internal/socktest/main_test.go (revision 1bdabae205052afe1dadb2ad6f1ba612cdbc532a) -@@ -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 && !windows -+//go:build !js && !plan9 && !wasip1 - - package socktest_test - -Index: src/net/internal/socktest/main_windows_test.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/internal/socktest/main_windows_test.go b/src/net/internal/socktest/main_windows_test.go -new file mode 100644 ---- /dev/null (revision 1bdabae205052afe1dadb2ad6f1ba612cdbc532a) -+++ b/src/net/internal/socktest/main_windows_test.go (revision 1bdabae205052afe1dadb2ad6f1ba612cdbc532a) -@@ -0,0 +1,22 @@ -+// 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 -+} -Index: src/net/internal/socktest/sys_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/internal/socktest/sys_windows.go b/src/net/internal/socktest/sys_windows.go ---- a/src/net/internal/socktest/sys_windows.go (revision 466f6c7a29bc098b0d4c987b803c779222894a11) -+++ b/src/net/internal/socktest/sys_windows.go (revision 1bdabae205052afe1dadb2ad6f1ba612cdbc532a) -@@ -9,6 +9,38 @@ - "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) -Index: src/net/main_windows_test.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/main_windows_test.go b/src/net/main_windows_test.go ---- a/src/net/main_windows_test.go (revision 466f6c7a29bc098b0d4c987b803c779222894a11) -+++ b/src/net/main_windows_test.go (revision 1bdabae205052afe1dadb2ad6f1ba612cdbc532a) -@@ -12,6 +12,7 @@ - - var ( - // Placeholders for saving original socket system calls. -+ origSocket = socketFunc - origWSASocket = wsaSocketFunc - origClosesocket = poll.CloseFunc - origConnect = connectFunc -@@ -21,6 +22,7 @@ - ) - - func installTestHooks() { -+ socketFunc = sw.Socket - wsaSocketFunc = sw.WSASocket - poll.CloseFunc = sw.Closesocket - connectFunc = sw.Connect -@@ -30,6 +32,7 @@ - } - - func uninstallTestHooks() { -+ socketFunc = origSocket - wsaSocketFunc = origWSASocket - poll.CloseFunc = origClosesocket - connectFunc = origConnect -Index: src/net/sock_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/sock_windows.go b/src/net/sock_windows.go ---- a/src/net/sock_windows.go (revision 466f6c7a29bc098b0d4c987b803c779222894a11) -+++ b/src/net/sock_windows.go (revision 1bdabae205052afe1dadb2ad6f1ba612cdbc532a) -@@ -20,6 +20,21 @@ - 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) - } -Index: src/syscall/exec_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/syscall/exec_windows.go b/src/syscall/exec_windows.go ---- a/src/syscall/exec_windows.go (revision 466f6c7a29bc098b0d4c987b803c779222894a11) -+++ b/src/syscall/exec_windows.go (revision a90777dcf692dd2168577853ba743b4338721b06) -@@ -14,7 +14,6 @@ - "unsafe" - ) - --// ForkLock is not used on Windows. - var ForkLock sync.RWMutex - - // EscapeArg rewrites command line argument s as prescribed -@@ -254,6 +253,9 @@ - var zeroProcAttr ProcAttr - var zeroSysProcAttr SysProcAttr - -+//go:linkname rtlGetNtVersionNumbers -+func rtlGetNtVersionNumbers(majorVersion *uint32, minorVersion *uint32, buildNumber *uint32) -+ - func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle uintptr, err error) { - if len(argv0) == 0 { - return 0, 0, EWINDOWS -@@ -317,6 +319,17 @@ - } - } - -+ 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 { -@@ -325,7 +338,15 @@ - fd := make([]Handle, len(attr.Files)) - for i := range attr.Files { - if attr.Files[i] > 0 { -- err := DuplicateHandle(p, Handle(attr.Files[i]), parentProcess, &fd[i], 0, true, DUPLICATE_SAME_ACCESS) -+ 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) - if err != nil { - return 0, 0, err - } -@@ -356,6 +377,14 @@ - - 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 -Index: src/runtime/syscall_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/runtime/syscall_windows.go b/src/runtime/syscall_windows.go ---- a/src/runtime/syscall_windows.go (revision a90777dcf692dd2168577853ba743b4338721b06) -+++ b/src/runtime/syscall_windows.go (revision f6bddda4e8ff58a957462a1a09562924d5f3d05c) -@@ -413,10 +413,20 @@ - - const _LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800 - -+// When available, this function will use LoadLibraryEx with the filename -+// parameter and the important SEARCH_SYSTEM32 argument. But on systems that -+// do not have that option, absoluteFilepath should contain a fallback -+// to the full path inside of system32 for use with vanilla LoadLibrary. -+// - //go:linkname syscall_loadsystemlibrary syscall.loadsystemlibrary --func syscall_loadsystemlibrary(filename *uint16) (handle, err uintptr) { -- handle, _, err = syscall_SyscallN(uintptr(unsafe.Pointer(_LoadLibraryExW)), uintptr(unsafe.Pointer(filename)), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32) -+func syscall_loadsystemlibrary(filename *uint16, absoluteFilepath *uint16) (handle, err uintptr) { -+ if useLoadLibraryEx { -+ handle, _, err = syscall_SyscallN(uintptr(unsafe.Pointer(_LoadLibraryExW)), uintptr(unsafe.Pointer(filename)), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32) -+ } else { -+ handle, _, err = syscall_SyscallN(uintptr(unsafe.Pointer(_LoadLibraryW)), uintptr(unsafe.Pointer(absoluteFilepath))) -+ } - KeepAlive(filename) -+ KeepAlive(absoluteFilepath) - if handle != 0 { - err = 0 - } -Index: src/syscall/dll_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/syscall/dll_windows.go b/src/syscall/dll_windows.go ---- a/src/syscall/dll_windows.go (revision a90777dcf692dd2168577853ba743b4338721b06) -+++ b/src/syscall/dll_windows.go (revision f6bddda4e8ff58a957462a1a09562924d5f3d05c) -@@ -45,7 +45,7 @@ - //go:noescape - func SyscallN(trap uintptr, args ...uintptr) (r1, r2 uintptr, err Errno) - func loadlibrary(filename *uint16) (handle uintptr, err Errno) --func loadsystemlibrary(filename *uint16) (handle uintptr, err Errno) -+func loadsystemlibrary(filename *uint16, absoluteFilepath *uint16) (handle uintptr, err Errno) - func getprocaddress(handle uintptr, procname *uint8) (proc uintptr, err Errno) - - // A DLL implements access to a single DLL. -@@ -54,6 +54,9 @@ - Handle Handle - } - -+//go:linkname getSystemDirectory -+func getSystemDirectory() string // Implemented in runtime package. -+ - // LoadDLL loads the named DLL file into memory. - // - // If name is not an absolute path and is not a known system DLL used by -@@ -70,7 +73,11 @@ - var h uintptr - var e Errno - if sysdll.IsSystemDLL[name] { -- h, e = loadsystemlibrary(namep) -+ absoluteFilepathp, err := UTF16PtrFromString(getSystemDirectory() + name) -+ if err != nil { -+ return nil, err -+ } -+ h, e = loadsystemlibrary(namep, absoluteFilepathp) - } else { - h, e = loadlibrary(namep) - } -Index: src/os/removeall_at.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/os/removeall_at.go b/src/os/removeall_at.go ---- a/src/os/removeall_at.go (revision f6bddda4e8ff58a957462a1a09562924d5f3d05c) -+++ b/src/os/removeall_at.go (revision bed309eff415bcb3c77dd4bc3277b682b89a388d) -@@ -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 unix || wasip1 || windows -+//go:build unix || wasip1 - - package os - -@@ -175,3 +175,25 @@ - } - return newDirFile(fd, name) - } -+ -+func rootRemoveAll(r *Root, name string) error { -+ // Consistency with os.RemoveAll: Strip trailing /s from the name, -+ // so RemoveAll("not_a_directory/") succeeds. -+ for len(name) > 0 && IsPathSeparator(name[len(name)-1]) { -+ name = name[:len(name)-1] -+ } -+ if endsWithDot(name) { -+ // Consistency with os.RemoveAll: Return EINVAL when trying to remove . -+ return &PathError{Op: "RemoveAll", Path: name, Err: syscall.EINVAL} -+ } -+ _, err := doInRoot(r, name, nil, func(parent sysfdType, name string) (struct{}, error) { -+ return struct{}{}, removeAllFrom(parent, name) -+ }) -+ if IsNotExist(err) { -+ return nil -+ } -+ if err != nil { -+ return &PathError{Op: "RemoveAll", Path: name, Err: underlyingError(err)} -+ } -+ return err -+} -Index: src/os/removeall_noat.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/os/removeall_noat.go b/src/os/removeall_noat.go ---- a/src/os/removeall_noat.go (revision f6bddda4e8ff58a957462a1a09562924d5f3d05c) -+++ b/src/os/removeall_noat.go (revision bed309eff415bcb3c77dd4bc3277b682b89a388d) -@@ -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 && wasm) || plan9 -+//go:build (js && wasm) || plan9 || windows - - package os - -@@ -140,3 +140,22 @@ - } - return err - } -+ -+func rootRemoveAll(r *Root, name string) error { -+ if endsWithDot(name) { -+ // Consistency with os.RemoveAll: Return EINVAL when trying to remove . -+ return &PathError{Op: "RemoveAll", Path: name, Err: syscall.EINVAL} -+ } -+ if err := checkPathEscapesLstat(r, name); err != nil { -+ if err == syscall.ENOTDIR { -+ // Some intermediate path component is not a directory. -+ // RemoveAll treats this as success (since the target doesn't exist). -+ return nil -+ } -+ return &PathError{Op: "RemoveAll", Path: name, Err: err} -+ } -+ if err := RemoveAll(joinPath(r.root.name, name)); err != nil { -+ return &PathError{Op: "RemoveAll", Path: name, Err: underlyingError(err)} -+ } -+ return nil -+} -Index: src/os/root_noopenat.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/os/root_noopenat.go b/src/os/root_noopenat.go ---- a/src/os/root_noopenat.go (revision f6bddda4e8ff58a957462a1a09562924d5f3d05c) -+++ b/src/os/root_noopenat.go (revision bed309eff415bcb3c77dd4bc3277b682b89a388d) -@@ -11,7 +11,6 @@ - "internal/filepathlite" - "internal/stringslite" - "sync/atomic" -- "syscall" - "time" - ) - -@@ -185,25 +184,6 @@ - } - return nil - } -- --func rootRemoveAll(r *Root, name string) error { -- if endsWithDot(name) { -- // Consistency with os.RemoveAll: Return EINVAL when trying to remove . -- return &PathError{Op: "RemoveAll", Path: name, Err: syscall.EINVAL} -- } -- if err := checkPathEscapesLstat(r, name); err != nil { -- if err == syscall.ENOTDIR { -- // Some intermediate path component is not a directory. -- // RemoveAll treats this as success (since the target doesn't exist). -- return nil -- } -- return &PathError{Op: "RemoveAll", Path: name, Err: err} -- } -- if err := RemoveAll(joinPath(r.root.name, name)); err != nil { -- return &PathError{Op: "RemoveAll", Path: name, Err: underlyingError(err)} -- } -- return nil --} - - func rootReadlink(r *Root, name string) (string, error) { - if err := checkPathEscapesLstat(r, name); err != nil { -Index: src/os/root_openat.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/os/root_openat.go b/src/os/root_openat.go ---- a/src/os/root_openat.go (revision f6bddda4e8ff58a957462a1a09562924d5f3d05c) -+++ b/src/os/root_openat.go (revision bed309eff415bcb3c77dd4bc3277b682b89a388d) -@@ -196,28 +196,6 @@ - return nil - } - --func rootRemoveAll(r *Root, name string) error { -- // Consistency with os.RemoveAll: Strip trailing /s from the name, -- // so RemoveAll("not_a_directory/") succeeds. -- for len(name) > 0 && IsPathSeparator(name[len(name)-1]) { -- name = name[:len(name)-1] -- } -- if endsWithDot(name) { -- // Consistency with os.RemoveAll: Return EINVAL when trying to remove . -- return &PathError{Op: "RemoveAll", Path: name, Err: syscall.EINVAL} -- } -- _, err := doInRoot(r, name, nil, func(parent sysfdType, name string) (struct{}, error) { -- return struct{}{}, removeAllFrom(parent, name) -- }) -- if IsNotExist(err) { -- return nil -- } -- if err != nil { -- return &PathError{Op: "RemoveAll", Path: name, Err: underlyingError(err)} -- } -- return err --} -- - func rootRename(r *Root, oldname, newname string) error { - _, err := doInRoot(r, oldname, nil, func(oldparent sysfdType, oldname string) (struct{}, error) { - _, err := doInRoot(r, newname, nil, func(newparent sysfdType, newname string) (struct{}, error) { -Index: src/os/root_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/os/root_windows.go b/src/os/root_windows.go ---- a/src/os/root_windows.go (revision f6bddda4e8ff58a957462a1a09562924d5f3d05c) -+++ b/src/os/root_windows.go (revision bed309eff415bcb3c77dd4bc3277b682b89a388d) -@@ -402,3 +402,14 @@ - } - return fi.Mode(), nil - } -+ -+func checkPathEscapes(r *Root, name string) error { -+ if !filepathlite.IsLocal(name) { -+ return errPathEscapes -+ } -+ return nil -+} -+ -+func checkPathEscapesLstat(r *Root, name string) error { -+ return checkPathEscapes(r, name) -+} -Index: src/os/exec_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/os/exec_windows.go b/src/os/exec_windows.go ---- a/src/os/exec_windows.go (revision bed309eff415bcb3c77dd4bc3277b682b89a388d) -+++ b/src/os/exec_windows.go (revision 34b899c2fb39b092db4fa67c4417e41dc046be4b) -@@ -10,6 +10,7 @@ - "runtime" - "syscall" - "time" -+ _ "unsafe" - ) - - // Note that Process.handle is never nil because Windows always requires -@@ -49,9 +50,23 @@ - // than statusDone. - p.doRelease(statusReleased) - -+ var maj, min, build uint32 -+ rtlGetNtVersionNumbers(&maj, &min, &build) -+ if maj < 10 { -+ // NOTE(brainman): It seems that sometimes process is not dead -+ // when WaitForSingleObject returns. But we do not know any -+ // other way to wait for it. Sleeping for a while seems to do -+ // the trick sometimes. -+ // See https://golang.org/issue/25965 for details. -+ time.Sleep(5 * time.Millisecond) -+ } -+ - return &ProcessState{p.Pid, syscall.WaitStatus{ExitCode: ec}, &u}, nil - } - -+//go:linkname rtlGetNtVersionNumbers syscall.rtlGetNtVersionNumbers -+func rtlGetNtVersionNumbers(majorVersion *uint32, minorVersion *uint32, buildNumber *uint32) -+ - func (p *Process) signal(sig Signal) error { - handle, status := p.handleTransientAcquire() - switch status { diff --git a/clash-meta/.github/patch/go1.26.patch b/clash-meta/.github/patch/go1.26.patch deleted file mode 100644 index c2400e797f..0000000000 --- a/clash-meta/.github/patch/go1.26.patch +++ /dev/null @@ -1,883 +0,0 @@ -Subject: [PATCH] Revert "os: remove 5ms sleep on Windows in (*Process).Wait" -Fix os.RemoveAll not working on Windows7 -Revert "runtime: always use LoadLibraryEx to load system libraries" -Revert "syscall: remove Windows 7 console handle workaround" -Revert "net: remove sysSocket fallback for Windows 7" -Revert "crypto/rand,runtime: switch RtlGenRandom for ProcessPrng" ---- -Index: src/crypto/internal/sysrand/rand_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/crypto/internal/sysrand/rand_windows.go b/src/crypto/internal/sysrand/rand_windows.go ---- a/src/crypto/internal/sysrand/rand_windows.go (revision e87b10ea2a2c6c65b80c4374af42b9c02ac9fb20) -+++ b/src/crypto/internal/sysrand/rand_windows.go (revision 4b29590aa510e05686ea53de16e1e571d22203d8) -@@ -7,5 +7,26 @@ - import "internal/syscall/windows" - - func read(b []byte) error { -- return windows.ProcessPrng(b) -+ // 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. -+ return batched(windows.RtlGenRandom, 1<<31-1)(b) -+} -+ -+// batched returns a function that calls f to populate a []byte by chunking it -+// into subslices of, at most, readMax bytes. -+func batched(f func([]byte) error, readMax int) func([]byte) error { -+ return func(out []byte) error { -+ for len(out) > 0 { -+ read := len(out) -+ if read > readMax { -+ read = readMax -+ } -+ if err := f(out[:read]); err != nil { -+ return err -+ } -+ out = out[read:] -+ } -+ return nil -+ } - } -Index: src/crypto/rand/rand.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/crypto/rand/rand.go b/src/crypto/rand/rand.go ---- a/src/crypto/rand/rand.go (revision e87b10ea2a2c6c65b80c4374af42b9c02ac9fb20) -+++ b/src/crypto/rand/rand.go (revision 4b29590aa510e05686ea53de16e1e571d22203d8) -@@ -25,7 +25,7 @@ - // - On legacy Linux (< 3.17), Reader opens /dev/urandom on first use. - // - On macOS, iOS, and OpenBSD Reader, uses arc4random_buf(3). - // - On NetBSD, Reader uses the kern.arandom sysctl. --// - On Windows, Reader uses the ProcessPrng API. -+// - On Windows systems, Reader uses the RtlGenRandom API. - // - On js/wasm, Reader uses the Web Crypto API. - // - On wasip1/wasm, Reader uses random_get. - // -Index: src/internal/syscall/windows/syscall_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/internal/syscall/windows/syscall_windows.go b/src/internal/syscall/windows/syscall_windows.go ---- a/src/internal/syscall/windows/syscall_windows.go (revision e87b10ea2a2c6c65b80c4374af42b9c02ac9fb20) -+++ b/src/internal/syscall/windows/syscall_windows.go (revision 4b29590aa510e05686ea53de16e1e571d22203d8) -@@ -421,7 +421,7 @@ - //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 ProcessPrng(buf []byte) (err error) = bcryptprimitives.ProcessPrng -+//sys RtlGenRandom(buf []byte) (err error) = advapi32.SystemFunction036 - - type FILE_ID_BOTH_DIR_INFO struct { - NextEntryOffset uint32 -Index: src/internal/syscall/windows/zsyscall_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/internal/syscall/windows/zsyscall_windows.go b/src/internal/syscall/windows/zsyscall_windows.go ---- a/src/internal/syscall/windows/zsyscall_windows.go (revision e87b10ea2a2c6c65b80c4374af42b9c02ac9fb20) -+++ b/src/internal/syscall/windows/zsyscall_windows.go (revision 4b29590aa510e05686ea53de16e1e571d22203d8) -@@ -38,7 +38,6 @@ - - var ( - 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")) -@@ -65,7 +64,7 @@ - procSetEntriesInAclW = modadvapi32.NewProc("SetEntriesInAclW") - procSetNamedSecurityInfoW = modadvapi32.NewProc("SetNamedSecurityInfoW") - procSetTokenInformation = modadvapi32.NewProc("SetTokenInformation") -- procProcessPrng = modbcryptprimitives.NewProc("ProcessPrng") -+ procSystemFunction036 = modadvapi32.NewProc("SystemFunction036") - procGetAdaptersAddresses = modiphlpapi.NewProc("GetAdaptersAddresses") - procCreateEventW = modkernel32.NewProc("CreateEventW") - procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort") -@@ -271,12 +270,12 @@ - return - } - --func ProcessPrng(buf []byte) (err error) { -+func RtlGenRandom(buf []byte) (err error) { - var _p0 *byte - if len(buf) > 0 { - _p0 = &buf[0] - } -- r1, _, e1 := syscall.SyscallN(procProcessPrng.Addr(), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf))) -+ r1, _, e1 := syscall.SyscallN(procSystemFunction036.Addr(), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf))) - if r1 == 0 { - err = errnoErr(e1) - } -Index: src/runtime/os_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go ---- a/src/runtime/os_windows.go (revision e87b10ea2a2c6c65b80c4374af42b9c02ac9fb20) -+++ b/src/runtime/os_windows.go (revision ce2e1a3d2c3c0d7277b4102841db1697147d2923) -@@ -40,7 +40,8 @@ - //go:cgo_import_dynamic runtime._GetSystemInfo GetSystemInfo%1 "kernel32.dll" - //go:cgo_import_dynamic runtime._GetThreadContext GetThreadContext%2 "kernel32.dll" - //go:cgo_import_dynamic runtime._SetThreadContext SetThreadContext%2 "kernel32.dll" --//go:cgo_import_dynamic runtime._LoadLibraryExW LoadLibraryExW%3 "kernel32.dll" -+//go:cgo_import_dynamic runtime._LoadLibraryW LoadLibraryW%1 "kernel32.dll" -+//go:cgo_import_dynamic runtime._LoadLibraryA LoadLibraryA%1 "kernel32.dll" - //go:cgo_import_dynamic runtime._PostQueuedCompletionStatus PostQueuedCompletionStatus%4 "kernel32.dll" - //go:cgo_import_dynamic runtime._QueryPerformanceCounter QueryPerformanceCounter%1 "kernel32.dll" - //go:cgo_import_dynamic runtime._QueryPerformanceFrequency QueryPerformanceFrequency%1 "kernel32.dll" -@@ -74,7 +75,6 @@ - // Following syscalls are available on every Windows PC. - // All these variables are set by the Windows executable - // loader before the Go program starts. -- _AddVectoredContinueHandler, - _AddVectoredExceptionHandler, - _CloseHandle, - _CreateEventA, -@@ -97,7 +97,8 @@ - _GetSystemInfo, - _GetThreadContext, - _SetThreadContext, -- _LoadLibraryExW, -+ _LoadLibraryW, -+ _LoadLibraryA, - _PostQueuedCompletionStatus, - _QueryPerformanceCounter, - _QueryPerformanceFrequency, -@@ -126,8 +127,23 @@ - _WriteFile, - _ stdFunction - -- // Use ProcessPrng to generate cryptographically random data. -- _ProcessPrng stdFunction -+ // Following syscalls are only available on some Windows PCs. -+ // We will load syscalls, if available, before using them. -+ _AddDllDirectory, -+ _AddVectoredContinueHandler, -+ _LoadLibraryExA, -+ _LoadLibraryExW, -+ _ 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 - - // Load ntdll.dll manually during startup, otherwise Mingw - // links wrong printf function to cgo executable (see issue -@@ -144,13 +160,6 @@ - _ stdFunction - ) - --var ( -- 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} --) -- - // Function to be called by windows CreateThread - // to start new os thread. - func tstart_stdcall(newm *m) -@@ -242,9 +251,40 @@ - return unsafe.String(&sysDirectory[0], sysDirectoryLen) - } - --func windowsLoadSystemLib(name []uint16) uintptr { -- const _LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800 -- return stdcall(_LoadLibraryExW, uintptr(unsafe.Pointer(&name[0])), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32) -+//go:linkname syscall_getSystemDirectory syscall.getSystemDirectory -+func syscall_getSystemDirectory() string { -+ return unsafe.String(&sysDirectory[0], sysDirectoryLen) -+} -+ -+func windowsLoadSystemLib(name []byte) uintptr { -+ if useLoadLibraryEx { -+ return stdcall(_LoadLibraryExA, uintptr(unsafe.Pointer(&name[0])), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32) -+ } else { -+ absName := append(sysDirectory[:sysDirectoryLen], name...) -+ return stdcall(_LoadLibraryA, uintptr(unsafe.Pointer(&absName[0]))) -+ } -+} -+ -+const _LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800 -+ -+// When available, this function will use LoadLibraryEx with the filename -+// parameter and the important SEARCH_SYSTEM32 argument. But on systems that -+// do not have that option, absoluteFilepath should contain a fallback -+// to the full path inside of system32 for use with vanilla LoadLibrary. -+// -+//go:linkname syscall_loadsystemlibrary syscall.loadsystemlibrary -+func syscall_loadsystemlibrary(filename *uint16, absoluteFilepath *uint16) (handle, err uintptr) { -+ if useLoadLibraryEx { -+ handle, _, err = syscall_syscalln(uintptr(unsafe.Pointer(_LoadLibraryExW)), 3, uintptr(unsafe.Pointer(filename)), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32) -+ } else { -+ handle, _, err = syscall_syscalln(uintptr(unsafe.Pointer(_LoadLibraryW)), 1, uintptr(unsafe.Pointer(absoluteFilepath))) -+ } -+ KeepAlive(filename) -+ KeepAlive(absoluteFilepath) -+ if handle != 0 { -+ err = 0 -+ } -+ return - } - - //go:linkname windows_QueryPerformanceCounter internal/syscall/windows.QueryPerformanceCounter -@@ -262,13 +302,28 @@ - } - - func loadOptionalSyscalls() { -- bcryptPrimitives := windowsLoadSystemLib(bcryptprimitivesdll[:]) -- if bcryptPrimitives == 0 { -- throw("bcryptprimitives.dll not found") -+ var kernel32dll = []byte("kernel32.dll\000") -+ k32 := stdcall(_LoadLibraryA, uintptr(unsafe.Pointer(&kernel32dll[0]))) -+ if k32 == 0 { -+ throw("kernel32.dll not found") - } -- _ProcessPrng = windowsFindfunc(bcryptPrimitives, []byte("ProcessPrng\000")) -+ _AddDllDirectory = windowsFindfunc(k32, []byte("AddDllDirectory\000")) -+ _AddVectoredContinueHandler = windowsFindfunc(k32, []byte("AddVectoredContinueHandler\000")) -+ _LoadLibraryExA = windowsFindfunc(k32, []byte("LoadLibraryExA\000")) -+ _LoadLibraryExW = windowsFindfunc(k32, []byte("LoadLibraryExW\000")) -+ useLoadLibraryEx = (_LoadLibraryExW != nil && _LoadLibraryExA != nil && _AddDllDirectory != nil) -+ -+ initSysDirectory() - -- n32 := windowsLoadSystemLib(ntdlldll[:]) -+ var advapi32dll = []byte("advapi32.dll\000") -+ a32 := windowsLoadSystemLib(advapi32dll) -+ if a32 == 0 { -+ throw("advapi32.dll not found") -+ } -+ _RtlGenRandom = windowsFindfunc(a32, []byte("SystemFunction036\000")) -+ -+ var ntdll = []byte("ntdll.dll\000") -+ n32 := windowsLoadSystemLib(ntdll) - if n32 == 0 { - throw("ntdll.dll not found") - } -@@ -297,7 +352,7 @@ - context uintptr - } - -- powrprof := windowsLoadSystemLib(powrprofdll[:]) -+ powrprof := windowsLoadSystemLib([]byte("powrprof.dll\000")) - if powrprof == 0 { - return // Running on Windows 7, where we don't need it anyway. - } -@@ -351,6 +406,22 @@ - // in sys_windows_386.s and sys_windows_amd64.s: - func getlasterror() uint32 - -+// When loading DLLs, we prefer to use LoadLibraryEx with -+// LOAD_LIBRARY_SEARCH_* flags, if available. LoadLibraryEx is not -+// available on old Windows, though, and the LOAD_LIBRARY_SEARCH_* -+// flags are not available on some versions of Windows without a -+// security patch. -+// -+// https://msdn.microsoft.com/en-us/library/ms684179(v=vs.85).aspx says: -+// "Windows 7, Windows Server 2008 R2, Windows Vista, and Windows -+// Server 2008: The LOAD_LIBRARY_SEARCH_* flags are available on -+// systems that have KB2533623 installed. To determine whether the -+// flags are available, use GetProcAddress to get the address of the -+// AddDllDirectory, RemoveDllDirectory, or SetDefaultDllDirectories -+// function. If GetProcAddress succeeds, the LOAD_LIBRARY_SEARCH_* -+// flags can be used with LoadLibraryEx." -+var useLoadLibraryEx bool -+ - var timeBeginPeriodRetValue uint32 - - // osRelaxMinNS indicates that sysmon shouldn't osRelax if the next -@@ -417,7 +488,8 @@ - // Only load winmm.dll if we need it. - // This avoids a dependency on winmm.dll for Go programs - // that run on new Windows versions. -- m32 := windowsLoadSystemLib(winmmdll[:]) -+ var winmmdll = []byte("winmm.dll\000") -+ m32 := windowsLoadSystemLib(winmmdll) - if m32 == 0 { - print("runtime: LoadLibraryExW failed; errno=", getlasterror(), "\n") - throw("winmm.dll not found") -@@ -458,6 +530,28 @@ - canUseLongPaths = true - } - -+var osVersionInfo struct { -+ majorVersion uint32 -+ minorVersion uint32 -+ buildNumber uint32 -+} -+ -+func initOsVersionInfo() { -+ info := windows.OSVERSIONINFOW{} -+ info.OSVersionInfoSize = uint32(unsafe.Sizeof(info)) -+ stdcall(_RtlGetVersion, uintptr(unsafe.Pointer(&info))) -+ osVersionInfo.majorVersion = info.MajorVersion -+ osVersionInfo.minorVersion = info.MinorVersion -+ osVersionInfo.buildNumber = info.BuildNumber -+} -+ -+//go:linkname rtlGetNtVersionNumbers syscall.rtlGetNtVersionNumbers -+func rtlGetNtVersionNumbers(majorVersion *uint32, minorVersion *uint32, buildNumber *uint32) { -+ *majorVersion = osVersionInfo.majorVersion -+ *minorVersion = osVersionInfo.minorVersion -+ *buildNumber = osVersionInfo.buildNumber -+} -+ - func osinit() { - asmstdcallAddr = unsafe.Pointer(windows.AsmStdCallAddr()) - -@@ -470,8 +564,8 @@ - initHighResTimer() - timeBeginPeriodRetValue = osRelax(false) - -- initSysDirectory() - initLongPathSupport() -+ initOsVersionInfo() - - numCPUStartup = getCPUCount() - -@@ -487,7 +581,7 @@ - //go:nosplit - func readRandom(r []byte) int { - n := 0 -- if stdcall(_ProcessPrng, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 { -+ if stdcall(_RtlGenRandom, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 { - n = len(r) - } - return n -Index: src/net/hook_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/hook_windows.go b/src/net/hook_windows.go ---- a/src/net/hook_windows.go (revision 4b29590aa510e05686ea53de16e1e571d22203d8) -+++ b/src/net/hook_windows.go (revision 2263b05b2fa6ce228fde1899587baf109f1e2e0a) -@@ -13,6 +13,7 @@ - hostsFilePath = windows.GetSystemDirectory() + "/Drivers/etc/hosts" - - // 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 -Index: src/net/internal/socktest/main_test.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/internal/socktest/main_test.go b/src/net/internal/socktest/main_test.go ---- a/src/net/internal/socktest/main_test.go (revision 4b29590aa510e05686ea53de16e1e571d22203d8) -+++ b/src/net/internal/socktest/main_test.go (revision 2263b05b2fa6ce228fde1899587baf109f1e2e0a) -@@ -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 && !windows -+//go:build !js && !plan9 && !wasip1 - - package socktest_test - -Index: src/net/internal/socktest/main_windows_test.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/internal/socktest/main_windows_test.go b/src/net/internal/socktest/main_windows_test.go -new file mode 100644 ---- /dev/null (revision 2263b05b2fa6ce228fde1899587baf109f1e2e0a) -+++ b/src/net/internal/socktest/main_windows_test.go (revision 2263b05b2fa6ce228fde1899587baf109f1e2e0a) -@@ -0,0 +1,22 @@ -+// 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 -+} -Index: src/net/internal/socktest/sys_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/internal/socktest/sys_windows.go b/src/net/internal/socktest/sys_windows.go ---- a/src/net/internal/socktest/sys_windows.go (revision 4b29590aa510e05686ea53de16e1e571d22203d8) -+++ b/src/net/internal/socktest/sys_windows.go (revision 2263b05b2fa6ce228fde1899587baf109f1e2e0a) -@@ -9,6 +9,38 @@ - "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) -Index: src/net/main_windows_test.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/main_windows_test.go b/src/net/main_windows_test.go ---- a/src/net/main_windows_test.go (revision 4b29590aa510e05686ea53de16e1e571d22203d8) -+++ b/src/net/main_windows_test.go (revision 2263b05b2fa6ce228fde1899587baf109f1e2e0a) -@@ -12,6 +12,7 @@ - - var ( - // Placeholders for saving original socket system calls. -+ origSocket = socketFunc - origWSASocket = wsaSocketFunc - origClosesocket = poll.CloseFunc - origConnect = connectFunc -@@ -21,6 +22,7 @@ - ) - - func installTestHooks() { -+ socketFunc = sw.Socket - wsaSocketFunc = sw.WSASocket - poll.CloseFunc = sw.Closesocket - connectFunc = sw.Connect -@@ -30,6 +32,7 @@ - } - - func uninstallTestHooks() { -+ socketFunc = origSocket - wsaSocketFunc = origWSASocket - poll.CloseFunc = origClosesocket - connectFunc = origConnect -Index: src/net/sock_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/sock_windows.go b/src/net/sock_windows.go ---- a/src/net/sock_windows.go (revision 4b29590aa510e05686ea53de16e1e571d22203d8) -+++ b/src/net/sock_windows.go (revision 2263b05b2fa6ce228fde1899587baf109f1e2e0a) -@@ -20,6 +20,21 @@ - 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) - } -Index: src/syscall/exec_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/syscall/exec_windows.go b/src/syscall/exec_windows.go ---- a/src/syscall/exec_windows.go (revision 4b29590aa510e05686ea53de16e1e571d22203d8) -+++ b/src/syscall/exec_windows.go (revision ae41f7abdd5d7b8b51db2c03bf819ac66b8e1eb1) -@@ -15,7 +15,6 @@ - "unsafe" - ) - --// ForkLock is not used on Windows. - var ForkLock sync.RWMutex - - // EscapeArg rewrites command line argument s as prescribed -@@ -304,6 +303,9 @@ - var zeroProcAttr ProcAttr - var zeroSysProcAttr SysProcAttr - -+//go:linkname rtlGetNtVersionNumbers -+func rtlGetNtVersionNumbers(majorVersion *uint32, minorVersion *uint32, buildNumber *uint32) -+ - func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle uintptr, err error) { - if len(argv0) == 0 { - return 0, 0, EWINDOWS -@@ -367,6 +369,17 @@ - } - } - -+ 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 { -@@ -375,7 +388,15 @@ - fd := make([]Handle, len(attr.Files)) - for i := range attr.Files { - if attr.Files[i] > 0 { -- err := DuplicateHandle(p, Handle(attr.Files[i]), parentProcess, &fd[i], 0, true, DUPLICATE_SAME_ACCESS) -+ 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) - if err != nil { - return 0, 0, err - } -@@ -406,6 +427,14 @@ - - 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 -Index: src/syscall/dll_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/syscall/dll_windows.go b/src/syscall/dll_windows.go ---- a/src/syscall/dll_windows.go (revision ae41f7abdd5d7b8b51db2c03bf819ac66b8e1eb1) -+++ b/src/syscall/dll_windows.go (revision ce2e1a3d2c3c0d7277b4102841db1697147d2923) -@@ -119,14 +119,7 @@ - } - - //go:linkname loadsystemlibrary --func loadsystemlibrary(filename *uint16) (uintptr, Errno) { -- const _LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800 -- handle, _, err := SyscallN(uintptr(__LoadLibraryExW), uintptr(unsafe.Pointer(filename)), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32) -- if handle != 0 { -- err = 0 -- } -- return handle, err --} -+func loadsystemlibrary(filename *uint16, absoluteFilepath *uint16) (handle uintptr, err Errno) - - //go:linkname getprocaddress - func getprocaddress(handle uintptr, procname *uint8) (uintptr, Errno) { -@@ -143,6 +136,9 @@ - Handle Handle - } - -+//go:linkname getSystemDirectory -+func getSystemDirectory() string // Implemented in runtime package. -+ - // LoadDLL loads the named DLL file into memory. - // - // If name is not an absolute path and is not a known system DLL used by -@@ -159,7 +155,11 @@ - var h uintptr - var e Errno - if sysdll.IsSystemDLL[name] { -- h, e = loadsystemlibrary(namep) -+ absoluteFilepathp, err := UTF16PtrFromString(getSystemDirectory() + name) -+ if err != nil { -+ return nil, err -+ } -+ h, e = loadsystemlibrary(namep, absoluteFilepathp) - } else { - h, e = loadlibrary(namep) - } -Index: src/os/removeall_at.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/os/removeall_at.go b/src/os/removeall_at.go ---- a/src/os/removeall_at.go (revision ce2e1a3d2c3c0d7277b4102841db1697147d2923) -+++ b/src/os/removeall_at.go (revision 4ea1045cf3124221f055dbd2f3d2c9822934f661) -@@ -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 unix || wasip1 || windows -+//go:build unix || wasip1 - - package os - -@@ -175,3 +175,25 @@ - } - return newDirFile(fd, name) - } -+ -+func rootRemoveAll(r *Root, name string) error { -+ // Consistency with os.RemoveAll: Strip trailing /s from the name, -+ // so RemoveAll("not_a_directory/") succeeds. -+ for len(name) > 0 && IsPathSeparator(name[len(name)-1]) { -+ name = name[:len(name)-1] -+ } -+ if endsWithDot(name) { -+ // Consistency with os.RemoveAll: Return EINVAL when trying to remove . -+ return &PathError{Op: "RemoveAll", Path: name, Err: syscall.EINVAL} -+ } -+ _, err := doInRoot(r, name, nil, func(parent sysfdType, name string) (struct{}, error) { -+ return struct{}{}, removeAllFrom(parent, name) -+ }) -+ if IsNotExist(err) { -+ return nil -+ } -+ if err != nil { -+ return &PathError{Op: "RemoveAll", Path: name, Err: underlyingError(err)} -+ } -+ return err -+} -Index: src/os/removeall_noat.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/os/removeall_noat.go b/src/os/removeall_noat.go ---- a/src/os/removeall_noat.go (revision ce2e1a3d2c3c0d7277b4102841db1697147d2923) -+++ b/src/os/removeall_noat.go (revision 4ea1045cf3124221f055dbd2f3d2c9822934f661) -@@ -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 && wasm) || plan9 -+//go:build (js && wasm) || plan9 || windows - - package os - -@@ -140,3 +140,22 @@ - } - return err - } -+ -+func rootRemoveAll(r *Root, name string) error { -+ if endsWithDot(name) { -+ // Consistency with os.RemoveAll: Return EINVAL when trying to remove . -+ return &PathError{Op: "RemoveAll", Path: name, Err: syscall.EINVAL} -+ } -+ if err := checkPathEscapesLstat(r, name); err != nil { -+ if err == syscall.ENOTDIR { -+ // Some intermediate path component is not a directory. -+ // RemoveAll treats this as success (since the target doesn't exist). -+ return nil -+ } -+ return &PathError{Op: "RemoveAll", Path: name, Err: err} -+ } -+ if err := RemoveAll(joinPath(r.root.name, name)); err != nil { -+ return &PathError{Op: "RemoveAll", Path: name, Err: underlyingError(err)} -+ } -+ return nil -+} -Index: src/os/root_noopenat.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/os/root_noopenat.go b/src/os/root_noopenat.go ---- a/src/os/root_noopenat.go (revision ce2e1a3d2c3c0d7277b4102841db1697147d2923) -+++ b/src/os/root_noopenat.go (revision 4ea1045cf3124221f055dbd2f3d2c9822934f661) -@@ -11,7 +11,6 @@ - "internal/filepathlite" - "internal/stringslite" - "sync/atomic" -- "syscall" - "time" - ) - -@@ -185,25 +184,6 @@ - } - return nil - } -- --func rootRemoveAll(r *Root, name string) error { -- if endsWithDot(name) { -- // Consistency with os.RemoveAll: Return EINVAL when trying to remove . -- return &PathError{Op: "RemoveAll", Path: name, Err: syscall.EINVAL} -- } -- if err := checkPathEscapesLstat(r, name); err != nil { -- if err == syscall.ENOTDIR { -- // Some intermediate path component is not a directory. -- // RemoveAll treats this as success (since the target doesn't exist). -- return nil -- } -- return &PathError{Op: "RemoveAll", Path: name, Err: err} -- } -- if err := RemoveAll(joinPath(r.root.name, name)); err != nil { -- return &PathError{Op: "RemoveAll", Path: name, Err: underlyingError(err)} -- } -- return nil --} - - func rootReadlink(r *Root, name string) (string, error) { - if err := checkPathEscapesLstat(r, name); err != nil { -Index: src/os/root_openat.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/os/root_openat.go b/src/os/root_openat.go ---- a/src/os/root_openat.go (revision ce2e1a3d2c3c0d7277b4102841db1697147d2923) -+++ b/src/os/root_openat.go (revision 4ea1045cf3124221f055dbd2f3d2c9822934f661) -@@ -196,28 +196,6 @@ - return nil - } - --func rootRemoveAll(r *Root, name string) error { -- // Consistency with os.RemoveAll: Strip trailing /s from the name, -- // so RemoveAll("not_a_directory/") succeeds. -- for len(name) > 0 && IsPathSeparator(name[len(name)-1]) { -- name = name[:len(name)-1] -- } -- if endsWithDot(name) { -- // Consistency with os.RemoveAll: Return EINVAL when trying to remove . -- return &PathError{Op: "RemoveAll", Path: name, Err: syscall.EINVAL} -- } -- _, err := doInRoot(r, name, nil, func(parent sysfdType, name string) (struct{}, error) { -- return struct{}{}, removeAllFrom(parent, name) -- }) -- if IsNotExist(err) { -- return nil -- } -- if err != nil { -- return &PathError{Op: "RemoveAll", Path: name, Err: underlyingError(err)} -- } -- return err --} -- - func rootRename(r *Root, oldname, newname string) error { - _, err := doInRoot(r, oldname, nil, func(oldparent sysfdType, oldname string) (struct{}, error) { - _, err := doInRoot(r, newname, nil, func(newparent sysfdType, newname string) (struct{}, error) { -Index: src/os/root_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/os/root_windows.go b/src/os/root_windows.go ---- a/src/os/root_windows.go (revision ce2e1a3d2c3c0d7277b4102841db1697147d2923) -+++ b/src/os/root_windows.go (revision 4ea1045cf3124221f055dbd2f3d2c9822934f661) -@@ -402,3 +402,14 @@ - } - return fi.Mode(), nil - } -+ -+func checkPathEscapes(r *Root, name string) error { -+ if !filepathlite.IsLocal(name) { -+ return errPathEscapes -+ } -+ return nil -+} -+ -+func checkPathEscapesLstat(r *Root, name string) error { -+ return checkPathEscapes(r, name) -+} -Index: src/os/exec_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/os/exec_windows.go b/src/os/exec_windows.go ---- a/src/os/exec_windows.go (revision 4ea1045cf3124221f055dbd2f3d2c9822934f661) -+++ b/src/os/exec_windows.go (revision 8149d992682ce76c6af804b507878e19fc966f7b) -@@ -10,6 +10,7 @@ - "runtime" - "syscall" - "time" -+ _ "unsafe" - ) - - // Note that Process.handle is never nil because Windows always requires -@@ -49,9 +50,23 @@ - // than statusDone. - p.doRelease(statusReleased) - -+ var maj, min, build uint32 -+ rtlGetNtVersionNumbers(&maj, &min, &build) -+ if maj < 10 { -+ // NOTE(brainman): It seems that sometimes process is not dead -+ // when WaitForSingleObject returns. But we do not know any -+ // other way to wait for it. Sleeping for a while seems to do -+ // the trick sometimes. -+ // See https://golang.org/issue/25965 for details. -+ time.Sleep(5 * time.Millisecond) -+ } -+ - return &ProcessState{p.Pid, syscall.WaitStatus{ExitCode: ec}, &u}, nil - } - -+//go:linkname rtlGetNtVersionNumbers syscall.rtlGetNtVersionNumbers -+func rtlGetNtVersionNumbers(majorVersion *uint32, minorVersion *uint32, buildNumber *uint32) -+ - func (p *Process) signal(sig Signal) error { - handle, status := p.handleTransientAcquire() - switch status { diff --git a/clash-meta/.github/workflows/build.yml b/clash-meta/.github/workflows/build.yml index 2b8c1671e6..d978787047 100644 --- a/clash-meta/.github/workflows/build.yml +++ b/clash-meta/.github/workflows/build.yml @@ -29,13 +29,6 @@ jobs: strategy: matrix: jobs: - - { goos: darwin, goarch: amd64, goamd64: v1, output: amd64-compatible } # old style file name will be removed in next released - - { goos: darwin, goarch: amd64, goamd64: v3, output: amd64 } - - { goos: darwin, goarch: amd64, goamd64: v1, output: amd64-v1 } - - { goos: darwin, goarch: amd64, goamd64: v2, output: amd64-v2 } - - { goos: darwin, goarch: amd64, goamd64: v3, output: amd64-v3 } - - { goos: darwin, goarch: arm64, output: arm64 } - - { goos: linux, goarch: '386', go386: sse2, output: '386', debian: i386, rpm: i386} - { goos: linux, goarch: '386', go386: softfloat, output: '386-softfloat' } - { goos: linux, goarch: amd64, goamd64: v1, output: amd64-compatible} # old style file name will be removed in next released @@ -59,6 +52,15 @@ jobs: - { goos: linux, goarch: s390x, output: s390x, debian: s390x, rpm: s390x } - { goos: linux, goarch: ppc64le, output: ppc64le, debian: ppc64el, rpm: ppc64le } + # Go 1.26 with special patch can work on macOS 10.13 High Sierra + # https://github.com/MetaCubeX/go/commits/release-branch.go1.26/ + - { goos: darwin, goarch: amd64, goamd64: v1, output: amd64-compatible } # old style file name will be removed in next released + - { goos: darwin, goarch: amd64, goamd64: v3, output: amd64 } + - { goos: darwin, goarch: amd64, goamd64: v1, output: amd64-v1 } + - { goos: darwin, goarch: amd64, goamd64: v2, output: amd64-v2 } + - { goos: darwin, goarch: amd64, goamd64: v3, output: amd64-v3 } + - { goos: darwin, goarch: arm64, output: arm64 } + # Go 1.26 with special patch can work on Windows 7 # https://github.com/MetaCubeX/go/commits/release-branch.go1.26/ - { goos: windows, goarch: '386', output: '386' } @@ -159,17 +161,17 @@ jobs: - name: Set up Go if: ${{ matrix.jobs.goversion == '' }} - uses: actions/setup-go@v6 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c with: + go-download-base-url: 'https://github.com/MetaCubeX/go/releases/download/build' go-version: '1.26' - check-latest: true # Always check for the latest patch release - name: Set up Go if: ${{ matrix.jobs.goversion != '' && matrix.jobs.goversion != 'custom' }} - uses: actions/setup-go@v6 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c with: + go-download-base-url: 'https://github.com/MetaCubeX/go/releases/download/build' go-version: ${{ matrix.jobs.goversion }} - check-latest: true # Always check for the latest patch release - name: Set up Go1.26 loongarch abi1 if: ${{ matrix.jobs.goarch == 'loong64' && matrix.jobs.abi == '1' }} @@ -178,6 +180,12 @@ jobs: go-download-base-url: 'https://github.com/MetaCubeX/loongarch64-golang/releases/download/1.26.0' go-version: 1.26.0 + - name: Verify Go installation + run: go version + + - name: Verify Go env + run: go env + # TODO: remove after issue77731 fixed, see: https://github.com/golang/go/issues/77731 - name: Fix issue77731 for Golang1.26 if: ${{ matrix.jobs.goversion == '' }} @@ -199,89 +207,6 @@ jobs: cd $(go env GOROOT) patch --verbose -p 1 < $GITHUB_WORKSPACE/.github/patch/issue77930.patch - # this patch file only works on golang1.26.x - # that means after golang1.27 release it must be changed - # see: https://github.com/MetaCubeX/go/commits/release-branch.go1.26/ - # 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" - # f0894a00f4b756d4b9b4078af2e686b359493583: "os: remove 5ms sleep on Windows in (*Process).Wait" - # sepical fix: - # - os.RemoveAll not working on Windows7 - - name: Revert Golang1.26 commit for Windows7/8 - if: ${{ matrix.jobs.goos == 'windows' && matrix.jobs.goversion == '' }} - run: | - cd $(go env GOROOT) - patch --verbose -p 1 < $GITHUB_WORKSPACE/.github/patch/go1.26.patch - - # this patch file only works on golang1.25.x - # that means after golang1.26 release it must be changed - # see: https://github.com/MetaCubeX/go/commits/release-branch.go1.25/ - # 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" - # f0894a00f4b756d4b9b4078af2e686b359493583: "os: remove 5ms sleep on Windows in (*Process).Wait" - # sepical fix: - # - os.RemoveAll not working on Windows7 - - name: Revert Golang1.25 commit for Windows7/8 - if: ${{ matrix.jobs.goos == 'windows' && matrix.jobs.goversion == '1.25' }} - run: | - cd $(go env GOROOT) - patch --verbose -p 1 < $GITHUB_WORKSPACE/.github/patch/go1.25.patch - - # this patch file only works on golang1.24.x - # that means after golang1.25 release it must be changed - # see: https://github.com/MetaCubeX/go/commits/release-branch.go1.24/ - # 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.24 commit for Windows7/8 - if: ${{ matrix.jobs.goos == 'windows' && matrix.jobs.goversion == '1.24' }} - run: | - cd $(go env GOROOT) - patch --verbose -p 1 < $GITHUB_WORKSPACE/.github/patch/go1.24.patch - - # 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" - # 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 == '1.23' }} - run: | - cd $(go env GOROOT) - patch --verbose -p 1 < $GITHUB_WORKSPACE/.github/patch/go1.23.patch - - # 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) - patch --verbose -p 1 < $GITHUB_WORKSPACE/.github/patch/go1.22.patch - - # modify from https://github.com/restic/restic/issues/4636#issuecomment-1896455557 - - name: Revert Golang1.21 commit for Windows7/8 - if: ${{ matrix.jobs.goos == 'windows' && matrix.jobs.goversion == '1.21' }} - run: | - cd $(go env GOROOT) - patch --verbose -p 1 < $GITHUB_WORKSPACE/.github/patch/go1.21.patch - - name: Set variables run: | VERSION="${GITHUB_REF_NAME,,}-$(git rev-parse --short HEAD)" @@ -316,6 +241,7 @@ jobs: - name: Test if: ${{ matrix.jobs.test == 'test' }} run: | + export SKIP_CONCURRENT_TEST=1 go test ./... echo "---test with_gvisor---" go test ./... -tags "with_gvisor" -count=1 diff --git a/clash-meta/.github/workflows/test.yml b/clash-meta/.github/workflows/test.yml index 13a08a4ad5..e72e7b2f3d 100644 --- a/clash-meta/.github/workflows/test.yml +++ b/clash-meta/.github/workflows/test.yml @@ -44,11 +44,17 @@ jobs: steps: - uses: actions/checkout@v6 - - name: Setup Go - uses: actions/setup-go@v6 + - name: Set up Go + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c with: + go-download-base-url: 'https://github.com/MetaCubeX/go/releases/download/build' go-version: ${{ matrix.go-version }} - check-latest: true # Always check for the latest patch release + + - name: Verify Go installation + run: go version + + - name: Verify Go env + run: go env # TODO: remove after issue77975 fixed, see: https://github.com/golang/go/issues/77975 - name: Fix issue77975 for Golang1.26 @@ -57,12 +63,6 @@ jobs: cd $(go env GOROOT) patch --verbose -p 1 < $GITHUB_WORKSPACE/.github/patch/issue77975.patch - - name: Revert Golang commit for Windows7/8 - if: ${{ runner.os == 'Windows' && matrix.go-version != '1.20' }} - run: | - cd $(go env GOROOT) - patch --verbose -p 1 < $GITHUB_WORKSPACE/.github/patch/go${{matrix.go-version}}.patch - - name: Remove inbound test for macOS if: ${{ runner.os == 'macOS' }} run: | diff --git a/clash-meta/adapter/outbound/vless.go b/clash-meta/adapter/outbound/vless.go index 78ccb6676f..dfe7d6b67e 100644 --- a/clash-meta/adapter/outbound/vless.go +++ b/clash-meta/adapter/outbound/vless.go @@ -17,6 +17,7 @@ import ( "github.com/metacubex/mihomo/transport/vless" "github.com/metacubex/mihomo/transport/vless/encryption" "github.com/metacubex/mihomo/transport/vmess" + "github.com/metacubex/mihomo/transport/xhttp" "github.com/metacubex/http" vmessSing "github.com/metacubex/sing-vmess" @@ -60,6 +61,7 @@ type VlessOption struct { HTTP2Opts HTTP2Options `proxy:"h2-opts,omitempty"` GrpcOpts GrpcOptions `proxy:"grpc-opts,omitempty"` WSOpts WSOptions `proxy:"ws-opts,omitempty"` + XHTTPOpts XHTTPOptions `proxy:"xhttp-opts,omitempty"` WSHeaders map[string]string `proxy:"ws-headers,omitempty"` SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` Fingerprint string `proxy:"fingerprint,omitempty"` @@ -69,6 +71,16 @@ type VlessOption struct { ClientFingerprint string `proxy:"client-fingerprint,omitempty"` } +type XHTTPOptions struct { + Path string `proxy:"path,omitempty"` + Host string `proxy:"host,omitempty"` + Mode string `proxy:"mode,omitempty"` + Headers map[string]string `proxy:"headers,omitempty"` + ScMaxConcurrentPosts int `proxy:"sc-max-concurrent-posts,omitempty"` + NoGRPCHeader bool `proxy:"no-grpc-header,omitempty"` + XPaddingBytes string `proxy:"x-padding-bytes,omitempty"` +} + func (v *Vless) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.Metadata) (_ net.Conn, err error) { switch v.option.Network { case "ws": @@ -150,6 +162,8 @@ func (v *Vless) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.M c, err = vmess.StreamH2Conn(ctx, c, h2Opts) case "grpc": break // already handle in gun transport + case "xhttp": + break // already handle in dialXHTTPConn default: // default tcp network // handle TLS @@ -228,13 +242,83 @@ func (v *Vless) streamTLSConn(ctx context.Context, conn net.Conn, isH2 bool) (ne return conn, nil } +func (v *Vless) dialXHTTPConn(ctx context.Context) (net.Conn, error) { + requestHost := v.option.XHTTPOpts.Host + if requestHost == "" { + if v.option.ServerName != "" { + requestHost = v.option.ServerName + } else { + requestHost = v.option.Server + } + } + + cfg := &xhttp.Config{ + Host: requestHost, + Path: v.option.XHTTPOpts.Path, + Mode: v.option.XHTTPOpts.Mode, + Headers: v.option.XHTTPOpts.Headers, + NoGRPCHeader: v.option.XHTTPOpts.NoGRPCHeader, + XPaddingBytes: v.option.XHTTPOpts.XPaddingBytes, + } + + mode := cfg.EffectiveMode(v.realityConfig != nil) + + switch mode { + case "stream-one": + return xhttp.DialStreamOne( + ctx, + v.option.Server, + v.option.Port, + cfg, + func(ctx context.Context) (net.Conn, error) { + return v.dialer.DialContext(ctx, "tcp", v.addr) + }, + func(ctx context.Context, raw net.Conn, isH2 bool) (net.Conn, error) { + return v.streamTLSConn(ctx, raw, isH2) + }, + ) + case "packet-up": + return xhttp.DialPacketUp( + ctx, + v.option.Server, + v.option.Port, + cfg, + func(ctx context.Context) (net.Conn, error) { + return v.dialer.DialContext(ctx, "tcp", v.addr) + }, + func(ctx context.Context, raw net.Conn, isH2 bool) (net.Conn, error) { + return v.streamTLSConn(ctx, raw, isH2) + }, + ) + default: + return nil, fmt.Errorf("xhttp mode %s is not implemented yet", mode) + } +} + // DialContext implements C.ProxyAdapter func (v *Vless) DialContext(ctx context.Context, metadata *C.Metadata) (_ C.Conn, err error) { + if v.option.Network == "xhttp" { + c, err := v.dialXHTTPConn(ctx) + if err != nil { + return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error()) + } + + c, err = v.streamConnContext(ctx, c, metadata) + if err != nil { + safeConnClose(c, err) + return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error()) + } + + return NewConn(c, v), nil + } + var c net.Conn - // gun transport - if v.gunTransport != nil { + switch v.option.Network { + case "xhttp": + c, err = v.dialXHTTPConn(ctx) + case "grpc": // gun transport c, err = v.gunTransport.Dial() - } else { + default: c, err = v.dialer.DialContext(ctx, "tcp", v.addr) } if err != nil { @@ -256,11 +340,14 @@ func (v *Vless) ListenPacketContext(ctx context.Context, metadata *C.Metadata) ( if err = v.ResolveUDP(ctx, metadata); err != nil { return nil, err } + var c net.Conn - // gun transport - if v.gunTransport != nil { + switch v.option.Network { + case "xhttp": + c, err = v.dialXHTTPConn(ctx) + case "grpc": // gun transport c, err = v.gunTransport.Dial() - } else { + default: c, err = v.dialer.DialContext(ctx, "tcp", v.addr) } if err != nil { diff --git a/clash-meta/common/contextutils/withoutcancel_compact.go b/clash-meta/common/contextutils/withoutcancel_compact.go new file mode 100644 index 0000000000..942b6503f1 --- /dev/null +++ b/clash-meta/common/contextutils/withoutcancel_compact.go @@ -0,0 +1,26 @@ +package contextutils + +import ( + "context" + "time" +) + +type withoutCancelCtx struct { + c context.Context +} + +func (withoutCancelCtx) Deadline() (deadline time.Time, ok bool) { + return +} + +func (withoutCancelCtx) Done() <-chan struct{} { + return nil +} + +func (withoutCancelCtx) Err() error { + return nil +} + +func (c withoutCancelCtx) Value(key any) any { + return c.c.Value(key) +} diff --git a/clash-meta/common/contextutils/withoutcancel_go120.go b/clash-meta/common/contextutils/withoutcancel_go120.go new file mode 100644 index 0000000000..44c6a941b1 --- /dev/null +++ b/clash-meta/common/contextutils/withoutcancel_go120.go @@ -0,0 +1,12 @@ +//go:build !go1.21 + +package contextutils + +import "context" + +func WithoutCancel(parent context.Context) context.Context { + if parent == nil { + panic("cannot create context from nil parent") + } + return withoutCancelCtx{parent} +} diff --git a/clash-meta/common/contextutils/withoutcancel_go121.go b/clash-meta/common/contextutils/withoutcancel_go121.go new file mode 100644 index 0000000000..f955db9270 --- /dev/null +++ b/clash-meta/common/contextutils/withoutcancel_go121.go @@ -0,0 +1,9 @@ +//go:build go1.21 + +package contextutils + +import "context" + +func WithoutCancel(parent context.Context) context.Context { + return context.WithoutCancel(parent) +} diff --git a/clash-meta/listener/config/vless.go b/clash-meta/listener/config/vless.go index 7facaf25f3..931b4fd8bf 100644 --- a/clash-meta/listener/config/vless.go +++ b/clash-meta/listener/config/vless.go @@ -19,6 +19,7 @@ type VlessServer struct { Users []VlessUser Decryption string WsPath string + XHTTPConfig XHTTPConfig GrpcServiceName string Certificate string PrivateKey string @@ -29,6 +30,12 @@ type VlessServer struct { MuxOption sing.MuxOption `yaml:"mux-option" json:"mux-option,omitempty"` } +type XHTTPConfig struct { + Path string + Host string + Mode string +} + func (t VlessServer) String() string { b, _ := json.Marshal(t) return string(b) diff --git a/clash-meta/listener/inbound/common_test.go b/clash-meta/listener/inbound/common_test.go index b1be5e4d57..eabdb6b111 100644 --- a/clash-meta/listener/inbound/common_test.go +++ b/clash-meta/listener/inbound/common_test.go @@ -9,6 +9,7 @@ import ( "io" "net" "net/netip" + "os" "strconv" "sync" "testing" @@ -61,7 +62,6 @@ type TestTunnel struct { HandleUDPPacketFn func(packet C.UDPPacket, metadata *C.Metadata) NatTableFn func() C.NatTable CloseFn func() error - DoTestFn func(t *testing.T, proxy C.ProxyAdapter) DoSequentialTestFn func(t *testing.T, proxy C.ProxyAdapter) DoConcurrentTestFn func(t *testing.T, proxy C.ProxyAdapter) } @@ -83,7 +83,8 @@ func (tt *TestTunnel) Close() error { } func (tt *TestTunnel) DoTest(t *testing.T, proxy C.ProxyAdapter) { - tt.DoTestFn(t, proxy) + tt.DoSequentialTestFn(t, proxy) + tt.DoConcurrentTestFn(t, proxy) } func (tt *TestTunnel) DoSequentialTest(t *testing.T, proxy C.ProxyAdapter) { @@ -236,6 +237,9 @@ func NewHttpTestTunnel() *TestTunnel { concurrentTestFn := func(t *testing.T, proxy C.ProxyAdapter) { // Concurrent testing to detect stress t.Run("Concurrent", func(t *testing.T) { + if skip, _ := strconv.ParseBool(os.Getenv("SKIP_CONCURRENT_TEST")); skip { + t.Skip("skip concurrent test") + } wg := sync.WaitGroup{} num := len(httpData) / 1024 for i := 1; i <= num; i++ { @@ -296,11 +300,7 @@ func NewHttpTestTunnel() *TestTunnel { } <-c.ch }, - CloseFn: ln.Close, - DoTestFn: func(t *testing.T, proxy C.ProxyAdapter) { - sequentialTestFn(t, proxy) - concurrentTestFn(t, proxy) - }, + CloseFn: ln.Close, DoSequentialTestFn: sequentialTestFn, DoConcurrentTestFn: concurrentTestFn, } diff --git a/clash-meta/listener/inbound/mux_test.go b/clash-meta/listener/inbound/mux_test.go index 0f632220e6..09facdb87b 100644 --- a/clash-meta/listener/inbound/mux_test.go +++ b/clash-meta/listener/inbound/mux_test.go @@ -6,9 +6,11 @@ import ( "github.com/metacubex/mihomo/adapter/outbound" "github.com/stretchr/testify/assert" + "golang.org/x/exp/slices" ) -var singMuxProtocolList = []string{"smux", "yamux"} // don't test "h2mux" because it has some confused bugs +var singMuxProtocolList = []string{"h2mux", "smux", "yamux"} +var singMuxProtocolListLong = []string{"yamux"} // don't test "smux", "h2mux" because it has some confused bugs // notCloseProxyAdapter is a proxy adapter that does not close the underlying outbound.ProxyAdapter. // The outbound.SingMux will close the underlying outbound.ProxyAdapter when it is closed, but we don't want to close it. @@ -37,7 +39,10 @@ func testSingMux(t *testing.T, tunnel *TestTunnel, out outbound.ProxyAdapter) { } defer out.Close() - tunnel.DoTest(t, out) + tunnel.DoSequentialTest(t, out) + if slices.Contains(singMuxProtocolListLong, protocol) { + tunnel.DoConcurrentTest(t, out) + } }) } }) diff --git a/clash-meta/listener/inbound/vless.go b/clash-meta/listener/inbound/vless.go index d8109f8443..1a88cc7411 100644 --- a/clash-meta/listener/inbound/vless.go +++ b/clash-meta/listener/inbound/vless.go @@ -14,6 +14,7 @@ type VlessOption struct { Users []VlessUser `inbound:"users"` Decryption string `inbound:"decryption,omitempty"` WsPath string `inbound:"ws-path,omitempty"` + XHTTPConfig XHTTPConfig `inbound:"xhttp-config,omitempty"` GrpcServiceName string `inbound:"grpc-service-name,omitempty"` Certificate string `inbound:"certificate,omitempty"` PrivateKey string `inbound:"private-key,omitempty"` @@ -30,6 +31,20 @@ type VlessUser struct { Flow string `inbound:"flow,omitempty"` } +type XHTTPConfig struct { + Path string `inbound:"path,omitempty"` + Host string `inbound:"host,omitempty"` + Mode string `inbound:"mode,omitempty"` +} + +func (o XHTTPConfig) Build() LC.XHTTPConfig { + return LC.XHTTPConfig{ + Path: o.Path, + Host: o.Host, + Mode: o.Mode, + } +} + func (o VlessOption) Equal(config C.InboundConfig) bool { return optionToString(o) == optionToString(config) } @@ -63,6 +78,7 @@ func NewVless(options *VlessOption) (*Vless, error) { Users: users, Decryption: options.Decryption, WsPath: options.WsPath, + XHTTPConfig: options.XHTTPConfig.Build(), GrpcServiceName: options.GrpcServiceName, Certificate: options.Certificate, PrivateKey: options.PrivateKey, diff --git a/clash-meta/listener/inbound/vless_test.go b/clash-meta/listener/inbound/vless_test.go index e12e95567d..5b5d68f89d 100644 --- a/clash-meta/listener/inbound/vless_test.go +++ b/clash-meta/listener/inbound/vless_test.go @@ -340,3 +340,54 @@ func TestInboundVless_Reality_Grpc(t *testing.T) { testInboundVless(t, inboundOptions, outboundOptions) }) } + +func TestInboundVless_XHTTP(t *testing.T) { + inboundOptions := inbound.VlessOption{ + Certificate: tlsCertificate, + PrivateKey: tlsPrivateKey, + XHTTPConfig: inbound.XHTTPConfig{ + Path: "/vless-xhttp", + Host: "example.com", + Mode: "auto", + }, + } + outboundOptions := outbound.VlessOption{ + TLS: true, + Fingerprint: tlsFingerprint, + Network: "xhttp", + XHTTPOpts: outbound.XHTTPOptions{ + Path: "/vless-xhttp", + Host: "example.com", + Mode: "auto", + }, + } + testInboundVlessTLS(t, inboundOptions, outboundOptions, false) +} + +func TestInboundVless_Reality_XHTTP(t *testing.T) { + inboundOptions := inbound.VlessOption{ + RealityConfig: inbound.RealityConfig{ + Dest: net.JoinHostPort(realityDest, "443"), + PrivateKey: realityPrivateKey, + ShortID: []string{realityShortid}, + ServerNames: []string{realityDest}, + }, + XHTTPConfig: inbound.XHTTPConfig{ + Mode: "auto", + }, + } + outboundOptions := outbound.VlessOption{ + TLS: true, + ServerName: realityDest, + RealityOpts: outbound.RealityOptions{ + PublicKey: realityPublickey, + ShortID: realityShortid, + }, + ClientFingerprint: "chrome", + Network: "xhttp", + XHTTPOpts: outbound.XHTTPOptions{ + Mode: "auto", + }, + } + testInboundVless(t, inboundOptions, outboundOptions) +} diff --git a/clash-meta/listener/sing_vless/server.go b/clash-meta/listener/sing_vless/server.go index 83ba577924..76566df986 100644 --- a/clash-meta/listener/sing_vless/server.go +++ b/clash-meta/listener/sing_vless/server.go @@ -17,11 +17,13 @@ import ( "github.com/metacubex/mihomo/transport/gun" "github.com/metacubex/mihomo/transport/vless/encryption" mihomoVMess "github.com/metacubex/mihomo/transport/vmess" + "github.com/metacubex/mihomo/transport/xhttp" "github.com/metacubex/http" "github.com/metacubex/sing/common" "github.com/metacubex/sing/common/metadata" "github.com/metacubex/tls" + "golang.org/x/exp/slices" ) type Listener struct { @@ -144,7 +146,27 @@ func New(config LC.VlessServer, tunnel C.Tunnel, additions ...inbound.Addition) }) tlsConfig.NextProtos = append([]string{"h2"}, tlsConfig.NextProtos...) // h2 must before http/1.1 } - + if config.XHTTPConfig.Mode != "" { + switch config.XHTTPConfig.Mode { + case "auto": + default: + return nil, errors.New("unsupported xhttp mode") + } + } + if config.XHTTPConfig.Path != "" || config.XHTTPConfig.Host != "" || config.XHTTPConfig.Mode != "" { + httpServer.Handler = xhttp.NewServerHandler(xhttp.ServerOption{ + Path: config.XHTTPConfig.Path, + Host: config.XHTTPConfig.Host, + Mode: config.XHTTPConfig.Mode, + ConnHandler: func(conn net.Conn) { + sl.HandleConn(conn, tunnel, additions...) + }, + HttpHandler: httpServer.Handler, + }) + if !slices.Contains(tlsConfig.NextProtos, "h2") { + tlsConfig.NextProtos = append([]string{"h2"}, tlsConfig.NextProtos...) + } + } for _, addr := range strings.Split(config.Listen, ",") { addr := addr diff --git a/clash-meta/transport/xhttp/client.go b/clash-meta/transport/xhttp/client.go new file mode 100644 index 0000000000..093ee1d238 --- /dev/null +++ b/clash-meta/transport/xhttp/client.go @@ -0,0 +1,293 @@ +package xhttp + +import ( + "context" + "crypto/rand" + "encoding/hex" + "fmt" + "io" + "net" + "net/url" + "strconv" + "sync" + "time" + + "github.com/metacubex/mihomo/common/contextutils" + "github.com/metacubex/mihomo/transport/gun" + + "github.com/metacubex/http" + "github.com/metacubex/http/httptrace" + "github.com/metacubex/tls" +) + +type DialRawFunc func(ctx context.Context) (net.Conn, error) +type WrapTLSFunc func(ctx context.Context, conn net.Conn, isH2 bool) (net.Conn, error) + +type PacketUpConn struct { + ctx context.Context + cfg *Config + address string + port int + host string + sessionID string + client *http.Client + writeMu sync.Mutex + seq uint64 + reader io.ReadCloser + gun.NetAddr + + // deadlines + deadline *time.Timer +} + +func (c *PacketUpConn) Read(b []byte) (int, error) { + return c.reader.Read(b) +} + +func (c *PacketUpConn) Write(b []byte) (int, error) { + c.writeMu.Lock() + defer c.writeMu.Unlock() + + u := url.URL{ + Scheme: "https", + Host: c.host, + Path: c.cfg.NormalizedPath(), + } + + req, err := http.NewRequestWithContext(c.ctx, http.MethodPost, u.String(), nil) + if err != nil { + return 0, err + } + + seqStr := strconv.FormatUint(c.seq, 10) + c.seq++ + + if err := c.cfg.FillPacketRequest(req, c.sessionID, seqStr, b); err != nil { + return 0, err + } + req.Host = c.host + + resp, err := c.client.Do(req) + if err != nil { + return 0, err + } + defer resp.Body.Close() + _, _ = io.Copy(io.Discard, resp.Body) + + if resp.StatusCode != http.StatusOK { + return 0, fmt.Errorf("xhttp packet-up bad status: %s", resp.Status) + } + + return len(b), nil +} + +func (c *PacketUpConn) Close() error { + if c.reader != nil { + return c.reader.Close() + } + return nil +} + +func (c *PacketUpConn) SetReadDeadline(t time.Time) error { return c.SetDeadline(t) } +func (c *PacketUpConn) SetWriteDeadline(t time.Time) error { return c.SetDeadline(t) } + +func (c *PacketUpConn) SetDeadline(t time.Time) error { + if t.IsZero() { + if c.deadline != nil { + c.deadline.Stop() + c.deadline = nil + } + return nil + } + d := time.Until(t) + if c.deadline != nil { + c.deadline.Reset(d) + return nil + } + c.deadline = time.AfterFunc(d, func() { + c.Close() + }) + return nil +} + +func DialStreamOne( + ctx context.Context, + address string, + port int, + cfg *Config, + dialRaw DialRawFunc, + wrapTLS WrapTLSFunc, +) (net.Conn, error) { + host := cfg.Host + if host == "" { + host = address + } + + requestURL := url.URL{ + Scheme: "https", + Host: host, + Path: cfg.NormalizedPath(), + } + + transport := &http.Http2Transport{ + DialTLSContext: func(ctx context.Context, network, addr string, _ *tls.Config) (net.Conn, error) { + raw, err := dialRaw(ctx) + if err != nil { + return nil, err + } + wrapped, err := wrapTLS(ctx, raw, true) + if err != nil { + _ = raw.Close() + return nil, err + } + return wrapped, nil + }, + } + + client := &http.Client{ + Transport: transport, + } + + pr, pw := io.Pipe() + + conn := &Conn{ + writer: pw, + } + + trace := &httptrace.ClientTrace{ + GotConn: func(connInfo httptrace.GotConnInfo) { + conn.SetLocalAddr(connInfo.Conn.LocalAddr()) + conn.SetRemoteAddr(connInfo.Conn.RemoteAddr()) + }, + } + + req, err := http.NewRequestWithContext(httptrace.WithClientTrace(contextutils.WithoutCancel(ctx), trace), http.MethodPost, requestURL.String(), pr) + if err != nil { + _ = pr.Close() + _ = pw.Close() + return nil, err + } + req.Host = host + + if err := cfg.FillStreamRequest(req); err != nil { + _ = pr.Close() + _ = pw.Close() + return nil, err + } + + type respResult struct { + resp *http.Response + err error + } + + respCh := make(chan respResult, 1) + + go func() { + resp, err := client.Do(req) + respCh <- respResult{resp: resp, err: err} + }() + + result := <-respCh + if result.err != nil { + _ = pr.Close() + _ = pw.Close() + return nil, result.err + } + if result.resp.StatusCode < 200 || result.resp.StatusCode >= 300 { + _ = result.resp.Body.Close() + _ = pr.Close() + _ = pw.Close() + return nil, fmt.Errorf("xhttp stream-one bad status: %s", result.resp.Status) + } + conn.reader = result.resp.Body + conn.onClose = func() { + _ = result.resp.Body.Close() + _ = pr.Close() + } + + return conn, nil +} + +func DialPacketUp( + ctx context.Context, + address string, + port int, + cfg *Config, + dialRaw DialRawFunc, + wrapTLS WrapTLSFunc, +) (net.Conn, error) { + host := cfg.Host + if host == "" { + host = address + } + + transport := &http.Http2Transport{ + DialTLSContext: func(ctx context.Context, network string, addr string, _ *tls.Config) (net.Conn, error) { + raw, err := dialRaw(ctx) + if err != nil { + return nil, err + } + wrapped, err := wrapTLS(ctx, raw, true) + if err != nil { + _ = raw.Close() + return nil, err + } + return wrapped, nil + }, + } + + client := &http.Client{Transport: transport} + + sessionID := newSessionID() + + downloadURL := url.URL{ + Scheme: "https", + Host: host, + Path: cfg.NormalizedPath(), + } + + conn := &PacketUpConn{ + ctx: contextutils.WithoutCancel(ctx), + cfg: cfg, + address: address, + port: port, + host: host, + sessionID: sessionID, + client: client, + seq: 0, + } + + trace := &httptrace.ClientTrace{ + GotConn: func(connInfo httptrace.GotConnInfo) { + conn.SetLocalAddr(connInfo.Conn.LocalAddr()) + conn.SetRemoteAddr(connInfo.Conn.RemoteAddr()) + }, + } + + req, err := http.NewRequestWithContext(httptrace.WithClientTrace(conn.ctx, trace), http.MethodGet, downloadURL.String(), nil) + if err != nil { + return nil, err + } + if err := cfg.FillDownloadRequest(req, sessionID); err != nil { + return nil, err + } + req.Host = host + + resp, err := client.Do(req) + if err != nil { + return nil, err + } + if resp.StatusCode != http.StatusOK { + _ = resp.Body.Close() + return nil, fmt.Errorf("xhttp packet-up download bad status: %s", resp.Status) + } + conn.reader = resp.Body + + return conn, nil +} + +func newSessionID() string { + var b [16]byte + _, _ = rand.Read(b[:]) + return hex.EncodeToString(b[:]) +} diff --git a/clash-meta/transport/xhttp/config.go b/clash-meta/transport/xhttp/config.go new file mode 100644 index 0000000000..eda9e26bf2 --- /dev/null +++ b/clash-meta/transport/xhttp/config.go @@ -0,0 +1,209 @@ +package xhttp + +import ( + "bytes" + "fmt" + "io" + "math/rand" + "strconv" + "strings" + + "github.com/metacubex/http" +) + +type Config struct { + Host string + Path string + Mode string + Headers map[string]string + NoGRPCHeader bool + XPaddingBytes string +} + +func (c *Config) NormalizedMode() string { + if c.Mode == "" { + return "auto" + } + return c.Mode +} + +func (c *Config) EffectiveMode(hasReality bool) string { + mode := c.NormalizedMode() + if mode != "auto" { + return mode + } + if hasReality { + return "stream-one" + } + return "packet-up" +} + +func (c *Config) NormalizedPath() string { + path := c.Path + if path == "" { + path = "/" + } + if !strings.HasPrefix(path, "/") { + path = "/" + path + } + if !strings.HasSuffix(path, "/") { + path += "/" + } + return path +} + +func (c *Config) RequestHeader() http.Header { + h := http.Header{} + for k, v := range c.Headers { + h.Set(k, v) + } + + if h.Get("User-Agent") == "" { + h.Set("User-Agent", "Mozilla/5.0") + } + if h.Get("Accept") == "" { + h.Set("Accept", "*/*") + } + if h.Get("Accept-Language") == "" { + h.Set("Accept-Language", "en-US,en;q=0.9") + } + if h.Get("Cache-Control") == "" { + h.Set("Cache-Control", "no-cache") + } + if h.Get("Pragma") == "" { + h.Set("Pragma", "no-cache") + } + + return h +} + +func (c *Config) RandomPadding() (string, error) { + paddingRange := c.XPaddingBytes + if paddingRange == "" { + paddingRange = "100-1000" + } + + minVal, maxVal, err := parseRange(paddingRange) + if err != nil { + return "", err + } + if minVal < 0 || maxVal < minVal { + return "", fmt.Errorf("invalid x-padding-bytes range: %s", paddingRange) + } + if maxVal == 0 { + return "", nil + } + + n := minVal + if maxVal > minVal { + n = minVal + rand.Intn(maxVal-minVal+1) + } + + return strings.Repeat("X", n), nil +} + +func parseRange(s string) (int, int, error) { + parts := strings.Split(strings.TrimSpace(s), "-") + if len(parts) == 1 { + v, err := strconv.Atoi(parts[0]) + if err != nil { + return 0, 0, err + } + return v, v, nil + } + if len(parts) != 2 { + return 0, 0, fmt.Errorf("invalid range: %s", s) + } + + minVal, err := strconv.Atoi(strings.TrimSpace(parts[0])) + if err != nil { + return 0, 0, err + } + maxVal, err := strconv.Atoi(strings.TrimSpace(parts[1])) + if err != nil { + return 0, 0, err + } + return minVal, maxVal, nil +} + +func (c *Config) FillStreamRequest(req *http.Request) error { + req.Header = c.RequestHeader() + + paddingValue, err := c.RandomPadding() + if err != nil { + return err + } + + if paddingValue != "" { + rawURL := req.URL.String() + sep := "?" + if strings.Contains(rawURL, "?") { + sep = "&" + } + req.Header.Set("Referer", rawURL+sep+"x_padding="+paddingValue) + } + + if req.Body != nil && !c.NoGRPCHeader { + req.Header.Set("Content-Type", "application/grpc") + } + + return nil +} + +func appendToPath(path, value string) string { + if strings.HasSuffix(path, "/") { + return path + value + } + return path + "/" + value +} + +func (c *Config) ApplyMetaToRequest(req *http.Request, sessionID string, seqStr string) { + if sessionID != "" { + req.URL.Path = appendToPath(req.URL.Path, sessionID) + } + if seqStr != "" { + req.URL.Path = appendToPath(req.URL.Path, seqStr) + } +} + +func (c *Config) FillPacketRequest(req *http.Request, sessionID string, seqStr string, payload []byte) error { + req.Header = c.RequestHeader() + req.Body = io.NopCloser(bytes.NewReader(payload)) + req.ContentLength = int64(len(payload)) + + paddingValue, err := c.RandomPadding() + if err != nil { + return err + } + if paddingValue != "" { + rawURL := req.URL.String() + sep := "?" + if strings.Contains(rawURL, "?") { + sep = "&" + } + req.Header.Set("Referer", rawURL+sep+"x_padding="+paddingValue) + } + + c.ApplyMetaToRequest(req, sessionID, seqStr) + return nil +} + +func (c *Config) FillDownloadRequest(req *http.Request, sessionID string) error { + req.Header = c.RequestHeader() + + paddingValue, err := c.RandomPadding() + if err != nil { + return err + } + if paddingValue != "" { + rawURL := req.URL.String() + sep := "?" + if strings.Contains(rawURL, "?") { + sep = "&" + } + req.Header.Set("Referer", rawURL+sep+"x_padding="+paddingValue) + } + + c.ApplyMetaToRequest(req, sessionID, "") + return nil +} diff --git a/clash-meta/transport/xhttp/conn.go b/clash-meta/transport/xhttp/conn.go new file mode 100644 index 0000000000..8816a0d037 --- /dev/null +++ b/clash-meta/transport/xhttp/conn.go @@ -0,0 +1,64 @@ +package xhttp + +import ( + "io" + "time" + + "github.com/metacubex/mihomo/transport/gun" +) + +type Conn struct { + writer io.WriteCloser + reader io.ReadCloser + onClose func() + gun.NetAddr + + // deadlines + deadline *time.Timer +} + +func (c *Conn) Write(b []byte) (int, error) { + return c.writer.Write(b) +} + +func (c *Conn) Read(b []byte) (int, error) { + return c.reader.Read(b) +} + +func (c *Conn) Close() error { + if c.onClose != nil { + c.onClose() + } + + err := c.writer.Close() + err2 := c.reader.Close() + if err != nil { + return err + } + if err2 != nil { + return err2 + } + return nil +} + +func (c *Conn) SetReadDeadline(t time.Time) error { return c.SetDeadline(t) } +func (c *Conn) SetWriteDeadline(t time.Time) error { return c.SetDeadline(t) } + +func (c *Conn) SetDeadline(t time.Time) error { + if t.IsZero() { + if c.deadline != nil { + c.deadline.Stop() + c.deadline = nil + } + return nil + } + d := time.Until(t) + if c.deadline != nil { + c.deadline.Reset(d) + return nil + } + c.deadline = time.AfterFunc(d, func() { + c.Close() + }) + return nil +} diff --git a/clash-meta/transport/xhttp/server.go b/clash-meta/transport/xhttp/server.go new file mode 100644 index 0000000000..317b131df3 --- /dev/null +++ b/clash-meta/transport/xhttp/server.go @@ -0,0 +1,306 @@ +package xhttp + +import ( + "io" + "net" + "strconv" + "strings" + "sync" + "time" + + N "github.com/metacubex/mihomo/common/net" + + "github.com/metacubex/http" + "github.com/metacubex/http/h2c" +) + +type ServerOption struct { + Path string + Host string + Mode string + ConnHandler func(net.Conn) + HttpHandler http.Handler +} + +type httpServerConn struct { + mu sync.Mutex + w http.ResponseWriter + flusher http.Flusher + reader io.Reader + closed bool + done chan struct{} + once sync.Once +} + +func newHTTPServerConn(w http.ResponseWriter, r io.Reader) *httpServerConn { + flusher, _ := w.(http.Flusher) + return &httpServerConn{ + w: w, + flusher: flusher, + reader: r, + done: make(chan struct{}), + } +} + +func (c *httpServerConn) Read(b []byte) (int, error) { + return c.reader.Read(b) +} + +func (c *httpServerConn) Write(b []byte) (int, error) { + c.mu.Lock() + defer c.mu.Unlock() + + if c.closed { + return 0, io.ErrClosedPipe + } + + n, err := c.w.Write(b) + if err == nil && c.flusher != nil { + c.flusher.Flush() + } + return n, err +} + +func (c *httpServerConn) Close() error { + c.once.Do(func() { + c.mu.Lock() + c.closed = true + c.mu.Unlock() + close(c.done) + }) + return nil +} + +func (c *httpServerConn) Wait() <-chan struct{} { + return c.done +} + +type httpSession struct { + uploadQueue *uploadQueue + connected chan struct{} + once sync.Once +} + +func newHTTPSession() *httpSession { + return &httpSession{ + uploadQueue: NewUploadQueue(), + connected: make(chan struct{}), + } +} + +func (s *httpSession) markConnected() { + s.once.Do(func() { + close(s.connected) + }) +} + +type requestHandler struct { + path string + host string + mode string + connHandler func(net.Conn) + httpHandler http.Handler + + mu sync.Mutex + sessions map[string]*httpSession +} + +func NewServerHandler(opt ServerOption) http.Handler { + path := opt.Path + if path == "" { + path = "/" + } + if !strings.HasPrefix(path, "/") { + path = "/" + path + } + if !strings.HasSuffix(path, "/") { + path += "/" + } + + // using h2c.NewHandler to ensure we can work in plain http2 + // and some tls conn is not *tls.Conn (like *reality.Conn) + return h2c.NewHandler(&requestHandler{ + path: path, + host: opt.Host, + mode: opt.Mode, + connHandler: opt.ConnHandler, + httpHandler: opt.HttpHandler, + sessions: map[string]*httpSession{}, + }, &http.Http2Server{ + IdleTimeout: 30 * time.Second, + }) +} + +func (h *requestHandler) getOrCreateSession(sessionID string) *httpSession { + h.mu.Lock() + defer h.mu.Unlock() + + s, ok := h.sessions[sessionID] + if ok { + return s + } + + s = newHTTPSession() + h.sessions[sessionID] = s + return s +} + +func (h *requestHandler) deleteSession(sessionID string) { + h.mu.Lock() + defer h.mu.Unlock() + + if s, ok := h.sessions[sessionID]; ok { + _ = s.uploadQueue.Close() + delete(h.sessions, sessionID) + } +} + +func (h *requestHandler) getSession(sessionID string) *httpSession { + h.mu.Lock() + defer h.mu.Unlock() + return h.sessions[sessionID] +} + +func (h *requestHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if h.httpHandler != nil && !strings.HasPrefix(r.URL.Path, h.path) { + h.httpHandler.ServeHTTP(w, r) + return + } + + if h.host != "" && !equalHost(r.Host, h.host) { + http.NotFound(w, r) + return + } + + if !strings.HasPrefix(r.URL.Path, h.path) { + http.NotFound(w, r) + return + } + + rest := strings.TrimPrefix(r.URL.Path, h.path) + parts := splitNonEmpty(rest) + + // stream-one: POST /path + if r.Method == http.MethodPost && len(parts) == 0 { + w.Header().Set("X-Accel-Buffering", "no") + w.Header().Set("Cache-Control", "no-store") + w.WriteHeader(http.StatusOK) + if flusher, ok := w.(http.Flusher); ok { + flusher.Flush() + } + + httpSC := newHTTPServerConn(w, r.Body) + conn := &Conn{ + writer: httpSC, + reader: httpSC, + } + conn.SetAddrFromRequest(r) + + go h.connHandler(N.NewDeadlineConn(conn)) + + select { + case <-r.Context().Done(): + case <-httpSC.Wait(): + } + + _ = conn.Close() + return + } + + // packet-up download: GET /path/{session} + if r.Method == http.MethodGet && len(parts) == 1 { + sessionID := parts[0] + session := h.getOrCreateSession(sessionID) + session.markConnected() + + w.Header().Set("X-Accel-Buffering", "no") + w.Header().Set("Cache-Control", "no-store") + w.WriteHeader(http.StatusOK) + if flusher, ok := w.(http.Flusher); ok { + flusher.Flush() + } + + httpSC := newHTTPServerConn(w, r.Body) + conn := &Conn{ + writer: httpSC, + reader: session.uploadQueue, + onClose: func() { + h.deleteSession(sessionID) + }, + } + conn.SetAddrFromRequest(r) + + go h.connHandler(N.NewDeadlineConn(conn)) + + select { + case <-r.Context().Done(): + case <-httpSC.Wait(): + } + + _ = conn.Close() + return + } + + // packet-up upload: POST /path/{session}/{seq} + if r.Method == http.MethodPost && len(parts) == 2 { + sessionID := parts[0] + seq, err := strconv.ParseUint(parts[1], 10, 64) + if err != nil { + http.Error(w, "invalid xhttp seq", http.StatusBadRequest) + return + } + + session := h.getSession(sessionID) + if session == nil { + http.Error(w, "unknown xhttp session", http.StatusBadRequest) + return + } + + body, err := io.ReadAll(r.Body) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + if err := session.uploadQueue.Push(Packet{ + Seq: seq, + Payload: body, + }); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + if len(body) == 0 { + w.Header().Set("Cache-Control", "no-store") + } + w.WriteHeader(http.StatusOK) + return + } + + http.NotFound(w, r) +} + +func splitNonEmpty(s string) []string { + raw := strings.Split(s, "/") + out := make([]string, 0, len(raw)) + for _, v := range raw { + if v != "" { + out = append(out, v) + } + } + return out +} + +func equalHost(a, b string) bool { + a = strings.ToLower(a) + b = strings.ToLower(b) + + if ah, _, err := net.SplitHostPort(a); err == nil { + a = ah + } + if bh, _, err := net.SplitHostPort(b); err == nil { + b = bh + } + + return a == b +} diff --git a/clash-meta/transport/xhttp/upload_queue.go b/clash-meta/transport/xhttp/upload_queue.go new file mode 100644 index 0000000000..bb9cbc4927 --- /dev/null +++ b/clash-meta/transport/xhttp/upload_queue.go @@ -0,0 +1,78 @@ +package xhttp + +import ( + "io" + "sync" +) + +type Packet struct { + Seq uint64 + Payload []byte +} + +type uploadQueue struct { + mu sync.Mutex + cond *sync.Cond + packets map[uint64][]byte + nextSeq uint64 + buf []byte + closed bool +} + +func NewUploadQueue() *uploadQueue { + q := &uploadQueue{ + packets: make(map[uint64][]byte), + } + q.cond = sync.NewCond(&q.mu) + return q +} + +func (q *uploadQueue) Push(p Packet) error { + q.mu.Lock() + defer q.mu.Unlock() + + if q.closed { + return io.ErrClosedPipe + } + + cp := make([]byte, len(p.Payload)) + copy(cp, p.Payload) + q.packets[p.Seq] = cp + q.cond.Broadcast() + return nil +} + +func (q *uploadQueue) Read(b []byte) (int, error) { + q.mu.Lock() + defer q.mu.Unlock() + + for { + if len(q.buf) > 0 { + n := copy(b, q.buf) + q.buf = q.buf[n:] + return n, nil + } + + if payload, ok := q.packets[q.nextSeq]; ok { + delete(q.packets, q.nextSeq) + q.nextSeq++ + q.buf = payload + continue + } + + if q.closed { + return 0, io.EOF + } + + q.cond.Wait() + } +} + +func (q *uploadQueue) Close() error { + q.mu.Lock() + defer q.mu.Unlock() + + q.closed = true + q.cond.Broadcast() + return nil +} diff --git a/clash-nyanpasu/.vscode/settings.json b/clash-nyanpasu/.vscode/settings.json index 2ef1001932..7eaef6c9e2 100644 --- a/clash-nyanpasu/.vscode/settings.json +++ b/clash-nyanpasu/.vscode/settings.json @@ -1,7 +1,11 @@ { "tailwindCSS.experimental.classRegex": [ ["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"], - ["cx\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"] + ["cx\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"], + [ + "\\.attr\\(\\s*(?:'|\\\")class(?:'|\\\")\\s*,\\s*([^)]*)\\)", + "(?:'|\"|`)([^\"'`]*)(?:'|\"|`)" + ] ], "files.eol": "\n", "js/ts.tsdk.path": "node_modules\\typescript\\lib" diff --git a/clash-nyanpasu/backend/Cargo.lock b/clash-nyanpasu/backend/Cargo.lock index 8e4210ace6..98a12c6727 100644 --- a/clash-nyanpasu/backend/Cargo.lock +++ b/clash-nyanpasu/backend/Cargo.lock @@ -9690,7 +9690,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.4.1", + "getrandom 0.3.3", "once_cell", "rustix 1.1.4", "windows-sys 0.61.2", @@ -10699,9 +10699,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.22.0" +version = "1.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a68d3c8f01c0cfa54a75291d83601161799e4a89a39e0929f4b0354d88757a37" +checksum = "5ac8b6f42ead25368cf5b098aeb3dc8a1a2c05a3eee8a9a1a68c640edbfc79d9" dependencies = [ "getrandom 0.4.1", "js-sys", diff --git a/clash-nyanpasu/backend/tauri/src/config/nyanpasu/mod.rs b/clash-nyanpasu/backend/tauri/src/config/nyanpasu/mod.rs index 55cce4189e..3bc9785725 100644 --- a/clash-nyanpasu/backend/tauri/src/config/nyanpasu/mod.rs +++ b/clash-nyanpasu/backend/tauri/src/config/nyanpasu/mod.rs @@ -149,6 +149,14 @@ pub enum BreakWhenProxyChange { All, } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize, Default, Type)] +#[serde(rename_all = "snake_case")] +pub enum WindowType { + Legacy, + #[default] + Main, +} + /// ### `verge.yaml` schema #[derive(Default, Debug, Clone, Deserialize, Serialize, VergePatch, specta::Type)] #[verge(patch_fn = "patch_config")] @@ -297,9 +305,9 @@ pub struct IVerge { /// When disabled, only shows status via icon changes (prevents text display issues on Wayland) pub enable_tray_text: Option, - /// Use legacy UI (original UI at "/" route) - /// When true, opens legacy window; when false, opens new main window - pub use_legacy_ui: Option, + /// Window type to use when opening the app window + /// Legacy: opens legacy window; Main: opens new main window + pub window_type: Option, } #[derive(Default, Debug, Clone, Deserialize, Serialize, Type)] @@ -382,8 +390,8 @@ impl IVerge { config.enable_tray_text = template.enable_tray_text; } - if config.use_legacy_ui.is_none() { - config.use_legacy_ui = template.use_legacy_ui; + if config.window_type.is_none() { + config.window_type = template.window_type; } config @@ -421,7 +429,7 @@ impl IVerge { enable_service_mode: Some(false), always_on_top: Some(false), enable_tray_text: Some(false), - use_legacy_ui: Some(true), + window_type: Some(WindowType::Main), ..Self::default() } } diff --git a/clash-nyanpasu/backend/tauri/src/ipc.rs b/clash-nyanpasu/backend/tauri/src/ipc.rs index a7c9651c8a..8cf7f7b3da 100644 --- a/clash-nyanpasu/backend/tauri/src/ipc.rs +++ b/clash-nyanpasu/backend/tauri/src/ipc.rs @@ -100,11 +100,11 @@ pub fn is_portable() -> Result { Ok(false) } -#[tauri::command] -#[specta::specta] -pub fn get_device_info() -> Result { - Ok(crate::utils::hwid::get_device_info()) -} +// #[tauri::command] +// #[specta::specta] +// pub fn get_device_info() -> Result { +// Ok(crate::utils::hwid::get_device_info()) +// } #[tauri::command] #[specta::specta] diff --git a/clash-nyanpasu/backend/tauri/src/lib.rs b/clash-nyanpasu/backend/tauri/src/lib.rs index 044ec66687..9b4235e0d1 100644 --- a/clash-nyanpasu/backend/tauri/src/lib.rs +++ b/clash-nyanpasu/backend/tauri/src/lib.rs @@ -249,7 +249,7 @@ pub fn run() -> std::io::Result<()> { ipc::service::stop_service, ipc::service::restart_service, ipc::is_portable, - ipc::get_device_info, + // ipc::get_device_info, ipc::get_proxies, ipc::select_proxy, ipc::update_proxy_provider, diff --git a/clash-nyanpasu/backend/tauri/src/utils/resolve.rs b/clash-nyanpasu/backend/tauri/src/utils/resolve.rs index a67f4dd42c..0ea91976f9 100644 --- a/clash-nyanpasu/backend/tauri/src/utils/resolve.rs +++ b/clash-nyanpasu/backend/tauri/src/utils/resolve.rs @@ -1,7 +1,7 @@ use crate::{ config::{ Config, IVerge, - nyanpasu::{ClashCore, WindowState}, + nyanpasu::{ClashCore, WindowState, WindowType}, }, core::{storage::Storage, tray::proxies, *}, log_err, @@ -372,49 +372,57 @@ pub fn save_legacy_window_state(app_handle: &AppHandle, save_to_file: bool) -> R LegacyWindow.save_state(app_handle, save_to_file) } -/// Create window based on use_legacy_ui config +/// Create window based on window_type config /// This is the primary function to use when opening window from tray, etc. #[tracing_attributes::instrument(skip(app_handle))] pub fn create_window(app_handle: &AppHandle) { - let use_legacy = Config::verge().latest().use_legacy_ui.unwrap_or(true); + let window_type = Config::verge() + .latest() + .window_type + .unwrap_or(WindowType::Main); - if use_legacy { - create_legacy_window(app_handle); - } else { - create_main_window(app_handle); + match window_type { + WindowType::Legacy => create_legacy_window(app_handle), + WindowType::Main => create_main_window(app_handle), } } -/// Close the currently active window based on use_legacy_ui config +/// Close the currently active window based on window_type config pub fn close_window(app_handle: &AppHandle) { - let use_legacy = Config::verge().latest().use_legacy_ui.unwrap_or(true); + let window_type = Config::verge() + .latest() + .window_type + .unwrap_or(WindowType::Main); - if use_legacy { - close_legacy_window(app_handle); - } else { - close_main_window(app_handle); + match window_type { + WindowType::Legacy => close_legacy_window(app_handle), + WindowType::Main => close_main_window(app_handle), } } /// Check if the configured window is open pub fn is_window_open(app_handle: &AppHandle) -> bool { - let use_legacy = Config::verge().latest().use_legacy_ui.unwrap_or(true); + let window_type = Config::verge() + .latest() + .window_type + .unwrap_or(WindowType::Main); - if use_legacy { - is_legacy_window_open(app_handle) - } else { - is_main_window_open(app_handle) + match window_type { + WindowType::Legacy => is_legacy_window_open(app_handle), + WindowType::Main => is_main_window_open(app_handle), } } /// Save window state for the configured window type pub fn save_window_state(app_handle: &AppHandle, save_to_file: bool) -> Result<()> { - let use_legacy = Config::verge().latest().use_legacy_ui.unwrap_or(true); + let window_type = Config::verge() + .latest() + .window_type + .unwrap_or(WindowType::Main); - if use_legacy { - save_legacy_window_state(app_handle, save_to_file) - } else { - save_main_window_state(app_handle, save_to_file) + match window_type { + WindowType::Legacy => save_legacy_window_state(app_handle, save_to_file), + WindowType::Main => save_main_window_state(app_handle, save_to_file), } } diff --git a/clash-nyanpasu/frontend/interface/src/hooks/index.ts b/clash-nyanpasu/frontend/interface/src/hooks/index.ts index eeb4a8c327..25d39ab865 100644 --- a/clash-nyanpasu/frontend/interface/src/hooks/index.ts +++ b/clash-nyanpasu/frontend/interface/src/hooks/index.ts @@ -1 +1,2 @@ +export * from './use-core-status' export * from './use-kv-storage' diff --git a/clash-nyanpasu/frontend/interface/src/hooks/use-core-status.ts b/clash-nyanpasu/frontend/interface/src/hooks/use-core-status.ts new file mode 100644 index 0000000000..68354ce356 --- /dev/null +++ b/clash-nyanpasu/frontend/interface/src/hooks/use-core-status.ts @@ -0,0 +1,29 @@ +import { commands } from '@/ipc' +import { CLASH_CORE_STATUS_QUERY_KEY } from '@/ipc/consts' +import { unwrapResult } from '@/utils' +import { useQuery } from '@tanstack/react-query' + +export function useCoreStatus() { + const query = useQuery({ + queryKey: [CLASH_CORE_STATUS_QUERY_KEY], + queryFn: async () => { + const res = await commands.getCoreStatus() + + const result = unwrapResult(res) + + if (!result) { + return null + } + + const [status, startAt, type] = result + + return { + status, + startAt, + type, + } + }, + }) + + return query +} diff --git a/clash-nyanpasu/frontend/interface/src/hooks/use-kv-storage.ts b/clash-nyanpasu/frontend/interface/src/hooks/use-kv-storage.ts index c24a95a0da..cc39355e88 100644 --- a/clash-nyanpasu/frontend/interface/src/hooks/use-kv-storage.ts +++ b/clash-nyanpasu/frontend/interface/src/hooks/use-kv-storage.ts @@ -75,6 +75,11 @@ export function useKvStorage( const migrateRef = useRef(options?.migrate) migrateRef.current = options?.migrate + // Track pending writes so the echo event from the backend doesn't cause a + // redundant re-render. The set stores the serialized JSON of each in-flight + // write; when the confirming event arrives we just discard it. + const pendingWritesRef = useRef>(new Set()) + const applyMigrate = useCallback((raw: unknown): T => { return migrateRef.current ? migrateRef.current(raw) : (raw as T) }, []) @@ -111,11 +116,31 @@ export function useKvStorage( } if (event.payload.value === null) { + pendingWritesRef.current.delete('null') setValueState(defaultValueRef.current) removeLocalCache(key) } else { + // If this event is the echo of our own optimistic write, skip the + // redundant setState to avoid an unnecessary re-render. + // Note: the backend double-encodes the value in the event payload + // (the stored JSON string is wrapped in another JSON string), so we + // compare against the double-encoded form. + if (pendingWritesRef.current.has(event.payload.value)) { + pendingWritesRef.current.delete(event.payload.value) + return + } + try { - const parsed = JSON.parse(event.payload.value) + // The backend emits the stored value double-encoded: the raw stored + // string (already valid JSON) is JSON-encoded again inside the event + // payload. Parse once to get the inner string, then parse again to + // get the actual value. Fall back to single-parse for backends that + // emit the value without extra encoding. + const firstParsed = JSON.parse(event.payload.value) + const parsed = + typeof firstParsed === 'string' + ? JSON.parse(firstParsed) + : firstParsed const migrated = applyMigrate(parsed) setValueState(migrated) @@ -138,14 +163,18 @@ export function useKvStorage( ? (newValue as (prev: T) => T)(valueRef.current) : newValue + const serialized = JSON.stringify(resolved) + + // Register this write so the confirming event can be suppressed. + // The backend double-encodes the value in the event, so we store the + // double-encoded form to match what the event listener will receive. + pendingWritesRef.current.add(JSON.stringify(serialized)) + // Optimistic update — the backend event will also arrive and confirm setValueState(resolved) setLocalCache(key, resolved) - const result = await commands.setStorageItem( - key, - JSON.stringify(resolved), - ) + const result = await commands.setStorageItem(key, serialized) if (result.status === 'error') { console.error('[useKvStorage] setStorageItem failed:', result.error) diff --git a/clash-nyanpasu/frontend/interface/src/ipc/bindings.ts b/clash-nyanpasu/frontend/interface/src/ipc/bindings.ts index 7aa5c890ad..b7710885de 100644 --- a/clash-nyanpasu/frontend/interface/src/ipc/bindings.ts +++ b/clash-nyanpasu/frontend/interface/src/ipc/bindings.ts @@ -1094,10 +1094,10 @@ export type IVerge = { */ enable_tray_text: boolean | null /** - * Use legacy UI (original UI at "/" route) - * When true, opens legacy window; when false, opens new main window + * Window type to use when opening the app window + * Legacy: opens legacy window; Main: opens new main window */ - use_legacy_ui: boolean | null + window_type: WindowType | null } export type JsonValue = | null @@ -1640,6 +1640,7 @@ export type WindowState = { maximized: boolean fullscreen: boolean } +export type WindowType = 'legacy' | 'main' type __EventObj__ = { listen: ( diff --git a/clash-nyanpasu/frontend/interface/src/ipc/consts.ts b/clash-nyanpasu/frontend/interface/src/ipc/consts.ts index 0e1ce12c8f..9f2e12b3fa 100644 --- a/clash-nyanpasu/frontend/interface/src/ipc/consts.ts +++ b/clash-nyanpasu/frontend/interface/src/ipc/consts.ts @@ -110,6 +110,11 @@ export const CLASH_RULES_PROVIDER_QUERY_KEY = 'clash-rules-provider' */ export const CLASH_PROXIES_PROVIDER_QUERY_KEY = 'clash-proxies-provider' +/** + * Clash core status query key, used by useCoreStatus hook to fetch core status from query + */ +export const CLASH_CORE_STATUS_QUERY_KEY = 'clash-core-status' + /** * Maximum connections history length, used by clash ws provider to limit connections history length */ diff --git a/clash-nyanpasu/frontend/nyanpasu/messages/en.json b/clash-nyanpasu/frontend/nyanpasu/messages/en.json index 2d4d0a2a42..0378ab7801 100644 --- a/clash-nyanpasu/frontend/nyanpasu/messages/en.json +++ b/clash-nyanpasu/frontend/nyanpasu/messages/en.json @@ -203,6 +203,31 @@ "providers_info_title": "Resource Info", "providers_subscription_title": "Subscription Info", "providers_update_provider": "Update", + "dashboard_context_menu_edit_widgets": "Edit Widgets", + "dashboard_context_menu_add_widgets": "Add Widgets", + "dashboard_widget_traffic_upload": "Upload", + "dashboard_widget_traffic_download": "Download", + "dashboard_widget_traffic_total": "Total: {value}", + "dashboard_widget_connections": "Connections", + "dashboard_widget_memory": "Memory Usage", + "dashboard_widget_proxy_status": "Proxy Status", + "dashboard_widget_proxy_status_success_system": "Sys. Proxy", + "dashboard_widget_proxy_status_success_tun": "TUN Ethernet", + "dashboard_widget_proxy_status_occupied": "Used by Other Programs", + "dashboard_widget_proxy_status_disabled": "Disabled", + "dashboard_widget_core_status": "Core Status", + "dashboard_widget_core_status_running": "Running", + "dashboard_widget_core_status_stopped": "Stopped", + "dashboard_widget_core_status_running_by_service": "Running by Service", + "dashboard_widget_core_status_running_by_child_process": "Running by Child Process", + "dashboard_widget_core_stopped_by_service_with_message": "Core stopped by service: {message}", + "dashboard_widget_core_stopped_by_service_unknown": "Core stopped by service", + "dashboard_widget_core_stopped_with_message": "Stopped: {message}", + "dashboard_widget_core_stopped_unknown": "Core Stopped", + "dashboard_widget_core_service_running": "Service Running", + "dashboard_widget_core_service_stopped": "Service Stopped", + "dashboard_widget_core_service_not_installed": "Service Not Installed", + "dashboard_add_widget": "Add Widget", "editor_before_close_message": "You have not saved the edited content, are you sure you want to close the editor?", "editor_validate_error_message": "Please fix the error before saving content", "editor_read_only_chip": "Read Only", diff --git a/clash-nyanpasu/frontend/nyanpasu/messages/ru.json b/clash-nyanpasu/frontend/nyanpasu/messages/ru.json index 83f2b17257..a1b9f31c08 100644 --- a/clash-nyanpasu/frontend/nyanpasu/messages/ru.json +++ b/clash-nyanpasu/frontend/nyanpasu/messages/ru.json @@ -203,6 +203,31 @@ "providers_info_title": "Информация о ресурсах", "providers_subscription_title": "Информация о подписке", "providers_update_provider": "Обновить ресурсы", + "dashboard_context_menu_edit_widgets": "Редактировать компоненты", + "dashboard_context_menu_add_widgets": "Добавить компоненты", + "dashboard_widget_traffic_download": "Загрузка трафика", + "dashboard_widget_traffic_upload": "Отправка трафика", + "dashboard_widget_traffic_total": "Всего: {value}", + "dashboard_widget_connections": "Активные соединения", + "dashboard_widget_memory": "Использование памяти", + "dashboard_widget_proxy_status": "Статус прокси", + "dashboard_widget_proxy_status_success_system": "Системный прокси", + "dashboard_widget_proxy_status_success_tun": "TUN режим", + "dashboard_widget_proxy_status_occupied": "Используется другими программами", + "dashboard_widget_proxy_status_disabled": "Отключено", + "dashboard_widget_core_status": "Статус ядра", + "dashboard_widget_core_status_running": "Запущено", + "dashboard_widget_core_status_stopped": "Остановлено", + "dashboard_widget_core_status_running_by_service": "Запущено через сервис", + "dashboard_widget_core_status_running_by_child_process": "Запущено через дочерний процесс", + "dashboard_widget_core_stopped_by_service_with_message": "Ядро остановлено сервисом: {message}", + "dashboard_widget_core_stopped_by_service_unknown": "Ядро остановлено сервисом", + "dashboard_widget_core_stopped_with_message": "Остановлено: {message}", + "dashboard_widget_core_stopped_unknown": "Ядро остановлено", + "dashboard_widget_core_service_running": "Сервис запущен", + "dashboard_widget_core_service_stopped": "Сервис остановлен", + "dashboard_widget_core_service_not_installed": "Сервис не установлен", + "dashboard_add_widget": "Добавить компонент", "editor_before_close_message": "Вы не сохранили измененное содержимое, вы уверены, что хотите закрыть редактор?", "editor_validate_error_message": "Пожалуйста, исправьте ошибки перед сохранением содержимого", "editor_read_only_chip": "Только для чтения", diff --git a/clash-nyanpasu/frontend/nyanpasu/messages/zh-cn.json b/clash-nyanpasu/frontend/nyanpasu/messages/zh-cn.json index 380a48e30a..f87c14d49f 100644 --- a/clash-nyanpasu/frontend/nyanpasu/messages/zh-cn.json +++ b/clash-nyanpasu/frontend/nyanpasu/messages/zh-cn.json @@ -203,6 +203,31 @@ "providers_info_title": "资源信息", "providers_subscription_title": "订阅信息", "providers_update_provider": "更新资源", + "dashboard_context_menu_edit_widgets": "编辑组件", + "dashboard_context_menu_add_widgets": "添加组件", + "dashboard_widget_traffic_download": "下载流量", + "dashboard_widget_traffic_upload": "上传流量", + "dashboard_widget_traffic_total": "总量:{value}", + "dashboard_widget_connections": "活动连接", + "dashboard_widget_memory": "内存占用", + "dashboard_widget_proxy_status": "代理状态", + "dashboard_widget_proxy_status_success_system": "系统代理", + "dashboard_widget_proxy_status_success_tun": "TUN 模式", + "dashboard_widget_proxy_status_occupied": "其他程序占用", + "dashboard_widget_proxy_status_disabled": "已禁用", + "dashboard_widget_core_status": "内核状态", + "dashboard_widget_core_status_running": "运行中", + "dashboard_widget_core_status_stopped": "已停止", + "dashboard_widget_core_status_running_by_service": "服务守护运行", + "dashboard_widget_core_status_running_by_child_process": "子进程守护运行", + "dashboard_widget_core_stopped_by_service_with_message": "内核在服务上停止:{message}", + "dashboard_widget_core_stopped_by_service_unknown": "内核在服务上停止", + "dashboard_widget_core_stopped_with_message": "内核停止:{message}", + "dashboard_widget_core_stopped_unknown": "内核已停止", + "dashboard_widget_core_service_running": "服务已启动", + "dashboard_widget_core_service_stopped": "服务已停止", + "dashboard_widget_core_service_not_installed": "服务未安装", + "dashboard_add_widget": "添加组件", "editor_before_close_message": "你尚未保存编辑的内容,确定要关闭编辑器吗?", "editor_validate_error_message": "请修复错误后再保存内容", "editor_read_only_chip": "只读", diff --git a/clash-nyanpasu/frontend/nyanpasu/messages/zh-tw.json b/clash-nyanpasu/frontend/nyanpasu/messages/zh-tw.json index 60b86f9b42..992e606e6b 100644 --- a/clash-nyanpasu/frontend/nyanpasu/messages/zh-tw.json +++ b/clash-nyanpasu/frontend/nyanpasu/messages/zh-tw.json @@ -203,6 +203,31 @@ "providers_info_title": "資源信息", "providers_subscription_title": "訂閱信息", "providers_update_provider": "更新資源", + "dashboard_context_menu_edit_widgets": "編輯組件", + "dashboard_context_menu_add_widgets": "添加組件", + "dashboard_widget_traffic_download": "下載流量", + "dashboard_widget_traffic_upload": "上傳流量", + "dashboard_widget_traffic_total": "總量:{value}", + "dashboard_widget_connections": "活動連接", + "dashboard_widget_memory": "記憶體占用", + "dashboard_widget_proxy_status": "代理狀態", + "dashboard_widget_proxy_status_success_system": "系統代理", + "dashboard_widget_proxy_status_success_tun": "TUN 模式", + "dashboard_widget_proxy_status_occupied": "其他程式佔用", + "dashboard_widget_proxy_status_disabled": "已禁用", + "dashboard_widget_core_status": "內核狀態", + "dashboard_widget_core_status_running": "運行中", + "dashboard_widget_core_status_stopped": "已停止", + "dashboard_widget_core_status_running_by_service": "服務守護運行", + "dashboard_widget_core_status_running_by_child_process": "子進程守護運行", + "dashboard_widget_core_stopped_by_service_with_message": "內核在服務上停止:{message}", + "dashboard_widget_core_stopped_by_service_unknown": "內核在服務上停止", + "dashboard_widget_core_stopped_with_message": "內核停止:{message}", + "dashboard_widget_core_stopped_unknown": "內核已停止", + "dashboard_widget_core_service_running": "服務已啟動", + "dashboard_widget_core_service_stopped": "服務已停止", + "dashboard_widget_core_service_not_installed": "服務未安裝", + "dashboard_add_widget": "添加組件", "editor_before_close_message": "你尚未儲存編輯的內容,確定要關閉編輯器嗎?", "editor_validate_error_message": "請修正錯誤後再儲存內容", "editor_read_only_chip": "只讀", diff --git a/clash-nyanpasu/frontend/nyanpasu/package.json b/clash-nyanpasu/frontend/nyanpasu/package.json index 39117cd93b..73e659f5c9 100644 --- a/clash-nyanpasu/frontend/nyanpasu/package.json +++ b/clash-nyanpasu/frontend/nyanpasu/package.json @@ -33,7 +33,6 @@ "@tanstack/react-virtual": "3.13.23", "@tanstack/router-zod-adapter": "1.81.5", "@tauri-apps/api": "2.10.1", - "@types/json-schema": "7.0.15", "@uidotdev/usehooks": "2.4.1", "@uiw/react-color": "2.9.6", "ahooks": "3.9.7", @@ -41,9 +40,10 @@ "class-variance-authority": "0.7.1", "country-code-emoji": "2.3.0", "country-emoji": "1.5.6", + "d3": "7.9.0", "dayjs": "1.11.20", "framer-motion": "12.38.0", - "i18next": "25.10.9", + "i18next": "25.10.10", "jotai": "2.19.0", "json-schema": "0.4.0", "material-react-table": "3.2.1", @@ -62,6 +62,7 @@ "react-use": "17.6.0", "rxjs": "7.8.2", "swr": "2.4.1", + "vaul": "1.1.2", "virtua": "0.46.6", "vite-bundle-visualizer": "1.2.1" }, @@ -83,6 +84,9 @@ "@tauri-apps/plugin-process": "2.3.1", "@tauri-apps/plugin-shell": "2.3.5", "@tauri-apps/plugin-updater": "2.10.0", + "@types/d3": "7.4.3", + "@types/d3-interpolate-path": "2.0.3", + "@types/json-schema": "7.0.15", "@types/react": "19.2.14", "@types/react-dom": "19.2.3", "@types/validator": "13.15.10", @@ -103,7 +107,7 @@ "validator": "13.15.26", "vite": "7.3.1", "vite-plugin-html": "3.2.2", - "vite-plugin-sass-dts": "1.3.35", + "vite-plugin-sass-dts": "1.3.37", "vite-plugin-svgr": "4.5.0", "vite-tsconfig-paths": "6.1.1", "zod": "4.3.6" diff --git a/clash-nyanpasu/frontend/nyanpasu/src/components/setting/setting-nyanpasu-ui.tsx b/clash-nyanpasu/frontend/nyanpasu/src/components/setting/setting-nyanpasu-ui.tsx index 0d2c0225e3..35f6ba59ed 100644 --- a/clash-nyanpasu/frontend/nyanpasu/src/components/setting/setting-nyanpasu-ui.tsx +++ b/clash-nyanpasu/frontend/nyanpasu/src/components/setting/setting-nyanpasu-ui.tsx @@ -111,10 +111,10 @@ const ThemeColor = () => { } const ExperimentalSwitch = () => { - const { upsert } = useSetting('use_legacy_ui') + const { upsert } = useSetting('window_type') const handleClick = useLockFn(async () => { - await upsert(false) + await upsert('main') await commands.createMainWindow() await currentWindow.close() }) diff --git a/clash-nyanpasu/frontend/nyanpasu/src/components/settings/system-proxy.tsx b/clash-nyanpasu/frontend/nyanpasu/src/components/settings/system-proxy.tsx index 60fbe32037..6a5cb65a31 100644 --- a/clash-nyanpasu/frontend/nyanpasu/src/components/settings/system-proxy.tsx +++ b/clash-nyanpasu/frontend/nyanpasu/src/components/settings/system-proxy.tsx @@ -19,7 +19,7 @@ const ProxyButton = ({ return ( + + + + )} + + ) +} diff --git a/clash-nyanpasu/frontend/nyanpasu/src/pages/(main)/main/dashboard/_modules/layout-adapt.ts b/clash-nyanpasu/frontend/nyanpasu/src/pages/(main)/main/dashboard/_modules/layout-adapt.ts new file mode 100644 index 0000000000..1b3dd70406 --- /dev/null +++ b/clash-nyanpasu/frontend/nyanpasu/src/pages/(main)/main/dashboard/_modules/layout-adapt.ts @@ -0,0 +1,176 @@ +import type { + DndGridItemType, + GridItemConstraints, + GridSize, +} from '@/components/ui/dnd-grid' +import { isOverlap } from '@/components/ui/dnd-grid/utils' + +export function sizeKey(size: GridSize): string { + return `${size.cols}x${size.rows}` +} + +/** + * Find the best stored layout for a given grid size. + * Scans all stored layouts whose dimensions fit within `size` and returns the + * one with the largest area (closest match). Returns null if none found. + */ +export function findBestLayout>( + storage: Record, + size: GridSize, +): T[] | null { + let best: { area: number; items: T[] } | null = null + + for (const [key, items] of Object.entries(storage)) { + const match = key.match(/^(\d+)x(\d+)$/) + if (!match) continue + + const cols = parseInt(match[1], 10) + const rows = parseInt(match[2], 10) + + if (cols <= size.cols && rows <= size.rows) { + const area = cols * rows + + if (!best || area > best.area) { + best = { area, items } + } + } + } + + return best?.items ?? null +} + +/** + * When no layout fits within `size`, find the stored layout whose dimensions + * are closest (Manhattan distance on cols/rows) to use as an adaptation base. + * Returns null if storage is empty. + */ +export function findClosestStoredLayout>( + storage: Record, + size: GridSize, +): T[] | null { + let best: { dist: number; items: T[] } | null = null + + for (const [key, items] of Object.entries(storage)) { + const match = key.match(/^(\d+)x(\d+)$/) + if (!match) continue + + const cols = parseInt(match[1], 10) + const rows = parseInt(match[2], 10) + const dist = Math.abs(cols - size.cols) + Math.abs(rows - size.rows) + + if (!best || dist < best.dist) { + best = { dist, items } + } + } + + return best?.items ?? null +} + +function hasOverlapWith>( + placed: T[], + candidate: T, +): boolean { + return placed.some((p) => p.id !== candidate.id && isOverlap(p, candidate)) +} + +/** Scan top-to-bottom, left-to-right for the first free slot of size (w × h). */ +function tryPlace>( + item: T, + w: number, + h: number, + placed: T[], + cols: number, + rows: number, +): T | null { + for (let y = 0; y + h <= rows; y++) { + for (let x = 0; x + w <= cols; x++) { + const candidate = { ...item, x, y, w, h } as T + + if (!hasOverlapWith(placed, candidate)) { + return candidate + } + } + } + return null +} + +/** + * Adapt `items` so they all fit within the new `size`. + * + * Priority per item: + * 1. Clamp (x,y) so the item stays in-bounds with its current (w,h). + * 2. If that position overlaps others, scan for a free slot at the same size. + * 3. If still no slot, progressively shrink (w,h) toward (minW,minH) and + * scan again. + * 4. If even the minimum size can't be placed, drop the item. + * + * Items that are already within bounds and overlap-free are left unchanged. + * Items are processed in reading order (top → bottom, left → right) so earlier + * items have priority over later ones. + */ +export function adaptLayout>( + items: T[], + size: GridSize, + constraints: Record, +): T[] { + const { cols, rows } = size + const result: T[] = [] + + const sorted = [...items].sort((a, b) => + a.y !== b.y ? a.y - b.y : a.x - b.x, + ) + + for (const item of sorted) { + const c = constraints[item.id] ?? {} + const minW = c.minW ?? 1 + const minH = c.minH ?? 1 + + // Can't fit even at minimum size — drop. + if (minW > cols || minH > rows) continue + + // Clamp dimensions to [minW..cols] and [minH..rows]. + const w = Math.max(minW, Math.min(item.w, cols)) + const h = Math.max(minH, Math.min(item.h, rows)) + // Clamp position so the item stays fully in-bounds. + const x = Math.max(0, Math.min(item.x, cols - w)) + const y = Math.max(0, Math.min(item.y, rows - h)) + + const clamped = { ...item, x, y, w, h } as T + + // Step 1: try at clamped position (no overlap). + if (!hasOverlapWith(result, clamped)) { + result.push(clamped) + continue + } + + // Step 2: find a free slot at current (w, h). + const placed = tryPlace(item, w, h, result, cols, rows) + if (placed) { + result.push(placed) + continue + } + + // Step 3: shrink (w, h) toward (minW, minH) and retry. + const findShrinkPlacement = (): T | null => { + for (let tw = w; tw >= minW; tw--) { + for (let th = h; th >= minH; th--) { + if (tw === w && th === h) continue // already tried above + + const p = tryPlace(item, tw, th, result, cols, rows) + + if (p) { + return p + } + } + } + + return null + } + + const found = findShrinkPlacement() + if (found) result.push(found) + // else: drop the item. + } + + return result +} diff --git a/clash-nyanpasu/frontend/nyanpasu/src/pages/(main)/main/dashboard/_modules/provider.tsx b/clash-nyanpasu/frontend/nyanpasu/src/pages/(main)/main/dashboard/_modules/provider.tsx new file mode 100644 index 0000000000..ae360c8cdf --- /dev/null +++ b/clash-nyanpasu/frontend/nyanpasu/src/pages/(main)/main/dashboard/_modules/provider.tsx @@ -0,0 +1,39 @@ +import { createContext, PropsWithChildren, use, useState } from 'react' + +const DashboardContext = createContext<{ + openSheet: boolean + setOpenSheet: (open: boolean) => void + isEditing: boolean + setIsEditing: (editing: boolean) => void +} | null>(null) + +export const useDashboardContext = () => { + const context = use(DashboardContext) + + if (!context) { + throw new Error( + 'useDashboardContext must be used within a DashboardProvider', + ) + } + + return context +} + +export function DashboardProvider({ children }: PropsWithChildren) { + const [openSheet, setOpenSheet] = useState(false) + + const [isEditing, setIsEditing] = useState(false) + + return ( + + {children} + + ) +} diff --git a/clash-nyanpasu/frontend/nyanpasu/src/pages/(main)/main/dashboard/_modules/widget-item.tsx b/clash-nyanpasu/frontend/nyanpasu/src/pages/(main)/main/dashboard/_modules/widget-item.tsx new file mode 100644 index 0000000000..7adb5117f0 --- /dev/null +++ b/clash-nyanpasu/frontend/nyanpasu/src/pages/(main)/main/dashboard/_modules/widget-item.tsx @@ -0,0 +1,61 @@ +import CloseRounded from '~icons/material-symbols/close-rounded' +import { AnimatePresence, motion } from 'framer-motion' +import { Button } from '@/components/ui/button' +import { DndGridItem, DndGridItemProps } from '@/components/ui/dnd-grid' +import { useDndGridContext } from '@/components/ui/dnd-grid/context' +import { cn } from '@nyanpasu/ui' +import { WidgetComponentProps } from './consts' + +export type WidgetItemProps = DndGridItemProps & WidgetComponentProps + +export default function WidgetItem({ + children, + className, + onCloseClick, + ...props +}: WidgetItemProps) { + const { disabled, sourceOnly } = useDndGridContext() + + return ( + + {children} + + + {!disabled && !sourceOnly && ( + + )} + + + ) +} diff --git a/clash-nyanpasu/frontend/nyanpasu/src/pages/(main)/main/dashboard/_modules/widget-sheet.tsx b/clash-nyanpasu/frontend/nyanpasu/src/pages/(main)/main/dashboard/_modules/widget-sheet.tsx new file mode 100644 index 0000000000..48625e03c7 --- /dev/null +++ b/clash-nyanpasu/frontend/nyanpasu/src/pages/(main)/main/dashboard/_modules/widget-sheet.tsx @@ -0,0 +1,111 @@ +import CloseRounded from '~icons/material-symbols/close-rounded' +import { useMemo, useState } from 'react' +import { Drawer } from 'vaul' +import { Button } from '@/components/ui/button' +import { DndGrid, GridSize } from '@/components/ui/dnd-grid' +import { ScrollArea } from '@/components/ui/scroll-area' +import { m } from '@/paraglide/messages' +import { cn } from '@nyanpasu/ui' +import { RENDER_MAP, WIDGET_MIN_SIZE_MAP, WidgetId } from './consts' +import { useDashboardContext } from './provider' + +export function WidgetSheet({ + onSourceDrop, + onSourceDragStart, +}: { + onSourceDrop: (id: WidgetId) => void + onSourceDragStart: () => void +}) { + const { openSheet, setOpenSheet } = useDashboardContext() + + const [gridSize, setGridSize] = useState() + + const sheetItems = useMemo(() => { + if (!gridSize) { + return [] + } + + const ids = Object.keys(RENDER_MAP) as WidgetId[] + const result = [] + let rowX = 0 + let rowY = 0 + let rowH = 0 + + for (const id of ids) { + const { minW: w, minH: h } = WIDGET_MIN_SIZE_MAP[id] + if (rowX + w > gridSize.cols) { + rowY += rowH + rowX = 0 + rowH = 0 + } + result.push({ id, x: rowX, y: rowY, w, h }) + rowX += w + rowH = Math.max(rowH, h) + } + + return result + }, [gridSize]) + + return ( + + + + + +
+ + {m.dashboard_add_widget()} + + + + + +
+ + div]:block!', + '[&_[data-slot=scroll-area-viewport]>div]:h-full', + )} + > +
+ onSourceDrop(id as WidgetId)} + onSourceDragStart={onSourceDragStart} + onSizeChange={(size) => setGridSize(size)} + > + {(item) => { + const WidgetComponent = RENDER_MAP[item.id as WidgetId] + + return ( + {}} /> + ) + }} + +
+
+
+
+
+ ) +} diff --git a/clash-nyanpasu/frontend/nyanpasu/src/pages/(main)/main/dashboard/_modules/widget-shortcut.tsx b/clash-nyanpasu/frontend/nyanpasu/src/pages/(main)/main/dashboard/_modules/widget-shortcut.tsx new file mode 100644 index 0000000000..bc68808422 --- /dev/null +++ b/clash-nyanpasu/frontend/nyanpasu/src/pages/(main)/main/dashboard/_modules/widget-shortcut.tsx @@ -0,0 +1,296 @@ +import { useMemo } from 'react' +import { + SystemProxyButton, + TunModeButton, +} from '@/components/settings/system-proxy' +import { Button } from '@/components/ui/button' +import { Card, CardContent, CardHeader } from '@/components/ui/card' +import TextMarquee from '@/components/ui/text-marquee' +import useCoreIcon from '@/hooks/use-core-icon' +import { m } from '@/paraglide/messages' +import { + useClashConfig, + useClashCores, + useCoreStatus, + useSetting, + useSystemProxy, + useSystemService, +} from '@nyanpasu/interface' +import { cn } from '@nyanpasu/ui' +import { Link } from '@tanstack/react-router' +import { WidgetComponentProps } from './consts' +import WidgetItem from './widget-item' + +enum ProxyStatus { + SYSTEM = 'system', + TUN = 'tun', + OCCUPIED = 'occupied', + DISABLED = 'disabled', +} + +const ProxyTitleRow = () => { + const { value: enableSystemProxy } = useSetting('enable_system_proxy') + + const { value: enableTunMode } = useSetting('enable_tun_mode') + + const { data: systemProxyStatus } = useSystemProxy() + + const { + query: { data: clashConfigs }, + } = useClashConfig() + + const status = useMemo(() => { + if (enableTunMode) { + return ProxyStatus.TUN + } + + if (enableSystemProxy) { + if (systemProxyStatus?.enable) { + const port = Number(systemProxyStatus.server.split(':')[1]) + + if (port === clashConfigs?.['mixed-port']) { + return ProxyStatus.SYSTEM + } + + return ProxyStatus.OCCUPIED + } + } + + return ProxyStatus.DISABLED + }, [enableSystemProxy, enableTunMode, systemProxyStatus, clashConfigs]) + + const messages = { + [ProxyStatus.SYSTEM]: m.dashboard_widget_proxy_status_success_system(), + [ProxyStatus.TUN]: m.dashboard_widget_proxy_status_success_tun(), + [ProxyStatus.OCCUPIED]: m.dashboard_widget_proxy_status_occupied(), + [ProxyStatus.DISABLED]: m.dashboard_widget_proxy_status_disabled(), + } + + return ( + + + {m.dashboard_widget_proxy_status()} + + + + + ) +} + +export function ProxyShortcutsWidget({ + id, + onCloseClick, +}: WidgetComponentProps) { + return ( + + + + + + + + + + + + ) +} + +const CoreStatusBadge = () => { + const { + query: { data: serviceStatus }, + } = useSystemService() + + const { data: coreStatus } = useCoreStatus() + + const message = useMemo(() => { + // core is running, we check if it's running by service or by child process + if (coreStatus?.status === 'Running') { + if (serviceStatus?.server?.core_infos.state === 'Running') { + return m.dashboard_widget_core_status_running_by_service() + } else { + return m.dashboard_widget_core_status_running_by_child_process() + } + } + + let stopedMessage + let serviceMessage + + if (serviceStatus?.status === 'running') { + serviceMessage = m.dashboard_widget_core_service_running() + + // service returned core status, but it's not running, so it's stopped by service + if ( + serviceStatus?.server?.core_infos.state !== 'Running' && + serviceStatus?.server?.core_infos.state.Stopped + ) { + stopedMessage = m.dashboard_widget_core_stopped_by_service_with_message( + { + message: serviceStatus?.server.core_infos.state.Stopped, + }, + ) + } else { + stopedMessage = m.dashboard_widget_core_stopped_by_service_unknown() + } + } + + // service is not running, so core is either stopped by service or not installed + if (serviceStatus?.status === 'stopped') { + serviceMessage = m.dashboard_widget_core_service_stopped() + } else { + serviceMessage = m.dashboard_widget_core_service_not_installed() + } + + // core is stopped, but we don't know why, so we check the core status + if (coreStatus?.status.Stopped) { + stopedMessage = m.dashboard_widget_core_stopped_with_message({ + message: coreStatus.status.Stopped, + }) + } else { + stopedMessage = m.dashboard_widget_core_stopped_unknown() + } + + return `${stopedMessage} ${serviceMessage}` + }, [serviceStatus, coreStatus]) + + return ( +
+ + {message} + +
+ ) +} + +const CurrentCoreCard = () => { + const { query: clashCores } = useClashCores() + + const { value: currentCoreKey } = useSetting('clash_core') + + const currentCoreIcon = useCoreIcon(currentCoreKey) + + const currentCore = currentCoreKey && clashCores.data?.[currentCoreKey] + + const { data: coreStatus } = useCoreStatus() + + const isRunning = coreStatus?.status === 'Running' + + return ( + + ) +} + +export function CoreShortcutsWidget({ + id, + onCloseClick, +}: WidgetComponentProps) { + return ( + + + + + {m.dashboard_widget_core_status()} + + + + + + + + + + + ) +} diff --git a/clash-nyanpasu/frontend/nyanpasu/src/pages/(main)/main/dashboard/_modules/widget-sparkline.tsx b/clash-nyanpasu/frontend/nyanpasu/src/pages/(main)/main/dashboard/_modules/widget-sparkline.tsx new file mode 100644 index 0000000000..42c1628d8f --- /dev/null +++ b/clash-nyanpasu/frontend/nyanpasu/src/pages/(main)/main/dashboard/_modules/widget-sparkline.tsx @@ -0,0 +1,237 @@ +import ArrowDownwardRounded from '~icons/material-symbols/arrow-downward-rounded' +import ArrowUpwardRounded from '~icons/material-symbols/arrow-upward-rounded' +import MemoryOutlineRounded from '~icons/material-symbols/memory-outline-rounded' +import SettingsEthernetRounded from '~icons/material-symbols/settings-ethernet-rounded' +import { filesize } from 'filesize' +import { ComponentProps, ComponentType } from 'react' +import { Card, CardContent } from '@/components/ui/card' +import { Sparkline } from '@/components/ui/sparkline' +import TextMarquee from '@/components/ui/text-marquee' +import { m } from '@/paraglide/messages' +import { + MAX_CONNECTIONS_HISTORY, + MAX_MEMORY_HISTORY, + MAX_TRAFFIC_HISTORY, + useClashConnections, + useClashMemory, + useClashTraffic, +} from '@nyanpasu/interface' +import { cn } from '@nyanpasu/ui' +import { WidgetComponentProps } from './consts' +import WidgetItem, { WidgetItemProps } from './widget-item' + +const padData = (data: (number | undefined)[] = [], max: number) => + Array(Math.max(0, max - data.length)) + .fill(0) + .concat(data.slice(-max)) + +function SparklineCard({ + id, + minH = 2, + minW = 2, + maxW, + maxH, + data, + className, + children, + onCloseClick, + ...props +}: ComponentProps & { + data: number[] +} & WidgetItemProps) { + return ( + + + + + + {children} + + + + ) +} + +function SparklineCardTitle({ + icon: Icon, + className, + children, + ...props +}: ComponentProps<'div'> & { + icon: ComponentType<{ + className?: string + }> +}) { + return ( +
+ + + {children} +
+ ) +} + +function SparklineCardContent({ className, ...props }: ComponentProps<'div'>) { + return ( +
+ ) +} + +function SparklineCardBottom({ className, ...props }: ComponentProps<'div'>) { + return ( +
+ ) +} + +export function TrafficDownWidget({ id, onCloseClick }: WidgetComponentProps) { + const { data: clashTraffic } = useClashTraffic() + + const { + query: { data: clashConnections }, + } = useClashConnections() + + const total = clashConnections?.at(-1)?.downloadTotal + + return ( + item.down), + MAX_TRAFFIC_HISTORY, + )} + onCloseClick={onCloseClick} + > + + {m.dashboard_widget_traffic_download()} + + + + {filesize(clashTraffic?.at(-1)?.down ?? 0)}/s + + + + {total !== undefined && + m.dashboard_widget_traffic_total({ + value: filesize(total), + })} + + + ) +} + +export function TrafficUpWidget({ id, onCloseClick }: WidgetComponentProps) { + const { data: clashTraffic } = useClashTraffic() + + const { + query: { data: clashConnections }, + } = useClashConnections() + + const total = clashConnections?.at(-1)?.uploadTotal + + return ( + item.up), + MAX_TRAFFIC_HISTORY, + )} + onCloseClick={onCloseClick} + > + + {m.dashboard_widget_traffic_upload()} + + + + {filesize(clashTraffic?.at(-1)?.up ?? 0)}/s + + + + {total !== undefined && + m.dashboard_widget_traffic_total({ + value: filesize(total), + })} + + + ) +} + +export function ConnectionsWidget({ id, onCloseClick }: WidgetComponentProps) { + const { + query: { data: clashConnections }, + } = useClashConnections() + + return ( + item.connections?.length ?? 0), + MAX_CONNECTIONS_HISTORY, + )} + onCloseClick={onCloseClick} + > + + {m.dashboard_widget_connections()} + + + + {clashConnections?.at(-1)?.connections?.length ?? 0} + + + + + ) +} + +export function MemoryWidget({ id, onCloseClick }: WidgetComponentProps) { + const { data: clashMemory } = useClashMemory() + + return ( + item.inuse), + MAX_MEMORY_HISTORY, + )} + onCloseClick={onCloseClick} + > + + {m.dashboard_widget_memory()} + + + + {filesize(clashMemory?.at(-1)?.inuse ?? 0)} + + + + + ) +} diff --git a/clash-nyanpasu/frontend/nyanpasu/src/pages/(main)/main/dashboard/index.tsx b/clash-nyanpasu/frontend/nyanpasu/src/pages/(main)/main/dashboard/index.tsx new file mode 100644 index 0000000000..921b4d1c40 --- /dev/null +++ b/clash-nyanpasu/frontend/nyanpasu/src/pages/(main)/main/dashboard/index.tsx @@ -0,0 +1,293 @@ +import AddRounded from '~icons/material-symbols/add-rounded' +import EditRounded from '~icons/material-symbols/edit-rounded' +import { useCallback, useRef, useState } from 'react' +import { + RegisterContextMenu, + RegisterContextMenuContent, + RegisterContextMenuTrigger, +} from '@/components/providers/context-menu-provider' +import { ContextMenuItem } from '@/components/ui/context-menu' +import { + DndGrid, + DndGridProvider, + DndGridRoot, + useDndGridRoot, + type DndGridItemType, + type GridItemConstraints, + type GridSize, +} from '@/components/ui/dnd-grid' +import { hasOverlap } from '@/components/ui/dnd-grid/utils' +import { m } from '@/paraglide/messages' +import { DragOverlay } from '@dnd-kit/core' +import { useKvStorage } from '@nyanpasu/interface' +import { createFileRoute } from '@tanstack/react-router' +import { + DashboardItem, + DEFAULT_ITEMS, + DEFAULT_LAYOUTS, + LayoutStorage, + RENDER_MAP, + WIDGET_MIN_SIZE_MAP, + WidgetId, +} from './_modules/consts' +import EditAction from './_modules/edit-action' +import { + adaptLayout, + findBestLayout, + findClosestStoredLayout, + sizeKey, +} from './_modules/layout-adapt' +import { useDashboardContext } from './_modules/provider' +import { WidgetSheet } from './_modules/widget-sheet' + +export const Route = createFileRoute('/(main)/main/dashboard/')({ + component: RouteComponent, +}) + +function normalizeItems(items: DndGridItemType[]): DashboardItem[] { + return items.map((item) => ({ + ...item, + type: (item as DashboardItem).type ?? (item.id as WidgetId), + })) +} + +function DashboardDragOverlay({ + displayItems, +}: { + displayItems: DashboardItem[] +}) { + const root = useDndGridRoot() + const activeDrag = root?.activeDrag ?? null + + return ( + + {activeDrag && + (() => { + const widgetType = + displayItems.find((i) => i.id === activeDrag.itemId)?.type ?? + (activeDrag.itemId as WidgetId) + const WidgetComponent = RENDER_MAP[widgetType] + + if (!WidgetComponent) { + return null + } + + return ( +
+ ({ + left: 0, + top: 0, + width: 0, + height: 0, + }), + dropInfoMap: {}, + activeItemId: null, + resizingItemId: null, + disabled: true, + sourceOnly: true, + dragIdPrefix: '', + isOverlay: true, + constraintsMapRef: { current: {} }, + onResizeStart: () => {}, + onResizeMove: () => {}, + onResizeEnd: () => {}, + }} + > + + +
+ ) + })()} +
+ ) +} + +const WidgetRender = () => { + const { isEditing, setOpenSheet } = useDashboardContext() + + const [layoutStorage, setLayoutStorage] = useKvStorage( + 'dashboard-widgets', + DEFAULT_LAYOUTS, + ) + + const [displayItems, setDisplayItems] = + useState(DEFAULT_ITEMS) + + const layoutStorageRef = useRef(layoutStorage) + layoutStorageRef.current = layoutStorage + + const displayItemsRef = useRef(displayItems) + displayItemsRef.current = displayItems + + const gridSizeRef = useRef({ cols: 1, rows: 1 }) + + const handleSizeChange = useCallback( + ( + newSize: GridSize, + constraintsMap: Record, + ) => { + gridSizeRef.current = newSize + + const bestLayout = findBestLayout(layoutStorageRef.current, newSize) + if (bestLayout) { + const normalized = normalizeItems(bestLayout) + displayItemsRef.current = normalized + setDisplayItems(normalized) + return + } + + const base = + findClosestStoredLayout(layoutStorageRef.current, newSize) ?? + DEFAULT_ITEMS + + const nextItems = normalizeItems( + adaptLayout(base, newSize, constraintsMap), + ) + displayItemsRef.current = nextItems + setDisplayItems(nextItems) + }, + [], + ) + + const handleLayoutChange = useCallback( + (newItems: DashboardItem[]) => { + const key = sizeKey(gridSizeRef.current) + displayItemsRef.current = newItems + setDisplayItems(newItems) + + layoutStorageRef.current = { + ...layoutStorageRef.current, + [key]: newItems, + } + setLayoutStorage(layoutStorageRef.current) + }, + [setLayoutStorage], + ) + + const addWidgetFromSheet = useCallback( + (widgetId: WidgetId) => { + const { minW, minH } = WIDGET_MIN_SIZE_MAP[widgetId] + const { cols, rows } = gridSizeRef.current + const current = displayItemsRef.current + const instanceId = crypto.randomUUID() + + const findPlacement = (): DashboardItem => { + for (let y = 0; y <= rows; y++) { + for (let x = 0; x <= cols - minW; x++) { + const candidate: DashboardItem = { + id: instanceId, + type: widgetId, + x, + y, + w: minW, + h: minH, + } + if (!hasOverlap(current, instanceId, candidate)) { + return candidate + } + } + } + const maxY = current.reduce((m, i) => Math.max(m, i.y + i.h), 0) + return { + id: instanceId, + type: widgetId, + x: 0, + y: maxY, + w: minW, + h: minH, + } + } + + handleLayoutChange([...current, findPlacement()]) + }, + [handleLayoutChange], + ) + + return ( + +
+ + handleLayoutChange(normalizeItems(newItems)) + } + minCellSize={64} + onSizeChange={handleSizeChange} + gap={16} + disabled={!isEditing} + > + {(item) => { + const WidgetComponent = RENDER_MAP[(item as DashboardItem).type] + + return ( + + handleLayoutChange( + displayItemsRef.current.filter((i) => i.id !== item.id), + ) + } + /> + ) + }} + +
+ + + + addWidgetFromSheet(id)} + onSourceDragStart={() => setOpenSheet(false)} + /> +
+ ) +} + +function RouteComponent() { + const { setIsEditing, setOpenSheet } = useDashboardContext() + + return ( + + +
+ + + +
+
+ + + setIsEditing(true)}> + + + {m.dashboard_context_menu_edit_widgets()} + + + { + setIsEditing(true) + setOpenSheet(true) + }} + > + + + {m.dashboard_context_menu_add_widgets()} + + +
+ ) +} diff --git a/clash-nyanpasu/frontend/nyanpasu/src/pages/(main)/main/dashboard/route.tsx b/clash-nyanpasu/frontend/nyanpasu/src/pages/(main)/main/dashboard/route.tsx index 762a556708..2786b4b9f8 100644 --- a/clash-nyanpasu/frontend/nyanpasu/src/pages/(main)/main/dashboard/route.tsx +++ b/clash-nyanpasu/frontend/nyanpasu/src/pages/(main)/main/dashboard/route.tsx @@ -1,6 +1,5 @@ -import { Button } from '@/components/ui/button' -import { AppContentScrollArea } from '@/components/ui/scroll-area' -import { createFileRoute } from '@tanstack/react-router' +import { createFileRoute, Outlet } from '@tanstack/react-router' +import { DashboardProvider } from './_modules/provider' export const Route = createFileRoute('/(main)/main/dashboard')({ component: RouteComponent, @@ -8,12 +7,8 @@ export const Route = createFileRoute('/(main)/main/dashboard')({ function RouteComponent() { return ( - -
-

Hello "/(main)/main/dashboard"!

- - -
-
+ + + ) } diff --git a/clash-nyanpasu/frontend/nyanpasu/src/pages/(main)/main/settings/debug/_modules/kv-storage.tsx b/clash-nyanpasu/frontend/nyanpasu/src/pages/(main)/main/settings/debug/_modules/kv-storage.tsx index 7a61b57a51..ec8e3e35fa 100644 --- a/clash-nyanpasu/frontend/nyanpasu/src/pages/(main)/main/settings/debug/_modules/kv-storage.tsx +++ b/clash-nyanpasu/frontend/nyanpasu/src/pages/(main)/main/settings/debug/_modules/kv-storage.tsx @@ -34,6 +34,11 @@ export default function KVStorage() { await query.refetch() }) + const handleRemoveItem = useLockFn(async (key: string) => { + await commands.removeStorageItem(key) + await query.refetch() + }) + return ( @@ -47,37 +52,61 @@ export default function KVStorage() {
{query.data && - query.data.map((storage) => ( -
-
Key: {storage.key}
+ query.data.map((storage) => { + const tryFmt = () => { + try { + const parsed = JSON.parse(storage.value) - - - - + return JSON.stringify( + typeof parsed === 'string' ? JSON.parse(parsed) : parsed, + null, + 2, + ) + } catch { + return storage.value + } + } - - - - Storage Detail - + return ( +
+
Key: {storage.key}
- -
-                          {JSON.stringify(storage, null, 2)}
-                        
-
+ - - {m.common_close()} - - - - -
- ))} + + + + + + + + + Storage Detail + + + +
+                            {tryFmt()}
+                          
+
+ + + {m.common_close()} + +
+
+
+
+ ) + })} diff --git a/clash-nyanpasu/frontend/nyanpasu/src/pages/(main)/main/settings/user-interface/_modules/switch-legacy.tsx b/clash-nyanpasu/frontend/nyanpasu/src/pages/(main)/main/settings/user-interface/_modules/switch-legacy.tsx index 1667c9e475..c34a86555e 100644 --- a/clash-nyanpasu/frontend/nyanpasu/src/pages/(main)/main/settings/user-interface/_modules/switch-legacy.tsx +++ b/clash-nyanpasu/frontend/nyanpasu/src/pages/(main)/main/settings/user-interface/_modules/switch-legacy.tsx @@ -1,3 +1,4 @@ +import ArrowForwardIosRounded from '~icons/material-symbols/arrow-forward-ios-rounded' import { Button } from '@/components/ui/button' import { Card, CardContent, CardFooter, CardHeader } from '@/components/ui/card' import { @@ -10,64 +11,72 @@ import { import { useLockFn } from '@/hooks/use-lock-fn' import { commands, useSetting } from '@nyanpasu/interface' import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow' -import { SettingsCard, SettingsCardContent } from '../../_modules/settings-card' +import { + ItemContainer, + ItemLabel, + ItemLabelText, + SettingsCard, + SettingsCardContent, + SettingsCardFooter, +} from '../../_modules/settings-card' const currentWindow = getCurrentWebviewWindow() export default function SwitchLegacy() { - const { upsert } = useSetting('use_legacy_ui') + const { upsert } = useSetting('window_type') const handleClick = useLockFn(async () => { - await upsert(true) + await upsert('legacy') await commands.createLegacyWindow() await currentWindow.close() }) return ( - - - Switch to Legacy UI + + + + - +
+ +
+ + + +
- - - - - Are you sure you want to switch to Legacy UI? - - + + + + + Are you sure you want to switch to Legacy UI? + + - -

- Switching to Legacy UI will revert the UI to the original - design. -

-
+ +

+ Switching to Legacy UI will revert the UI to the original + design. +

+
- - + + - - - - -
-
-
- -
-
+ + + + + + +
) } diff --git a/clash-nyanpasu/frontend/nyanpasu/src/pages/(main)/main/settings/user-interface/route.tsx b/clash-nyanpasu/frontend/nyanpasu/src/pages/(main)/main/settings/user-interface/route.tsx index 0fcc3bd3d1..020b8eed8e 100644 --- a/clash-nyanpasu/frontend/nyanpasu/src/pages/(main)/main/settings/user-interface/route.tsx +++ b/clash-nyanpasu/frontend/nyanpasu/src/pages/(main)/main/settings/user-interface/route.tsx @@ -70,9 +70,15 @@ function RouteComponent() { -
- {/* */} +
+ Switch to Legacy UI + + + + +
+ ) } diff --git a/clash-nyanpasu/frontend/nyanpasu/src/route-tree.gen.ts b/clash-nyanpasu/frontend/nyanpasu/src/route-tree.gen.ts index 9a7e4d1329..93e1aae7bc 100644 --- a/clash-nyanpasu/frontend/nyanpasu/src/route-tree.gen.ts +++ b/clash-nyanpasu/frontend/nyanpasu/src/route-tree.gen.ts @@ -37,6 +37,7 @@ import { Route as mainMainProxiesIndexRouteImport } from './pages/(main)/main/pr import { Route as mainMainProvidersIndexRouteImport } from './pages/(main)/main/providers/index' import { Route as mainMainProfilesIndexRouteImport } from './pages/(main)/main/profiles/index' import { Route as mainMainLogsIndexRouteImport } from './pages/(main)/main/logs/index' +import { Route as mainMainDashboardIndexRouteImport } from './pages/(main)/main/dashboard/index' import { Route as mainMainConnectionsIndexRouteImport } from './pages/(main)/main/connections/index' import { Route as mainMainSettingsWebUiRouteRouteImport } from './pages/(main)/main/settings/web-ui/route' import { Route as mainMainSettingsUserInterfaceRouteRouteImport } from './pages/(main)/main/settings/user-interface/route' @@ -192,6 +193,11 @@ const mainMainLogsIndexRoute = mainMainLogsIndexRouteImport.update({ path: '/', getParentRoute: () => mainMainLogsRouteRoute, } as any) +const mainMainDashboardIndexRoute = mainMainDashboardIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => mainMainDashboardRouteRoute, +} as any) const mainMainConnectionsIndexRoute = mainMainConnectionsIndexRouteImport.update({ id: '/', @@ -295,7 +301,7 @@ export interface FileRoutesByFullPath { '/settings': typeof legacySettingsRoute '/': typeof legacyIndexRoute '/main/connections': typeof mainMainConnectionsRouteRouteWithChildren - '/main/dashboard': typeof mainMainDashboardRouteRoute + '/main/dashboard': typeof mainMainDashboardRouteRouteWithChildren '/main/logs': typeof mainMainLogsRouteRouteWithChildren '/main/profiles': typeof mainMainProfilesRouteRouteWithChildren '/main/providers': typeof mainMainProvidersRouteRouteWithChildren @@ -313,6 +319,7 @@ export interface FileRoutesByFullPath { '/main/settings/user-interface': typeof mainMainSettingsUserInterfaceRouteRoute '/main/settings/web-ui': typeof mainMainSettingsWebUiRouteRoute '/main/connections/': typeof mainMainConnectionsIndexRoute + '/main/dashboard/': typeof mainMainDashboardIndexRoute '/main/logs/': typeof mainMainLogsIndexRoute '/main/profiles/': typeof mainMainProfilesIndexRoute '/main/providers/': typeof mainMainProvidersIndexRoute @@ -336,7 +343,6 @@ export interface FileRoutesByTo { '/rules': typeof legacyRulesRoute '/settings': typeof legacySettingsRoute '/': typeof legacyIndexRoute - '/main/dashboard': typeof mainMainDashboardRouteRoute '/editor': typeof editorEditorIndexRoute '/main': typeof mainMainIndexRoute '/main/profiles/inspect': typeof mainMainProfilesInspectRouteRoute @@ -347,6 +353,7 @@ export interface FileRoutesByTo { '/main/settings/user-interface': typeof mainMainSettingsUserInterfaceRouteRoute '/main/settings/web-ui': typeof mainMainSettingsWebUiRouteRoute '/main/connections': typeof mainMainConnectionsIndexRoute + '/main/dashboard': typeof mainMainDashboardIndexRoute '/main/logs': typeof mainMainLogsIndexRoute '/main/profiles': typeof mainMainProfilesIndexRoute '/main/providers': typeof mainMainProvidersIndexRoute @@ -375,7 +382,7 @@ export interface FileRoutesById { '/(legacy)/settings': typeof legacySettingsRoute '/(legacy)/': typeof legacyIndexRoute '/(main)/main/connections': typeof mainMainConnectionsRouteRouteWithChildren - '/(main)/main/dashboard': typeof mainMainDashboardRouteRoute + '/(main)/main/dashboard': typeof mainMainDashboardRouteRouteWithChildren '/(main)/main/logs': typeof mainMainLogsRouteRouteWithChildren '/(main)/main/profiles': typeof mainMainProfilesRouteRouteWithChildren '/(main)/main/providers': typeof mainMainProvidersRouteRouteWithChildren @@ -393,6 +400,7 @@ export interface FileRoutesById { '/(main)/main/settings/user-interface': typeof mainMainSettingsUserInterfaceRouteRoute '/(main)/main/settings/web-ui': typeof mainMainSettingsWebUiRouteRoute '/(main)/main/connections/': typeof mainMainConnectionsIndexRoute + '/(main)/main/dashboard/': typeof mainMainDashboardIndexRoute '/(main)/main/logs/': typeof mainMainLogsIndexRoute '/(main)/main/profiles/': typeof mainMainProfilesIndexRoute '/(main)/main/providers/': typeof mainMainProvidersIndexRoute @@ -438,6 +446,7 @@ export interface FileRouteTypes { | '/main/settings/user-interface' | '/main/settings/web-ui' | '/main/connections/' + | '/main/dashboard/' | '/main/logs/' | '/main/profiles/' | '/main/providers/' @@ -461,7 +470,6 @@ export interface FileRouteTypes { | '/rules' | '/settings' | '/' - | '/main/dashboard' | '/editor' | '/main' | '/main/profiles/inspect' @@ -472,6 +480,7 @@ export interface FileRouteTypes { | '/main/settings/user-interface' | '/main/settings/web-ui' | '/main/connections' + | '/main/dashboard' | '/main/logs' | '/main/profiles' | '/main/providers' @@ -517,6 +526,7 @@ export interface FileRouteTypes { | '/(main)/main/settings/user-interface' | '/(main)/main/settings/web-ui' | '/(main)/main/connections/' + | '/(main)/main/dashboard/' | '/(main)/main/logs/' | '/(main)/main/profiles/' | '/(main)/main/providers/' @@ -735,6 +745,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof mainMainLogsIndexRouteImport parentRoute: typeof mainMainLogsRouteRoute } + '/(main)/main/dashboard/': { + id: '/(main)/main/dashboard/' + path: '/' + fullPath: '/main/dashboard/' + preLoaderRoute: typeof mainMainDashboardIndexRouteImport + parentRoute: typeof mainMainDashboardRouteRoute + } '/(main)/main/connections/': { id: '/(main)/main/connections/' path: '/' @@ -885,6 +902,20 @@ const mainMainConnectionsRouteRouteWithChildren = mainMainConnectionsRouteRouteChildren, ) +interface mainMainDashboardRouteRouteChildren { + mainMainDashboardIndexRoute: typeof mainMainDashboardIndexRoute +} + +const mainMainDashboardRouteRouteChildren: mainMainDashboardRouteRouteChildren = + { + mainMainDashboardIndexRoute: mainMainDashboardIndexRoute, + } + +const mainMainDashboardRouteRouteWithChildren = + mainMainDashboardRouteRoute._addFileChildren( + mainMainDashboardRouteRouteChildren, + ) + interface mainMainLogsRouteRouteChildren { mainMainLogsIndexRoute: typeof mainMainLogsIndexRoute } @@ -1001,7 +1032,7 @@ const mainMainSettingsRouteRouteWithChildren = interface mainRouteRouteChildren { mainMainConnectionsRouteRoute: typeof mainMainConnectionsRouteRouteWithChildren - mainMainDashboardRouteRoute: typeof mainMainDashboardRouteRoute + mainMainDashboardRouteRoute: typeof mainMainDashboardRouteRouteWithChildren mainMainLogsRouteRoute: typeof mainMainLogsRouteRouteWithChildren mainMainProfilesRouteRoute: typeof mainMainProfilesRouteRouteWithChildren mainMainProvidersRouteRoute: typeof mainMainProvidersRouteRouteWithChildren @@ -1013,7 +1044,7 @@ interface mainRouteRouteChildren { const mainRouteRouteChildren: mainRouteRouteChildren = { mainMainConnectionsRouteRoute: mainMainConnectionsRouteRouteWithChildren, - mainMainDashboardRouteRoute: mainMainDashboardRouteRoute, + mainMainDashboardRouteRoute: mainMainDashboardRouteRouteWithChildren, mainMainLogsRouteRoute: mainMainLogsRouteRouteWithChildren, mainMainProfilesRouteRoute: mainMainProfilesRouteRouteWithChildren, mainMainProvidersRouteRoute: mainMainProvidersRouteRouteWithChildren, diff --git a/clash-nyanpasu/manifest/version.json b/clash-nyanpasu/manifest/version.json index 3b38fe5548..97d545912a 100644 --- a/clash-nyanpasu/manifest/version.json +++ b/clash-nyanpasu/manifest/version.json @@ -2,10 +2,10 @@ "manifest_version": 1, "latest": { "mihomo": "v1.19.21", - "mihomo_alpha": "alpha-d0f3312", + "mihomo_alpha": "alpha-809aee8", "clash_rs": "v0.9.6", "clash_premium": "2023-09-05-gdcc8d87", - "clash_rs_alpha": "0.9.6-alpha+sha.b667e62" + "clash_rs_alpha": "0.9.6-alpha+sha.8a5452f" }, "arch_template": { "mihomo": { @@ -69,5 +69,5 @@ "linux-armv7hf": "clash-rs-armv7-unknown-linux-gnueabihf" } }, - "updated_at": "2026-03-25T22:24:04.344Z" + "updated_at": "2026-03-26T22:23:05.609Z" } diff --git a/clash-nyanpasu/pnpm-lock.yaml b/clash-nyanpasu/pnpm-lock.yaml index 44fd954965..fcd6152570 100644 --- a/clash-nyanpasu/pnpm-lock.yaml +++ b/clash-nyanpasu/pnpm-lock.yaml @@ -230,9 +230,6 @@ importers: '@tauri-apps/api': specifier: 2.10.1 version: 2.10.1 - '@types/json-schema': - specifier: 7.0.15 - version: 7.0.15 '@uidotdev/usehooks': specifier: 2.4.1 version: 2.4.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -254,6 +251,9 @@ importers: country-emoji: specifier: 1.5.6 version: 1.5.6 + d3: + specifier: 7.9.0 + version: 7.9.0 dayjs: specifier: 1.11.20 version: 1.11.20 @@ -261,8 +261,8 @@ importers: specifier: 12.38.0 version: 12.38.0(@emotion/is-prop-valid@1.3.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) i18next: - specifier: 25.10.9 - version: 25.10.9(typescript@5.9.3) + specifier: 25.10.10 + version: 25.10.10(typescript@5.9.3) jotai: specifier: 2.19.0 version: 2.19.0(@babel/core@7.29.0)(@babel/template@7.28.6)(@types/react@19.2.14)(react@19.2.4) @@ -301,7 +301,7 @@ importers: version: 8.2.0(6d218d2bf105c0e96ea9b04fabea0c78) react-i18next: specifier: 15.7.4 - version: 15.7.4(i18next@25.10.9(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + version: 15.7.4(i18next@25.10.10(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) react-markdown: specifier: 10.1.0 version: 10.1.0(@types/react@19.2.14)(react@19.2.4) @@ -317,6 +317,9 @@ importers: swr: specifier: 2.4.1 version: 2.4.1(react@19.2.4) + vaul: + specifier: 1.1.2 + version: 1.1.2(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) virtua: specifier: 0.46.6 version: 0.46.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(solid-js@1.9.5) @@ -375,6 +378,15 @@ importers: '@tauri-apps/plugin-updater': specifier: 2.10.0 version: 2.10.0 + '@types/d3': + specifier: 7.4.3 + version: 7.4.3 + '@types/d3-interpolate-path': + specifier: 2.0.3 + version: 2.0.3 + '@types/json-schema': + specifier: 7.0.15 + version: 7.0.15 '@types/react': specifier: 19.2.14 version: 19.2.14 @@ -436,8 +448,8 @@ importers: specifier: 3.2.2 version: 3.2.2(vite@7.3.1(@types/node@24.11.0)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.32.0)(sass-embedded@1.98.0)(sass@1.98.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.2)) vite-plugin-sass-dts: - specifier: 1.3.35 - version: 1.3.35(postcss@8.5.8)(prettier@3.8.1)(sass-embedded@1.98.0)(vite@7.3.1(@types/node@24.11.0)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.32.0)(sass-embedded@1.98.0)(sass@1.98.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.2)) + specifier: 1.3.37 + version: 1.3.37(postcss@8.5.8)(prettier@3.8.1)(sass-embedded@1.98.0)(vite@7.3.1(@types/node@24.11.0)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.32.0)(sass-embedded@1.98.0)(sass@1.98.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.2)) vite-plugin-svgr: specifier: 4.5.0 version: 4.5.0(rollup@4.46.2)(typescript@5.9.3)(vite@7.3.1(@types/node@24.11.0)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.32.0)(sass-embedded@1.98.0)(sass@1.98.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.2)) @@ -500,7 +512,7 @@ importers: version: 6.0.0(react@19.2.4) react-i18next: specifier: 15.7.4 - version: 15.7.4(i18next@25.10.9(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + version: 15.7.4(i18next@25.10.10(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) react-use: specifier: 17.6.0 version: 17.6.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -5798,8 +5810,8 @@ packages: hyphenate-style-name@1.1.0: resolution: {integrity: sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==} - i18next@25.10.9: - resolution: {integrity: sha512-hQY9/bFoQKGlSKMlaCuLR8w1h5JjieqrsnZvEmj1Ja6Ec7fbyc4cTrCsY9mb9Sd8YQ/swsrKz1S9M8AcvVI70w==} + i18next@25.10.10: + resolution: {integrity: sha512-cqUW2Z3EkRx7NqSyywjkgCLK7KLCL6IFVFcONG7nVYIJ3ekZ1/N5jUsihHV6Bq37NfhgtczxJcxduELtjTwkuQ==} peerDependencies: typescript: ^5 || ^6 peerDependenciesMeta: @@ -7927,6 +7939,12 @@ packages: varint@6.0.0: resolution: {integrity: sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==} + vaul@1.1.2: + resolution: {integrity: sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc + vfile-message@4.0.2: resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==} @@ -7972,14 +7990,14 @@ packages: peerDependencies: vite: '>=2.0.0' - vite-plugin-sass-dts@1.3.35: - resolution: {integrity: sha512-WWfOvgwu7ljBdmtt8VjSvsyzmXUhyITdXhDYzkTprXYOJ9Efbknf97lBPbKcF2sCVeK/JsH+djvJbkazDU5VGQ==} + vite-plugin-sass-dts@1.3.37: + resolution: {integrity: sha512-R3TgyrMhHh81sBuFFt5FjEWRbFeukoqwIeUmQDxJKYhOk8lafkftvoSqKsfWMepXQL73cu3HFk6FD6nLZ2mDjA==} engines: {node: '>=20'} peerDependencies: postcss: ^8 prettier: ^2.7 || ^3 sass-embedded: ^1.78.0 - vite: ^7 + vite: ^7 || ^8 vite-plugin-svgr@4.5.0: resolution: {integrity: sha512-W+uoSpmVkSmNOGPSsDCWVW/DDAyv+9fap9AZXBvWiQqrboJ08j2vh0tFxTD/LjwqwAd3yYSVJgm54S/1GhbdnA==} @@ -13514,7 +13532,7 @@ snapshots: hyphenate-style-name@1.1.0: {} - i18next@25.10.9(typescript@5.9.3): + i18next@25.10.10(typescript@5.9.3): dependencies: '@babel/runtime': 7.29.2 optionalDependencies: @@ -14770,11 +14788,11 @@ snapshots: dependencies: react: 19.2.4 - react-i18next@15.7.4(i18next@25.10.9(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3): + react-i18next@15.7.4(i18next@25.10.10(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3): dependencies: '@babel/runtime': 7.28.3 html-parse-stringify: 3.0.1 - i18next: 25.10.9(typescript@5.9.3) + i18next: 25.10.10(typescript@5.9.3) react: 19.2.4 optionalDependencies: react-dom: 19.2.4(react@19.2.4) @@ -15783,6 +15801,15 @@ snapshots: varint@6.0.0: {} + vaul@1.1.2(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + dependencies: + '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + transitivePeerDependencies: + - '@types/react' + - '@types/react-dom' + vfile-message@4.0.2: dependencies: '@types/unist': 3.0.2 @@ -15845,7 +15872,7 @@ snapshots: pathe: 0.2.0 vite: 7.3.1(@types/node@24.11.0)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.32.0)(sass-embedded@1.98.0)(sass@1.98.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.2) - vite-plugin-sass-dts@1.3.35(postcss@8.5.8)(prettier@3.8.1)(sass-embedded@1.98.0)(vite@7.3.1(@types/node@24.11.0)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.32.0)(sass-embedded@1.98.0)(sass@1.98.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.2)): + vite-plugin-sass-dts@1.3.37(postcss@8.5.8)(prettier@3.8.1)(sass-embedded@1.98.0)(vite@7.3.1(@types/node@24.11.0)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.32.0)(sass-embedded@1.98.0)(sass@1.98.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.2)): dependencies: postcss: 8.5.8 postcss-js: 4.0.1(postcss@8.5.8) diff --git a/mihomo/.github/patch/go1.21.patch b/mihomo/.github/patch/go1.21.patch deleted file mode 100644 index 2ada77813a..0000000000 --- a/mihomo/.github/patch/go1.21.patch +++ /dev/null @@ -1,182 +0,0 @@ -Subject: [PATCH] Revert "[release-branch.go1.21] crypto/rand,runtime: switch RtlGenRandom for ProcessPrng" ---- -Index: src/crypto/rand/rand.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/crypto/rand/rand.go b/src/crypto/rand/rand.go ---- a/src/crypto/rand/rand.go (revision 8bba868de983dd7bf55fcd121495ba8d6e2734e7) -+++ b/src/crypto/rand/rand.go (revision 7e6c963d81e14ee394402671d4044b2940c8d2c1) -@@ -15,7 +15,7 @@ - // 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 ProcessPrng API. -+// On Windows systems, Reader uses the RtlGenRandom 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 -Index: src/crypto/rand/rand_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/crypto/rand/rand_windows.go b/src/crypto/rand/rand_windows.go ---- a/src/crypto/rand/rand_windows.go (revision 8bba868de983dd7bf55fcd121495ba8d6e2734e7) -+++ b/src/crypto/rand/rand_windows.go (revision 7e6c963d81e14ee394402671d4044b2940c8d2c1) -@@ -15,8 +15,11 @@ - - type rngReader struct{} - --func (r *rngReader) Read(b []byte) (int, error) { -- if err := windows.ProcessPrng(b); err != nil { -+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 { - return 0, err - } - return len(b), nil -Index: src/internal/syscall/windows/syscall_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/internal/syscall/windows/syscall_windows.go b/src/internal/syscall/windows/syscall_windows.go ---- a/src/internal/syscall/windows/syscall_windows.go (revision 8bba868de983dd7bf55fcd121495ba8d6e2734e7) -+++ b/src/internal/syscall/windows/syscall_windows.go (revision 7e6c963d81e14ee394402671d4044b2940c8d2c1) -@@ -384,7 +384,7 @@ - //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 ProcessPrng(buf []byte) (err error) = bcryptprimitives.ProcessPrng -+//sys RtlGenRandom(buf []byte) (err error) = advapi32.SystemFunction036 - - //sys RtlLookupFunctionEntry(pc uintptr, baseAddress *uintptr, table *byte) (ret uintptr) = kernel32.RtlLookupFunctionEntry - //sys RtlVirtualUnwind(handlerType uint32, baseAddress uintptr, pc uintptr, entry uintptr, ctxt uintptr, data *uintptr, frame *uintptr, ctxptrs *byte) (ret uintptr) = kernel32.RtlVirtualUnwind -Index: src/internal/syscall/windows/zsyscall_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/internal/syscall/windows/zsyscall_windows.go b/src/internal/syscall/windows/zsyscall_windows.go ---- a/src/internal/syscall/windows/zsyscall_windows.go (revision 8bba868de983dd7bf55fcd121495ba8d6e2734e7) -+++ b/src/internal/syscall/windows/zsyscall_windows.go (revision 7e6c963d81e14ee394402671d4044b2940c8d2c1) -@@ -37,14 +37,13 @@ - } - - var ( -- 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")) -+ 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")) - - procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges") - procDuplicateTokenEx = modadvapi32.NewProc("DuplicateTokenEx") -@@ -53,7 +52,7 @@ - procOpenThreadToken = modadvapi32.NewProc("OpenThreadToken") - procRevertToSelf = modadvapi32.NewProc("RevertToSelf") - procSetTokenInformation = modadvapi32.NewProc("SetTokenInformation") -- procProcessPrng = modbcryptprimitives.NewProc("ProcessPrng") -+ procSystemFunction036 = modadvapi32.NewProc("SystemFunction036") - procGetAdaptersAddresses = modiphlpapi.NewProc("GetAdaptersAddresses") - procCreateEventW = modkernel32.NewProc("CreateEventW") - procGetACP = modkernel32.NewProc("GetACP") -@@ -149,12 +148,12 @@ - return - } - --func ProcessPrng(buf []byte) (err error) { -+func RtlGenRandom(buf []byte) (err error) { - var _p0 *byte - if len(buf) > 0 { - _p0 = &buf[0] - } -- r1, _, e1 := syscall.Syscall(procProcessPrng.Addr(), 2, uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), 0) -+ r1, _, e1 := syscall.Syscall(procSystemFunction036.Addr(), 2, uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), 0) - if r1 == 0 { - err = errnoErr(e1) - } -Index: src/runtime/os_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go ---- a/src/runtime/os_windows.go (revision 8bba868de983dd7bf55fcd121495ba8d6e2734e7) -+++ b/src/runtime/os_windows.go (revision 7e6c963d81e14ee394402671d4044b2940c8d2c1) -@@ -127,8 +127,15 @@ - _AddVectoredContinueHandler, - _ stdFunction - -- // Use ProcessPrng to generate cryptographically random data. -- _ProcessPrng 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 - - // Load ntdll.dll manually during startup, otherwise Mingw - // links wrong printf function to cgo executable (see issue -@@ -145,12 +152,12 @@ - ) - - var ( -- bcryptprimitivesdll = [...]uint16{'b', 'c', 'r', 'y', 'p', 't', 'p', 'r', 'i', 'm', 'i', 't', 'i', 'v', 'e', 's', '.', 'd', 'l', 'l', 0} -- kernel32dll = [...]uint16{'k', 'e', 'r', 'n', 'e', 'l', '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} -+ advapi32dll = [...]uint16{'a', 'd', 'v', 'a', 'p', 'i', '3', '2', '.', 'd', 'l', 'l', 0} -+ kernel32dll = [...]uint16{'k', 'e', 'r', 'n', 'e', 'l', '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} - ) - - // Function to be called by windows CreateThread -@@ -249,11 +256,11 @@ - } - _AddVectoredContinueHandler = windowsFindfunc(k32, []byte("AddVectoredContinueHandler\000")) - -- bcryptPrimitives := windowsLoadSystemLib(bcryptprimitivesdll[:]) -- if bcryptPrimitives == 0 { -- throw("bcryptprimitives.dll not found") -+ a32 := windowsLoadSystemLib(advapi32dll[:]) -+ if a32 == 0 { -+ throw("advapi32.dll not found") - } -- _ProcessPrng = windowsFindfunc(bcryptPrimitives, []byte("ProcessPrng\000")) -+ _RtlGenRandom = windowsFindfunc(a32, []byte("SystemFunction036\000")) - - n32 := windowsLoadSystemLib(ntdlldll[:]) - if n32 == 0 { -@@ -610,7 +617,7 @@ - //go:nosplit - func getRandomData(r []byte) { - n := 0 -- if stdcall2(_ProcessPrng, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 { -+ if stdcall2(_RtlGenRandom, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 { - n = len(r) - } - extendRandom(r, n) diff --git a/mihomo/.github/patch/go1.22.patch b/mihomo/.github/patch/go1.22.patch deleted file mode 100644 index 3c0c66e363..0000000000 --- a/mihomo/.github/patch/go1.22.patch +++ /dev/null @@ -1,645 +0,0 @@ -Subject: [PATCH] Revert "runtime: always use LoadLibraryEx to load system libraries" -Revert "syscall: remove Windows 7 console handle workaround" -Revert "net: remove sysSocket fallback for Windows 7" -Revert "crypto/rand,runtime: switch RtlGenRandom for ProcessPrng" ---- -Index: src/crypto/rand/rand.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/crypto/rand/rand.go b/src/crypto/rand/rand.go ---- a/src/crypto/rand/rand.go (revision cb4eee693c382bea4222f20837e26501d40ed892) -+++ b/src/crypto/rand/rand.go (revision 9779155f18b6556a034f7bb79fb7fb2aad1e26a9) -@@ -15,7 +15,7 @@ - // 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 ProcessPrng API. -+// On Windows systems, Reader uses the RtlGenRandom 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 -Index: src/crypto/rand/rand_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/crypto/rand/rand_windows.go b/src/crypto/rand/rand_windows.go ---- a/src/crypto/rand/rand_windows.go (revision cb4eee693c382bea4222f20837e26501d40ed892) -+++ b/src/crypto/rand/rand_windows.go (revision 9779155f18b6556a034f7bb79fb7fb2aad1e26a9) -@@ -15,8 +15,11 @@ - - type rngReader struct{} - --func (r *rngReader) Read(b []byte) (int, error) { -- if err := windows.ProcessPrng(b); err != nil { -+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 { - return 0, err - } - return len(b), nil -Index: src/internal/syscall/windows/syscall_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/internal/syscall/windows/syscall_windows.go b/src/internal/syscall/windows/syscall_windows.go ---- a/src/internal/syscall/windows/syscall_windows.go (revision cb4eee693c382bea4222f20837e26501d40ed892) -+++ b/src/internal/syscall/windows/syscall_windows.go (revision 9779155f18b6556a034f7bb79fb7fb2aad1e26a9) -@@ -384,7 +384,7 @@ - //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 ProcessPrng(buf []byte) (err error) = bcryptprimitives.ProcessPrng -+//sys RtlGenRandom(buf []byte) (err error) = advapi32.SystemFunction036 - - type FILE_ID_BOTH_DIR_INFO struct { - NextEntryOffset uint32 -Index: src/internal/syscall/windows/zsyscall_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/internal/syscall/windows/zsyscall_windows.go b/src/internal/syscall/windows/zsyscall_windows.go ---- a/src/internal/syscall/windows/zsyscall_windows.go (revision cb4eee693c382bea4222f20837e26501d40ed892) -+++ b/src/internal/syscall/windows/zsyscall_windows.go (revision 9779155f18b6556a034f7bb79fb7fb2aad1e26a9) -@@ -37,14 +37,13 @@ - } - - var ( -- 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")) -+ 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")) - - procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges") - procDuplicateTokenEx = modadvapi32.NewProc("DuplicateTokenEx") -@@ -56,7 +55,7 @@ - procQueryServiceStatus = modadvapi32.NewProc("QueryServiceStatus") - procRevertToSelf = modadvapi32.NewProc("RevertToSelf") - procSetTokenInformation = modadvapi32.NewProc("SetTokenInformation") -- procProcessPrng = modbcryptprimitives.NewProc("ProcessPrng") -+ procSystemFunction036 = modadvapi32.NewProc("SystemFunction036") - procGetAdaptersAddresses = modiphlpapi.NewProc("GetAdaptersAddresses") - procCreateEventW = modkernel32.NewProc("CreateEventW") - procGetACP = modkernel32.NewProc("GetACP") -@@ -180,12 +179,12 @@ - return - } - --func ProcessPrng(buf []byte) (err error) { -+func RtlGenRandom(buf []byte) (err error) { - var _p0 *byte - if len(buf) > 0 { - _p0 = &buf[0] - } -- r1, _, e1 := syscall.Syscall(procProcessPrng.Addr(), 2, uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), 0) -+ r1, _, e1 := syscall.Syscall(procSystemFunction036.Addr(), 2, uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), 0) - if r1 == 0 { - err = errnoErr(e1) - } -Index: src/runtime/os_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go ---- a/src/runtime/os_windows.go (revision cb4eee693c382bea4222f20837e26501d40ed892) -+++ b/src/runtime/os_windows.go (revision 83ff9782e024cb328b690cbf0da4e7848a327f4f) -@@ -40,8 +40,8 @@ - //go:cgo_import_dynamic runtime._GetSystemInfo GetSystemInfo%1 "kernel32.dll" - //go:cgo_import_dynamic runtime._GetThreadContext GetThreadContext%2 "kernel32.dll" - //go:cgo_import_dynamic runtime._SetThreadContext SetThreadContext%2 "kernel32.dll" --//go:cgo_import_dynamic runtime._LoadLibraryExW LoadLibraryExW%3 "kernel32.dll" - //go:cgo_import_dynamic runtime._LoadLibraryW LoadLibraryW%1 "kernel32.dll" -+//go:cgo_import_dynamic runtime._LoadLibraryA LoadLibraryA%1 "kernel32.dll" - //go:cgo_import_dynamic runtime._PostQueuedCompletionStatus PostQueuedCompletionStatus%4 "kernel32.dll" - //go:cgo_import_dynamic runtime._QueryPerformanceCounter QueryPerformanceCounter%1 "kernel32.dll" - //go:cgo_import_dynamic runtime._RaiseFailFastException RaiseFailFastException%3 "kernel32.dll" -@@ -74,7 +74,6 @@ - // Following syscalls are available on every Windows PC. - // All these variables are set by the Windows executable - // loader before the Go program starts. -- _AddVectoredContinueHandler, - _AddVectoredExceptionHandler, - _CloseHandle, - _CreateEventA, -@@ -98,8 +97,8 @@ - _GetSystemInfo, - _GetThreadContext, - _SetThreadContext, -- _LoadLibraryExW, - _LoadLibraryW, -+ _LoadLibraryA, - _PostQueuedCompletionStatus, - _QueryPerformanceCounter, - _RaiseFailFastException, -@@ -127,8 +126,23 @@ - _WriteFile, - _ stdFunction - -- // Use ProcessPrng to generate cryptographically random data. -- _ProcessPrng stdFunction -+ // Following syscalls are only available on some Windows PCs. -+ // We will load syscalls, if available, before using them. -+ _AddDllDirectory, -+ _AddVectoredContinueHandler, -+ _LoadLibraryExA, -+ _LoadLibraryExW, -+ _ 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 - - // Load ntdll.dll manually during startup, otherwise Mingw - // links wrong printf function to cgo executable (see issue -@@ -143,14 +157,6 @@ - _ stdFunction - ) - --var ( -- 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 - // to start new os thread. - func tstart_stdcall(newm *m) -@@ -239,25 +245,51 @@ - return unsafe.String(&sysDirectory[0], sysDirectoryLen) - } - --func windowsLoadSystemLib(name []uint16) uintptr { -- return stdcall3(_LoadLibraryExW, uintptr(unsafe.Pointer(&name[0])), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32) -+//go:linkname syscall_getSystemDirectory syscall.getSystemDirectory -+func syscall_getSystemDirectory() string { -+ return unsafe.String(&sysDirectory[0], sysDirectoryLen) -+} -+ -+func windowsLoadSystemLib(name []byte) uintptr { -+ if useLoadLibraryEx { -+ return stdcall3(_LoadLibraryExA, uintptr(unsafe.Pointer(&name[0])), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32) -+ } else { -+ absName := append(sysDirectory[:sysDirectoryLen], name...) -+ return stdcall1(_LoadLibraryA, uintptr(unsafe.Pointer(&absName[0]))) -+ } - } - - func loadOptionalSyscalls() { -- bcryptPrimitives := windowsLoadSystemLib(bcryptprimitivesdll[:]) -- if bcryptPrimitives == 0 { -- throw("bcryptprimitives.dll not found") -+ var kernel32dll = []byte("kernel32.dll\000") -+ k32 := stdcall1(_LoadLibraryA, uintptr(unsafe.Pointer(&kernel32dll[0]))) -+ if k32 == 0 { -+ throw("kernel32.dll not found") - } -- _ProcessPrng = windowsFindfunc(bcryptPrimitives, []byte("ProcessPrng\000")) -+ _AddDllDirectory = windowsFindfunc(k32, []byte("AddDllDirectory\000")) -+ _AddVectoredContinueHandler = windowsFindfunc(k32, []byte("AddVectoredContinueHandler\000")) -+ _LoadLibraryExA = windowsFindfunc(k32, []byte("LoadLibraryExA\000")) -+ _LoadLibraryExW = windowsFindfunc(k32, []byte("LoadLibraryExW\000")) -+ useLoadLibraryEx = (_LoadLibraryExW != nil && _LoadLibraryExA != nil && _AddDllDirectory != nil) -+ -+ initSysDirectory() - -- n32 := windowsLoadSystemLib(ntdlldll[:]) -+ var advapi32dll = []byte("advapi32.dll\000") -+ a32 := windowsLoadSystemLib(advapi32dll) -+ if a32 == 0 { -+ throw("advapi32.dll not found") -+ } -+ _RtlGenRandom = windowsFindfunc(a32, []byte("SystemFunction036\000")) -+ -+ var ntdll = []byte("ntdll.dll\000") -+ n32 := windowsLoadSystemLib(ntdll) - if n32 == 0 { - throw("ntdll.dll not found") - } - _RtlGetCurrentPeb = windowsFindfunc(n32, []byte("RtlGetCurrentPeb\000")) - _RtlGetNtVersionNumbers = windowsFindfunc(n32, []byte("RtlGetNtVersionNumbers\000")) - -- m32 := windowsLoadSystemLib(winmmdll[:]) -+ var winmmdll = []byte("winmm.dll\000") -+ m32 := windowsLoadSystemLib(winmmdll) - if m32 == 0 { - throw("winmm.dll not found") - } -@@ -267,7 +299,8 @@ - throw("timeBegin/EndPeriod not found") - } - -- ws232 := windowsLoadSystemLib(ws2_32dll[:]) -+ var ws232dll = []byte("ws2_32.dll\000") -+ ws232 := windowsLoadSystemLib(ws232dll) - if ws232 == 0 { - throw("ws2_32.dll not found") - } -@@ -286,7 +319,7 @@ - context uintptr - } - -- powrprof := windowsLoadSystemLib(powrprofdll[:]) -+ powrprof := windowsLoadSystemLib([]byte("powrprof.dll\000")) - if powrprof == 0 { - return // Running on Windows 7, where we don't need it anyway. - } -@@ -360,6 +393,22 @@ - // in sys_windows_386.s and sys_windows_amd64.s: - func getlasterror() uint32 - -+// When loading DLLs, we prefer to use LoadLibraryEx with -+// LOAD_LIBRARY_SEARCH_* flags, if available. LoadLibraryEx is not -+// available on old Windows, though, and the LOAD_LIBRARY_SEARCH_* -+// flags are not available on some versions of Windows without a -+// security patch. -+// -+// https://msdn.microsoft.com/en-us/library/ms684179(v=vs.85).aspx says: -+// "Windows 7, Windows Server 2008 R2, Windows Vista, and Windows -+// Server 2008: The LOAD_LIBRARY_SEARCH_* flags are available on -+// systems that have KB2533623 installed. To determine whether the -+// flags are available, use GetProcAddress to get the address of the -+// AddDllDirectory, RemoveDllDirectory, or SetDefaultDllDirectories -+// function. If GetProcAddress succeeds, the LOAD_LIBRARY_SEARCH_* -+// flags can be used with LoadLibraryEx." -+var useLoadLibraryEx bool -+ - var timeBeginPeriodRetValue uint32 - - // osRelaxMinNS indicates that sysmon shouldn't osRelax if the next -@@ -507,7 +556,6 @@ - initHighResTimer() - timeBeginPeriodRetValue = osRelax(false) - -- initSysDirectory() - initLongPathSupport() - - ncpu = getproccount() -@@ -524,7 +572,7 @@ - //go:nosplit - func readRandom(r []byte) int { - n := 0 -- if stdcall2(_ProcessPrng, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 { -+ if stdcall2(_RtlGenRandom, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 { - n = len(r) - } - return n -Index: src/net/hook_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/hook_windows.go b/src/net/hook_windows.go ---- a/src/net/hook_windows.go (revision 9779155f18b6556a034f7bb79fb7fb2aad1e26a9) -+++ b/src/net/hook_windows.go (revision ef0606261340e608017860b423ffae5c1ce78239) -@@ -13,6 +13,7 @@ - hostsFilePath = windows.GetSystemDirectory() + "/Drivers/etc/hosts" - - // 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 -Index: src/net/internal/socktest/main_test.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/internal/socktest/main_test.go b/src/net/internal/socktest/main_test.go ---- a/src/net/internal/socktest/main_test.go (revision 9779155f18b6556a034f7bb79fb7fb2aad1e26a9) -+++ b/src/net/internal/socktest/main_test.go (revision ef0606261340e608017860b423ffae5c1ce78239) -@@ -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 && !windows -+//go:build !js && !plan9 && !wasip1 - - package socktest_test - -Index: src/net/internal/socktest/main_windows_test.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/internal/socktest/main_windows_test.go b/src/net/internal/socktest/main_windows_test.go -new file mode 100644 ---- /dev/null (revision ef0606261340e608017860b423ffae5c1ce78239) -+++ b/src/net/internal/socktest/main_windows_test.go (revision ef0606261340e608017860b423ffae5c1ce78239) -@@ -0,0 +1,22 @@ -+// 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 -+} -Index: src/net/internal/socktest/sys_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/internal/socktest/sys_windows.go b/src/net/internal/socktest/sys_windows.go ---- a/src/net/internal/socktest/sys_windows.go (revision 9779155f18b6556a034f7bb79fb7fb2aad1e26a9) -+++ b/src/net/internal/socktest/sys_windows.go (revision ef0606261340e608017860b423ffae5c1ce78239) -@@ -9,6 +9,38 @@ - "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) -Index: src/net/main_windows_test.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/main_windows_test.go b/src/net/main_windows_test.go ---- a/src/net/main_windows_test.go (revision 9779155f18b6556a034f7bb79fb7fb2aad1e26a9) -+++ b/src/net/main_windows_test.go (revision ef0606261340e608017860b423ffae5c1ce78239) -@@ -8,6 +8,7 @@ - - var ( - // Placeholders for saving original socket system calls. -+ origSocket = socketFunc - origWSASocket = wsaSocketFunc - origClosesocket = poll.CloseFunc - origConnect = connectFunc -@@ -17,6 +18,7 @@ - ) - - func installTestHooks() { -+ socketFunc = sw.Socket - wsaSocketFunc = sw.WSASocket - poll.CloseFunc = sw.Closesocket - connectFunc = sw.Connect -@@ -26,6 +28,7 @@ - } - - func uninstallTestHooks() { -+ socketFunc = origSocket - wsaSocketFunc = origWSASocket - poll.CloseFunc = origClosesocket - connectFunc = origConnect -Index: src/net/sock_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/sock_windows.go b/src/net/sock_windows.go ---- a/src/net/sock_windows.go (revision 9779155f18b6556a034f7bb79fb7fb2aad1e26a9) -+++ b/src/net/sock_windows.go (revision ef0606261340e608017860b423ffae5c1ce78239) -@@ -20,6 +20,21 @@ - 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) - } -Index: src/syscall/exec_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/syscall/exec_windows.go b/src/syscall/exec_windows.go ---- a/src/syscall/exec_windows.go (revision 9779155f18b6556a034f7bb79fb7fb2aad1e26a9) -+++ b/src/syscall/exec_windows.go (revision 7f83badcb925a7e743188041cb6e561fc9b5b642) -@@ -14,7 +14,6 @@ - "unsafe" - ) - --// ForkLock is not used on Windows. - var ForkLock sync.RWMutex - - // EscapeArg rewrites command line argument s as prescribed -@@ -317,6 +316,17 @@ - } - } - -+ 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 { -@@ -325,7 +335,15 @@ - fd := make([]Handle, len(attr.Files)) - for i := range attr.Files { - if attr.Files[i] > 0 { -- err := DuplicateHandle(p, Handle(attr.Files[i]), parentProcess, &fd[i], 0, true, DUPLICATE_SAME_ACCESS) -+ 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) - if err != nil { - return 0, 0, err - } -@@ -356,6 +374,14 @@ - - 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 -Index: src/runtime/syscall_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/runtime/syscall_windows.go b/src/runtime/syscall_windows.go ---- a/src/runtime/syscall_windows.go (revision 7f83badcb925a7e743188041cb6e561fc9b5b642) -+++ b/src/runtime/syscall_windows.go (revision 83ff9782e024cb328b690cbf0da4e7848a327f4f) -@@ -413,23 +413,36 @@ - - const _LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800 - -+// When available, this function will use LoadLibraryEx with the filename -+// parameter and the important SEARCH_SYSTEM32 argument. But on systems that -+// do not have that option, absoluteFilepath should contain a fallback -+// to the full path inside of system32 for use with vanilla LoadLibrary. -+// - //go:linkname syscall_loadsystemlibrary syscall.loadsystemlibrary - //go:nosplit - //go:cgo_unsafe_args --func syscall_loadsystemlibrary(filename *uint16) (handle, err uintptr) { -+func syscall_loadsystemlibrary(filename *uint16, absoluteFilepath *uint16) (handle, err uintptr) { - lockOSThread() - c := &getg().m.syscall -- c.fn = getLoadLibraryEx() -- c.n = 3 -- args := struct { -- lpFileName *uint16 -- hFile uintptr // always 0 -- flags uint32 -- }{filename, 0, _LOAD_LIBRARY_SEARCH_SYSTEM32} -- c.args = uintptr(noescape(unsafe.Pointer(&args))) -+ -+ if useLoadLibraryEx { -+ c.fn = getLoadLibraryEx() -+ c.n = 3 -+ args := struct { -+ lpFileName *uint16 -+ hFile uintptr // always 0 -+ flags uint32 -+ }{filename, 0, _LOAD_LIBRARY_SEARCH_SYSTEM32} -+ c.args = uintptr(noescape(unsafe.Pointer(&args))) -+ } else { -+ c.fn = getLoadLibrary() -+ c.n = 1 -+ c.args = uintptr(noescape(unsafe.Pointer(&absoluteFilepath))) -+ } - - cgocall(asmstdcallAddr, unsafe.Pointer(c)) - KeepAlive(filename) -+ KeepAlive(absoluteFilepath) - handle = c.r1 - if handle == 0 { - err = c.err -Index: src/syscall/dll_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/syscall/dll_windows.go b/src/syscall/dll_windows.go ---- a/src/syscall/dll_windows.go (revision 7f83badcb925a7e743188041cb6e561fc9b5b642) -+++ b/src/syscall/dll_windows.go (revision 83ff9782e024cb328b690cbf0da4e7848a327f4f) -@@ -44,7 +44,7 @@ - - func SyscallN(trap uintptr, args ...uintptr) (r1, r2 uintptr, err Errno) - func loadlibrary(filename *uint16) (handle uintptr, err Errno) --func loadsystemlibrary(filename *uint16) (handle uintptr, err Errno) -+func loadsystemlibrary(filename *uint16, absoluteFilepath *uint16) (handle uintptr, err Errno) - func getprocaddress(handle uintptr, procname *uint8) (proc uintptr, err Errno) - - // A DLL implements access to a single DLL. -@@ -53,6 +53,9 @@ - Handle Handle - } - -+//go:linkname getSystemDirectory -+func getSystemDirectory() string // Implemented in runtime package. -+ - // LoadDLL loads the named DLL file into memory. - // - // If name is not an absolute path and is not a known system DLL used by -@@ -69,7 +72,11 @@ - var h uintptr - var e Errno - if sysdll.IsSystemDLL[name] { -- h, e = loadsystemlibrary(namep) -+ absoluteFilepathp, err := UTF16PtrFromString(getSystemDirectory() + name) -+ if err != nil { -+ return nil, err -+ } -+ h, e = loadsystemlibrary(namep, absoluteFilepathp) - } else { - h, e = loadlibrary(namep) - } diff --git a/mihomo/.github/patch/go1.23.patch b/mihomo/.github/patch/go1.23.patch deleted file mode 100644 index e8597548f7..0000000000 --- a/mihomo/.github/patch/go1.23.patch +++ /dev/null @@ -1,643 +0,0 @@ -Subject: [PATCH] Revert "runtime: always use LoadLibraryEx to load system libraries" -Revert "syscall: remove Windows 7 console handle workaround" -Revert "net: remove sysSocket fallback for Windows 7" -Revert "crypto/rand,runtime: switch RtlGenRandom for ProcessPrng" ---- -Index: src/crypto/rand/rand.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/crypto/rand/rand.go b/src/crypto/rand/rand.go ---- a/src/crypto/rand/rand.go (revision 6885bad7dd86880be6929c02085e5c7a67ff2887) -+++ b/src/crypto/rand/rand.go (revision 9ac42137ef6730e8b7daca016ece831297a1d75b) -@@ -16,7 +16,7 @@ - // - On macOS and iOS, Reader uses arc4random_buf(3). - // - On OpenBSD and NetBSD, Reader uses getentropy(2). - // - On other Unix-like systems, Reader reads from /dev/urandom. --// - On Windows, Reader uses the ProcessPrng API. -+// - On Windows systems, Reader uses the RtlGenRandom 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 -Index: src/crypto/rand/rand_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/crypto/rand/rand_windows.go b/src/crypto/rand/rand_windows.go ---- a/src/crypto/rand/rand_windows.go (revision 6885bad7dd86880be6929c02085e5c7a67ff2887) -+++ b/src/crypto/rand/rand_windows.go (revision 9ac42137ef6730e8b7daca016ece831297a1d75b) -@@ -15,8 +15,11 @@ - - type rngReader struct{} - --func (r *rngReader) Read(b []byte) (int, error) { -- if err := windows.ProcessPrng(b); err != nil { -+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 { - return 0, err - } - return len(b), nil -Index: src/internal/syscall/windows/syscall_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/internal/syscall/windows/syscall_windows.go b/src/internal/syscall/windows/syscall_windows.go ---- a/src/internal/syscall/windows/syscall_windows.go (revision 6885bad7dd86880be6929c02085e5c7a67ff2887) -+++ b/src/internal/syscall/windows/syscall_windows.go (revision 9ac42137ef6730e8b7daca016ece831297a1d75b) -@@ -414,7 +414,7 @@ - //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 ProcessPrng(buf []byte) (err error) = bcryptprimitives.ProcessPrng -+//sys RtlGenRandom(buf []byte) (err error) = advapi32.SystemFunction036 - - type FILE_ID_BOTH_DIR_INFO struct { - NextEntryOffset uint32 -Index: src/internal/syscall/windows/zsyscall_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/internal/syscall/windows/zsyscall_windows.go b/src/internal/syscall/windows/zsyscall_windows.go ---- a/src/internal/syscall/windows/zsyscall_windows.go (revision 6885bad7dd86880be6929c02085e5c7a67ff2887) -+++ b/src/internal/syscall/windows/zsyscall_windows.go (revision 9ac42137ef6730e8b7daca016ece831297a1d75b) -@@ -38,7 +38,6 @@ - - var ( - 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")) -@@ -57,7 +56,7 @@ - procQueryServiceStatus = modadvapi32.NewProc("QueryServiceStatus") - procRevertToSelf = modadvapi32.NewProc("RevertToSelf") - procSetTokenInformation = modadvapi32.NewProc("SetTokenInformation") -- procProcessPrng = modbcryptprimitives.NewProc("ProcessPrng") -+ procSystemFunction036 = modadvapi32.NewProc("SystemFunction036") - procGetAdaptersAddresses = modiphlpapi.NewProc("GetAdaptersAddresses") - procCreateEventW = modkernel32.NewProc("CreateEventW") - procGetACP = modkernel32.NewProc("GetACP") -@@ -183,12 +182,12 @@ - return - } - --func ProcessPrng(buf []byte) (err error) { -+func RtlGenRandom(buf []byte) (err error) { - var _p0 *byte - if len(buf) > 0 { - _p0 = &buf[0] - } -- r1, _, e1 := syscall.Syscall(procProcessPrng.Addr(), 2, uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), 0) -+ r1, _, e1 := syscall.Syscall(procSystemFunction036.Addr(), 2, uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), 0) - if r1 == 0 { - err = errnoErr(e1) - } -Index: src/runtime/os_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go ---- a/src/runtime/os_windows.go (revision 6885bad7dd86880be6929c02085e5c7a67ff2887) -+++ b/src/runtime/os_windows.go (revision 69e2eed6dd0f6d815ebf15797761c13f31213dd6) -@@ -39,8 +39,8 @@ - //go:cgo_import_dynamic runtime._GetSystemInfo GetSystemInfo%1 "kernel32.dll" - //go:cgo_import_dynamic runtime._GetThreadContext GetThreadContext%2 "kernel32.dll" - //go:cgo_import_dynamic runtime._SetThreadContext SetThreadContext%2 "kernel32.dll" --//go:cgo_import_dynamic runtime._LoadLibraryExW LoadLibraryExW%3 "kernel32.dll" - //go:cgo_import_dynamic runtime._LoadLibraryW LoadLibraryW%1 "kernel32.dll" -+//go:cgo_import_dynamic runtime._LoadLibraryA LoadLibraryA%1 "kernel32.dll" - //go:cgo_import_dynamic runtime._PostQueuedCompletionStatus PostQueuedCompletionStatus%4 "kernel32.dll" - //go:cgo_import_dynamic runtime._QueryPerformanceCounter QueryPerformanceCounter%1 "kernel32.dll" - //go:cgo_import_dynamic runtime._QueryPerformanceFrequency QueryPerformanceFrequency%1 "kernel32.dll" -@@ -74,7 +74,6 @@ - // Following syscalls are available on every Windows PC. - // All these variables are set by the Windows executable - // loader before the Go program starts. -- _AddVectoredContinueHandler, - _AddVectoredExceptionHandler, - _CloseHandle, - _CreateEventA, -@@ -97,8 +96,8 @@ - _GetSystemInfo, - _GetThreadContext, - _SetThreadContext, -- _LoadLibraryExW, - _LoadLibraryW, -+ _LoadLibraryA, - _PostQueuedCompletionStatus, - _QueryPerformanceCounter, - _QueryPerformanceFrequency, -@@ -127,8 +126,23 @@ - _WriteFile, - _ stdFunction - -- // Use ProcessPrng to generate cryptographically random data. -- _ProcessPrng stdFunction -+ // Following syscalls are only available on some Windows PCs. -+ // We will load syscalls, if available, before using them. -+ _AddDllDirectory, -+ _AddVectoredContinueHandler, -+ _LoadLibraryExA, -+ _LoadLibraryExW, -+ _ 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 - - // Load ntdll.dll manually during startup, otherwise Mingw - // links wrong printf function to cgo executable (see issue -@@ -145,13 +159,6 @@ - _ stdFunction - ) - --var ( -- 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} --) -- - // Function to be called by windows CreateThread - // to start new os thread. - func tstart_stdcall(newm *m) -@@ -244,8 +251,18 @@ - return unsafe.String(&sysDirectory[0], sysDirectoryLen) - } - --func windowsLoadSystemLib(name []uint16) uintptr { -- return stdcall3(_LoadLibraryExW, uintptr(unsafe.Pointer(&name[0])), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32) -+//go:linkname syscall_getSystemDirectory syscall.getSystemDirectory -+func syscall_getSystemDirectory() string { -+ return unsafe.String(&sysDirectory[0], sysDirectoryLen) -+} -+ -+func windowsLoadSystemLib(name []byte) uintptr { -+ if useLoadLibraryEx { -+ return stdcall3(_LoadLibraryExA, uintptr(unsafe.Pointer(&name[0])), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32) -+ } else { -+ absName := append(sysDirectory[:sysDirectoryLen], name...) -+ return stdcall1(_LoadLibraryA, uintptr(unsafe.Pointer(&absName[0]))) -+ } - } - - //go:linkname windows_QueryPerformanceCounter internal/syscall/windows.QueryPerformanceCounter -@@ -263,13 +280,28 @@ - } - - func loadOptionalSyscalls() { -- bcryptPrimitives := windowsLoadSystemLib(bcryptprimitivesdll[:]) -- if bcryptPrimitives == 0 { -- throw("bcryptprimitives.dll not found") -+ var kernel32dll = []byte("kernel32.dll\000") -+ k32 := stdcall1(_LoadLibraryA, uintptr(unsafe.Pointer(&kernel32dll[0]))) -+ if k32 == 0 { -+ throw("kernel32.dll not found") - } -- _ProcessPrng = windowsFindfunc(bcryptPrimitives, []byte("ProcessPrng\000")) -+ _AddDllDirectory = windowsFindfunc(k32, []byte("AddDllDirectory\000")) -+ _AddVectoredContinueHandler = windowsFindfunc(k32, []byte("AddVectoredContinueHandler\000")) -+ _LoadLibraryExA = windowsFindfunc(k32, []byte("LoadLibraryExA\000")) -+ _LoadLibraryExW = windowsFindfunc(k32, []byte("LoadLibraryExW\000")) -+ useLoadLibraryEx = (_LoadLibraryExW != nil && _LoadLibraryExA != nil && _AddDllDirectory != nil) -+ -+ initSysDirectory() - -- n32 := windowsLoadSystemLib(ntdlldll[:]) -+ var advapi32dll = []byte("advapi32.dll\000") -+ a32 := windowsLoadSystemLib(advapi32dll) -+ if a32 == 0 { -+ throw("advapi32.dll not found") -+ } -+ _RtlGenRandom = windowsFindfunc(a32, []byte("SystemFunction036\000")) -+ -+ var ntdll = []byte("ntdll.dll\000") -+ n32 := windowsLoadSystemLib(ntdll) - if n32 == 0 { - throw("ntdll.dll not found") - } -@@ -298,7 +330,7 @@ - context uintptr - } - -- powrprof := windowsLoadSystemLib(powrprofdll[:]) -+ powrprof := windowsLoadSystemLib([]byte("powrprof.dll\000")) - if powrprof == 0 { - return // Running on Windows 7, where we don't need it anyway. - } -@@ -357,6 +389,22 @@ - // in sys_windows_386.s and sys_windows_amd64.s: - func getlasterror() uint32 - -+// When loading DLLs, we prefer to use LoadLibraryEx with -+// LOAD_LIBRARY_SEARCH_* flags, if available. LoadLibraryEx is not -+// available on old Windows, though, and the LOAD_LIBRARY_SEARCH_* -+// flags are not available on some versions of Windows without a -+// security patch. -+// -+// https://msdn.microsoft.com/en-us/library/ms684179(v=vs.85).aspx says: -+// "Windows 7, Windows Server 2008 R2, Windows Vista, and Windows -+// Server 2008: The LOAD_LIBRARY_SEARCH_* flags are available on -+// systems that have KB2533623 installed. To determine whether the -+// flags are available, use GetProcAddress to get the address of the -+// AddDllDirectory, RemoveDllDirectory, or SetDefaultDllDirectories -+// function. If GetProcAddress succeeds, the LOAD_LIBRARY_SEARCH_* -+// flags can be used with LoadLibraryEx." -+var useLoadLibraryEx bool -+ - var timeBeginPeriodRetValue uint32 - - // osRelaxMinNS indicates that sysmon shouldn't osRelax if the next -@@ -430,7 +478,8 @@ - // Only load winmm.dll if we need it. - // This avoids a dependency on winmm.dll for Go programs - // that run on new Windows versions. -- m32 := windowsLoadSystemLib(winmmdll[:]) -+ var winmmdll = []byte("winmm.dll\000") -+ m32 := windowsLoadSystemLib(winmmdll) - if m32 == 0 { - print("runtime: LoadLibraryExW failed; errno=", getlasterror(), "\n") - throw("winmm.dll not found") -@@ -471,6 +520,28 @@ - canUseLongPaths = true - } - -+var osVersionInfo struct { -+ majorVersion uint32 -+ minorVersion uint32 -+ buildNumber uint32 -+} -+ -+func initOsVersionInfo() { -+ info := _OSVERSIONINFOW{} -+ info.osVersionInfoSize = uint32(unsafe.Sizeof(info)) -+ stdcall1(_RtlGetVersion, uintptr(unsafe.Pointer(&info))) -+ osVersionInfo.majorVersion = info.majorVersion -+ osVersionInfo.minorVersion = info.minorVersion -+ osVersionInfo.buildNumber = info.buildNumber -+} -+ -+//go:linkname rtlGetNtVersionNumbers syscall.rtlGetNtVersionNumbers -+func rtlGetNtVersionNumbers(majorVersion *uint32, minorVersion *uint32, buildNumber *uint32) { -+ *majorVersion = osVersionInfo.majorVersion -+ *minorVersion = osVersionInfo.minorVersion -+ *buildNumber = osVersionInfo.buildNumber -+} -+ - func osinit() { - asmstdcallAddr = unsafe.Pointer(abi.FuncPCABI0(asmstdcall)) - -@@ -483,8 +554,8 @@ - initHighResTimer() - timeBeginPeriodRetValue = osRelax(false) - -- initSysDirectory() - initLongPathSupport() -+ initOsVersionInfo() - - ncpu = getproccount() - -@@ -500,7 +571,7 @@ - //go:nosplit - func readRandom(r []byte) int { - n := 0 -- if stdcall2(_ProcessPrng, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 { -+ if stdcall2(_RtlGenRandom, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 { - n = len(r) - } - return n -Index: src/net/hook_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/hook_windows.go b/src/net/hook_windows.go ---- a/src/net/hook_windows.go (revision 9ac42137ef6730e8b7daca016ece831297a1d75b) -+++ b/src/net/hook_windows.go (revision 21290de8a4c91408de7c2b5b68757b1e90af49dd) -@@ -13,6 +13,7 @@ - hostsFilePath = windows.GetSystemDirectory() + "/Drivers/etc/hosts" - - // 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 -Index: src/net/internal/socktest/main_test.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/internal/socktest/main_test.go b/src/net/internal/socktest/main_test.go ---- a/src/net/internal/socktest/main_test.go (revision 9ac42137ef6730e8b7daca016ece831297a1d75b) -+++ b/src/net/internal/socktest/main_test.go (revision 21290de8a4c91408de7c2b5b68757b1e90af49dd) -@@ -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 && !windows -+//go:build !js && !plan9 && !wasip1 - - package socktest_test - -Index: src/net/internal/socktest/main_windows_test.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/internal/socktest/main_windows_test.go b/src/net/internal/socktest/main_windows_test.go -new file mode 100644 ---- /dev/null (revision 21290de8a4c91408de7c2b5b68757b1e90af49dd) -+++ b/src/net/internal/socktest/main_windows_test.go (revision 21290de8a4c91408de7c2b5b68757b1e90af49dd) -@@ -0,0 +1,22 @@ -+// 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 -+} -Index: src/net/internal/socktest/sys_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/internal/socktest/sys_windows.go b/src/net/internal/socktest/sys_windows.go ---- a/src/net/internal/socktest/sys_windows.go (revision 9ac42137ef6730e8b7daca016ece831297a1d75b) -+++ b/src/net/internal/socktest/sys_windows.go (revision 21290de8a4c91408de7c2b5b68757b1e90af49dd) -@@ -9,6 +9,38 @@ - "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) -Index: src/net/main_windows_test.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/main_windows_test.go b/src/net/main_windows_test.go ---- a/src/net/main_windows_test.go (revision 9ac42137ef6730e8b7daca016ece831297a1d75b) -+++ b/src/net/main_windows_test.go (revision 21290de8a4c91408de7c2b5b68757b1e90af49dd) -@@ -8,6 +8,7 @@ - - var ( - // Placeholders for saving original socket system calls. -+ origSocket = socketFunc - origWSASocket = wsaSocketFunc - origClosesocket = poll.CloseFunc - origConnect = connectFunc -@@ -17,6 +18,7 @@ - ) - - func installTestHooks() { -+ socketFunc = sw.Socket - wsaSocketFunc = sw.WSASocket - poll.CloseFunc = sw.Closesocket - connectFunc = sw.Connect -@@ -26,6 +28,7 @@ - } - - func uninstallTestHooks() { -+ socketFunc = origSocket - wsaSocketFunc = origWSASocket - poll.CloseFunc = origClosesocket - connectFunc = origConnect -Index: src/net/sock_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/sock_windows.go b/src/net/sock_windows.go ---- a/src/net/sock_windows.go (revision 9ac42137ef6730e8b7daca016ece831297a1d75b) -+++ b/src/net/sock_windows.go (revision 21290de8a4c91408de7c2b5b68757b1e90af49dd) -@@ -20,6 +20,21 @@ - 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) - } -Index: src/syscall/exec_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/syscall/exec_windows.go b/src/syscall/exec_windows.go ---- a/src/syscall/exec_windows.go (revision 9ac42137ef6730e8b7daca016ece831297a1d75b) -+++ b/src/syscall/exec_windows.go (revision 6a31d3fa8e47ddabc10bd97bff10d9a85f4cfb76) -@@ -14,7 +14,6 @@ - "unsafe" - ) - --// ForkLock is not used on Windows. - var ForkLock sync.RWMutex - - // EscapeArg rewrites command line argument s as prescribed -@@ -254,6 +253,9 @@ - var zeroProcAttr ProcAttr - var zeroSysProcAttr SysProcAttr - -+//go:linkname rtlGetNtVersionNumbers -+func rtlGetNtVersionNumbers(majorVersion *uint32, minorVersion *uint32, buildNumber *uint32) -+ - func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle uintptr, err error) { - if len(argv0) == 0 { - return 0, 0, EWINDOWS -@@ -317,6 +319,17 @@ - } - } - -+ 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 { -@@ -325,7 +338,15 @@ - fd := make([]Handle, len(attr.Files)) - for i := range attr.Files { - if attr.Files[i] > 0 { -- err := DuplicateHandle(p, Handle(attr.Files[i]), parentProcess, &fd[i], 0, true, DUPLICATE_SAME_ACCESS) -+ 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) - if err != nil { - return 0, 0, err - } -@@ -356,6 +377,14 @@ - - 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 -Index: src/runtime/syscall_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/runtime/syscall_windows.go b/src/runtime/syscall_windows.go ---- a/src/runtime/syscall_windows.go (revision 6a31d3fa8e47ddabc10bd97bff10d9a85f4cfb76) -+++ b/src/runtime/syscall_windows.go (revision 69e2eed6dd0f6d815ebf15797761c13f31213dd6) -@@ -413,10 +413,20 @@ - - const _LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800 - -+// When available, this function will use LoadLibraryEx with the filename -+// parameter and the important SEARCH_SYSTEM32 argument. But on systems that -+// do not have that option, absoluteFilepath should contain a fallback -+// to the full path inside of system32 for use with vanilla LoadLibrary. -+// - //go:linkname syscall_loadsystemlibrary syscall.loadsystemlibrary --func syscall_loadsystemlibrary(filename *uint16) (handle, err uintptr) { -- handle, _, err = syscall_SyscallN(uintptr(unsafe.Pointer(_LoadLibraryExW)), uintptr(unsafe.Pointer(filename)), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32) -+func syscall_loadsystemlibrary(filename *uint16, absoluteFilepath *uint16) (handle, err uintptr) { -+ if useLoadLibraryEx { -+ handle, _, err = syscall_SyscallN(uintptr(unsafe.Pointer(_LoadLibraryExW)), uintptr(unsafe.Pointer(filename)), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32) -+ } else { -+ handle, _, err = syscall_SyscallN(uintptr(unsafe.Pointer(_LoadLibraryW)), uintptr(unsafe.Pointer(absoluteFilepath))) -+ } - KeepAlive(filename) -+ KeepAlive(absoluteFilepath) - if handle != 0 { - err = 0 - } -Index: src/syscall/dll_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/syscall/dll_windows.go b/src/syscall/dll_windows.go ---- a/src/syscall/dll_windows.go (revision 6a31d3fa8e47ddabc10bd97bff10d9a85f4cfb76) -+++ b/src/syscall/dll_windows.go (revision 69e2eed6dd0f6d815ebf15797761c13f31213dd6) -@@ -44,7 +44,7 @@ - - func SyscallN(trap uintptr, args ...uintptr) (r1, r2 uintptr, err Errno) - func loadlibrary(filename *uint16) (handle uintptr, err Errno) --func loadsystemlibrary(filename *uint16) (handle uintptr, err Errno) -+func loadsystemlibrary(filename *uint16, absoluteFilepath *uint16) (handle uintptr, err Errno) - func getprocaddress(handle uintptr, procname *uint8) (proc uintptr, err Errno) - - // A DLL implements access to a single DLL. -@@ -53,6 +53,9 @@ - Handle Handle - } - -+//go:linkname getSystemDirectory -+func getSystemDirectory() string // Implemented in runtime package. -+ - // LoadDLL loads the named DLL file into memory. - // - // If name is not an absolute path and is not a known system DLL used by -@@ -69,7 +72,11 @@ - var h uintptr - var e Errno - if sysdll.IsSystemDLL[name] { -- h, e = loadsystemlibrary(namep) -+ absoluteFilepathp, err := UTF16PtrFromString(getSystemDirectory() + name) -+ if err != nil { -+ return nil, err -+ } -+ h, e = loadsystemlibrary(namep, absoluteFilepathp) - } else { - h, e = loadlibrary(namep) - } diff --git a/mihomo/.github/patch/go1.24.patch b/mihomo/.github/patch/go1.24.patch deleted file mode 100644 index ecfc99ff73..0000000000 --- a/mihomo/.github/patch/go1.24.patch +++ /dev/null @@ -1,657 +0,0 @@ -Subject: [PATCH] Revert "runtime: always use LoadLibraryEx to load system libraries" -Revert "syscall: remove Windows 7 console handle workaround" -Revert "net: remove sysSocket fallback for Windows 7" -Revert "crypto/rand,runtime: switch RtlGenRandom for ProcessPrng" ---- -Index: src/crypto/internal/sysrand/rand_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/crypto/internal/sysrand/rand_windows.go b/src/crypto/internal/sysrand/rand_windows.go ---- a/src/crypto/internal/sysrand/rand_windows.go (revision 3901409b5d0fb7c85a3e6730a59943cc93b2835c) -+++ b/src/crypto/internal/sysrand/rand_windows.go (revision 2a406dc9f1ea7323d6ca9fccb2fe9ddebb6b1cc8) -@@ -7,5 +7,26 @@ - import "internal/syscall/windows" - - func read(b []byte) error { -- return windows.ProcessPrng(b) -+ // 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. -+ return batched(windows.RtlGenRandom, 1<<31-1)(b) -+} -+ -+// batched returns a function that calls f to populate a []byte by chunking it -+// into subslices of, at most, readMax bytes. -+func batched(f func([]byte) error, readMax int) func([]byte) error { -+ return func(out []byte) error { -+ for len(out) > 0 { -+ read := len(out) -+ if read > readMax { -+ read = readMax -+ } -+ if err := f(out[:read]); err != nil { -+ return err -+ } -+ out = out[read:] -+ } -+ return nil -+ } - } -Index: src/crypto/rand/rand.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/crypto/rand/rand.go b/src/crypto/rand/rand.go ---- a/src/crypto/rand/rand.go (revision 3901409b5d0fb7c85a3e6730a59943cc93b2835c) -+++ b/src/crypto/rand/rand.go (revision 2a406dc9f1ea7323d6ca9fccb2fe9ddebb6b1cc8) -@@ -22,7 +22,7 @@ - // - On legacy Linux (< 3.17), Reader opens /dev/urandom on first use. - // - On macOS, iOS, and OpenBSD Reader, uses arc4random_buf(3). - // - On NetBSD, Reader uses the kern.arandom sysctl. --// - On Windows, Reader uses the ProcessPrng API. -+// - On Windows systems, Reader uses the RtlGenRandom API. - // - On js/wasm, Reader uses the Web Crypto API. - // - On wasip1/wasm, Reader uses random_get. - // -Index: src/internal/syscall/windows/syscall_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/internal/syscall/windows/syscall_windows.go b/src/internal/syscall/windows/syscall_windows.go ---- a/src/internal/syscall/windows/syscall_windows.go (revision 3901409b5d0fb7c85a3e6730a59943cc93b2835c) -+++ b/src/internal/syscall/windows/syscall_windows.go (revision 2a406dc9f1ea7323d6ca9fccb2fe9ddebb6b1cc8) -@@ -416,7 +416,7 @@ - //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 ProcessPrng(buf []byte) (err error) = bcryptprimitives.ProcessPrng -+//sys RtlGenRandom(buf []byte) (err error) = advapi32.SystemFunction036 - - type FILE_ID_BOTH_DIR_INFO struct { - NextEntryOffset uint32 -Index: src/internal/syscall/windows/zsyscall_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/internal/syscall/windows/zsyscall_windows.go b/src/internal/syscall/windows/zsyscall_windows.go ---- a/src/internal/syscall/windows/zsyscall_windows.go (revision 3901409b5d0fb7c85a3e6730a59943cc93b2835c) -+++ b/src/internal/syscall/windows/zsyscall_windows.go (revision 2a406dc9f1ea7323d6ca9fccb2fe9ddebb6b1cc8) -@@ -38,7 +38,6 @@ - - var ( - 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")) -@@ -63,7 +62,7 @@ - procQueryServiceStatus = modadvapi32.NewProc("QueryServiceStatus") - procRevertToSelf = modadvapi32.NewProc("RevertToSelf") - procSetTokenInformation = modadvapi32.NewProc("SetTokenInformation") -- procProcessPrng = modbcryptprimitives.NewProc("ProcessPrng") -+ procSystemFunction036 = modadvapi32.NewProc("SystemFunction036") - procGetAdaptersAddresses = modiphlpapi.NewProc("GetAdaptersAddresses") - procCreateEventW = modkernel32.NewProc("CreateEventW") - procGetACP = modkernel32.NewProc("GetACP") -@@ -236,12 +235,12 @@ - return - } - --func ProcessPrng(buf []byte) (err error) { -+func RtlGenRandom(buf []byte) (err error) { - var _p0 *byte - if len(buf) > 0 { - _p0 = &buf[0] - } -- r1, _, e1 := syscall.Syscall(procProcessPrng.Addr(), 2, uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), 0) -+ r1, _, e1 := syscall.Syscall(procSystemFunction036.Addr(), 2, uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), 0) - if r1 == 0 { - err = errnoErr(e1) - } -Index: src/runtime/os_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go ---- a/src/runtime/os_windows.go (revision 3901409b5d0fb7c85a3e6730a59943cc93b2835c) -+++ b/src/runtime/os_windows.go (revision ac3e93c061779dfefc0dd13a5b6e6f764a25621e) -@@ -40,8 +40,8 @@ - //go:cgo_import_dynamic runtime._GetSystemInfo GetSystemInfo%1 "kernel32.dll" - //go:cgo_import_dynamic runtime._GetThreadContext GetThreadContext%2 "kernel32.dll" - //go:cgo_import_dynamic runtime._SetThreadContext SetThreadContext%2 "kernel32.dll" --//go:cgo_import_dynamic runtime._LoadLibraryExW LoadLibraryExW%3 "kernel32.dll" - //go:cgo_import_dynamic runtime._LoadLibraryW LoadLibraryW%1 "kernel32.dll" -+//go:cgo_import_dynamic runtime._LoadLibraryA LoadLibraryA%1 "kernel32.dll" - //go:cgo_import_dynamic runtime._PostQueuedCompletionStatus PostQueuedCompletionStatus%4 "kernel32.dll" - //go:cgo_import_dynamic runtime._QueryPerformanceCounter QueryPerformanceCounter%1 "kernel32.dll" - //go:cgo_import_dynamic runtime._QueryPerformanceFrequency QueryPerformanceFrequency%1 "kernel32.dll" -@@ -75,7 +75,6 @@ - // Following syscalls are available on every Windows PC. - // All these variables are set by the Windows executable - // loader before the Go program starts. -- _AddVectoredContinueHandler, - _AddVectoredExceptionHandler, - _CloseHandle, - _CreateEventA, -@@ -98,8 +97,8 @@ - _GetSystemInfo, - _GetThreadContext, - _SetThreadContext, -- _LoadLibraryExW, - _LoadLibraryW, -+ _LoadLibraryA, - _PostQueuedCompletionStatus, - _QueryPerformanceCounter, - _QueryPerformanceFrequency, -@@ -128,8 +127,23 @@ - _WriteFile, - _ stdFunction - -- // Use ProcessPrng to generate cryptographically random data. -- _ProcessPrng stdFunction -+ // Following syscalls are only available on some Windows PCs. -+ // We will load syscalls, if available, before using them. -+ _AddDllDirectory, -+ _AddVectoredContinueHandler, -+ _LoadLibraryExA, -+ _LoadLibraryExW, -+ _ 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 - - // Load ntdll.dll manually during startup, otherwise Mingw - // links wrong printf function to cgo executable (see issue -@@ -146,13 +160,6 @@ - _ stdFunction - ) - --var ( -- 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} --) -- - // Function to be called by windows CreateThread - // to start new os thread. - func tstart_stdcall(newm *m) -@@ -245,8 +252,18 @@ - return unsafe.String(&sysDirectory[0], sysDirectoryLen) - } - --func windowsLoadSystemLib(name []uint16) uintptr { -- return stdcall3(_LoadLibraryExW, uintptr(unsafe.Pointer(&name[0])), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32) -+//go:linkname syscall_getSystemDirectory syscall.getSystemDirectory -+func syscall_getSystemDirectory() string { -+ return unsafe.String(&sysDirectory[0], sysDirectoryLen) -+} -+ -+func windowsLoadSystemLib(name []byte) uintptr { -+ if useLoadLibraryEx { -+ return stdcall3(_LoadLibraryExA, uintptr(unsafe.Pointer(&name[0])), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32) -+ } else { -+ absName := append(sysDirectory[:sysDirectoryLen], name...) -+ return stdcall1(_LoadLibraryA, uintptr(unsafe.Pointer(&absName[0]))) -+ } - } - - //go:linkname windows_QueryPerformanceCounter internal/syscall/windows.QueryPerformanceCounter -@@ -264,13 +281,28 @@ - } - - func loadOptionalSyscalls() { -- bcryptPrimitives := windowsLoadSystemLib(bcryptprimitivesdll[:]) -- if bcryptPrimitives == 0 { -- throw("bcryptprimitives.dll not found") -+ var kernel32dll = []byte("kernel32.dll\000") -+ k32 := stdcall1(_LoadLibraryA, uintptr(unsafe.Pointer(&kernel32dll[0]))) -+ if k32 == 0 { -+ throw("kernel32.dll not found") - } -- _ProcessPrng = windowsFindfunc(bcryptPrimitives, []byte("ProcessPrng\000")) -+ _AddDllDirectory = windowsFindfunc(k32, []byte("AddDllDirectory\000")) -+ _AddVectoredContinueHandler = windowsFindfunc(k32, []byte("AddVectoredContinueHandler\000")) -+ _LoadLibraryExA = windowsFindfunc(k32, []byte("LoadLibraryExA\000")) -+ _LoadLibraryExW = windowsFindfunc(k32, []byte("LoadLibraryExW\000")) -+ useLoadLibraryEx = (_LoadLibraryExW != nil && _LoadLibraryExA != nil && _AddDllDirectory != nil) -+ -+ initSysDirectory() - -- n32 := windowsLoadSystemLib(ntdlldll[:]) -+ var advapi32dll = []byte("advapi32.dll\000") -+ a32 := windowsLoadSystemLib(advapi32dll) -+ if a32 == 0 { -+ throw("advapi32.dll not found") -+ } -+ _RtlGenRandom = windowsFindfunc(a32, []byte("SystemFunction036\000")) -+ -+ var ntdll = []byte("ntdll.dll\000") -+ n32 := windowsLoadSystemLib(ntdll) - if n32 == 0 { - throw("ntdll.dll not found") - } -@@ -299,7 +331,7 @@ - context uintptr - } - -- powrprof := windowsLoadSystemLib(powrprofdll[:]) -+ powrprof := windowsLoadSystemLib([]byte("powrprof.dll\000")) - if powrprof == 0 { - return // Running on Windows 7, where we don't need it anyway. - } -@@ -358,6 +390,22 @@ - // in sys_windows_386.s and sys_windows_amd64.s: - func getlasterror() uint32 - -+// When loading DLLs, we prefer to use LoadLibraryEx with -+// LOAD_LIBRARY_SEARCH_* flags, if available. LoadLibraryEx is not -+// available on old Windows, though, and the LOAD_LIBRARY_SEARCH_* -+// flags are not available on some versions of Windows without a -+// security patch. -+// -+// https://msdn.microsoft.com/en-us/library/ms684179(v=vs.85).aspx says: -+// "Windows 7, Windows Server 2008 R2, Windows Vista, and Windows -+// Server 2008: The LOAD_LIBRARY_SEARCH_* flags are available on -+// systems that have KB2533623 installed. To determine whether the -+// flags are available, use GetProcAddress to get the address of the -+// AddDllDirectory, RemoveDllDirectory, or SetDefaultDllDirectories -+// function. If GetProcAddress succeeds, the LOAD_LIBRARY_SEARCH_* -+// flags can be used with LoadLibraryEx." -+var useLoadLibraryEx bool -+ - var timeBeginPeriodRetValue uint32 - - // osRelaxMinNS indicates that sysmon shouldn't osRelax if the next -@@ -431,7 +479,8 @@ - // Only load winmm.dll if we need it. - // This avoids a dependency on winmm.dll for Go programs - // that run on new Windows versions. -- m32 := windowsLoadSystemLib(winmmdll[:]) -+ var winmmdll = []byte("winmm.dll\000") -+ m32 := windowsLoadSystemLib(winmmdll) - if m32 == 0 { - print("runtime: LoadLibraryExW failed; errno=", getlasterror(), "\n") - throw("winmm.dll not found") -@@ -472,6 +521,28 @@ - canUseLongPaths = true - } - -+var osVersionInfo struct { -+ majorVersion uint32 -+ minorVersion uint32 -+ buildNumber uint32 -+} -+ -+func initOsVersionInfo() { -+ info := _OSVERSIONINFOW{} -+ info.osVersionInfoSize = uint32(unsafe.Sizeof(info)) -+ stdcall1(_RtlGetVersion, uintptr(unsafe.Pointer(&info))) -+ osVersionInfo.majorVersion = info.majorVersion -+ osVersionInfo.minorVersion = info.minorVersion -+ osVersionInfo.buildNumber = info.buildNumber -+} -+ -+//go:linkname rtlGetNtVersionNumbers syscall.rtlGetNtVersionNumbers -+func rtlGetNtVersionNumbers(majorVersion *uint32, minorVersion *uint32, buildNumber *uint32) { -+ *majorVersion = osVersionInfo.majorVersion -+ *minorVersion = osVersionInfo.minorVersion -+ *buildNumber = osVersionInfo.buildNumber -+} -+ - func osinit() { - asmstdcallAddr = unsafe.Pointer(abi.FuncPCABI0(asmstdcall)) - -@@ -484,8 +555,8 @@ - initHighResTimer() - timeBeginPeriodRetValue = osRelax(false) - -- initSysDirectory() - initLongPathSupport() -+ initOsVersionInfo() - - ncpu = getproccount() - -@@ -501,7 +572,7 @@ - //go:nosplit - func readRandom(r []byte) int { - n := 0 -- if stdcall2(_ProcessPrng, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 { -+ if stdcall2(_RtlGenRandom, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 { - n = len(r) - } - return n -Index: src/net/hook_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/hook_windows.go b/src/net/hook_windows.go ---- a/src/net/hook_windows.go (revision 2a406dc9f1ea7323d6ca9fccb2fe9ddebb6b1cc8) -+++ b/src/net/hook_windows.go (revision 7b1fd7d39c6be0185fbe1d929578ab372ac5c632) -@@ -13,6 +13,7 @@ - hostsFilePath = windows.GetSystemDirectory() + "/Drivers/etc/hosts" - - // 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 -Index: src/net/internal/socktest/main_test.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/internal/socktest/main_test.go b/src/net/internal/socktest/main_test.go ---- a/src/net/internal/socktest/main_test.go (revision 2a406dc9f1ea7323d6ca9fccb2fe9ddebb6b1cc8) -+++ b/src/net/internal/socktest/main_test.go (revision 7b1fd7d39c6be0185fbe1d929578ab372ac5c632) -@@ -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 && !windows -+//go:build !js && !plan9 && !wasip1 - - package socktest_test - -Index: src/net/internal/socktest/main_windows_test.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/internal/socktest/main_windows_test.go b/src/net/internal/socktest/main_windows_test.go -new file mode 100644 ---- /dev/null (revision 7b1fd7d39c6be0185fbe1d929578ab372ac5c632) -+++ b/src/net/internal/socktest/main_windows_test.go (revision 7b1fd7d39c6be0185fbe1d929578ab372ac5c632) -@@ -0,0 +1,22 @@ -+// 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 -+} -Index: src/net/internal/socktest/sys_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/internal/socktest/sys_windows.go b/src/net/internal/socktest/sys_windows.go ---- a/src/net/internal/socktest/sys_windows.go (revision 2a406dc9f1ea7323d6ca9fccb2fe9ddebb6b1cc8) -+++ b/src/net/internal/socktest/sys_windows.go (revision 7b1fd7d39c6be0185fbe1d929578ab372ac5c632) -@@ -9,6 +9,38 @@ - "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) -Index: src/net/main_windows_test.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/main_windows_test.go b/src/net/main_windows_test.go ---- a/src/net/main_windows_test.go (revision 2a406dc9f1ea7323d6ca9fccb2fe9ddebb6b1cc8) -+++ b/src/net/main_windows_test.go (revision 7b1fd7d39c6be0185fbe1d929578ab372ac5c632) -@@ -8,6 +8,7 @@ - - var ( - // Placeholders for saving original socket system calls. -+ origSocket = socketFunc - origWSASocket = wsaSocketFunc - origClosesocket = poll.CloseFunc - origConnect = connectFunc -@@ -17,6 +18,7 @@ - ) - - func installTestHooks() { -+ socketFunc = sw.Socket - wsaSocketFunc = sw.WSASocket - poll.CloseFunc = sw.Closesocket - connectFunc = sw.Connect -@@ -26,6 +28,7 @@ - } - - func uninstallTestHooks() { -+ socketFunc = origSocket - wsaSocketFunc = origWSASocket - poll.CloseFunc = origClosesocket - connectFunc = origConnect -Index: src/net/sock_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/sock_windows.go b/src/net/sock_windows.go ---- a/src/net/sock_windows.go (revision 2a406dc9f1ea7323d6ca9fccb2fe9ddebb6b1cc8) -+++ b/src/net/sock_windows.go (revision 7b1fd7d39c6be0185fbe1d929578ab372ac5c632) -@@ -20,6 +20,21 @@ - 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) - } -Index: src/syscall/exec_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/syscall/exec_windows.go b/src/syscall/exec_windows.go ---- a/src/syscall/exec_windows.go (revision 2a406dc9f1ea7323d6ca9fccb2fe9ddebb6b1cc8) -+++ b/src/syscall/exec_windows.go (revision 979d6d8bab3823ff572ace26767fd2ce3cf351ae) -@@ -14,7 +14,6 @@ - "unsafe" - ) - --// ForkLock is not used on Windows. - var ForkLock sync.RWMutex - - // EscapeArg rewrites command line argument s as prescribed -@@ -254,6 +253,9 @@ - var zeroProcAttr ProcAttr - var zeroSysProcAttr SysProcAttr - -+//go:linkname rtlGetNtVersionNumbers -+func rtlGetNtVersionNumbers(majorVersion *uint32, minorVersion *uint32, buildNumber *uint32) -+ - func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle uintptr, err error) { - if len(argv0) == 0 { - return 0, 0, EWINDOWS -@@ -317,6 +319,17 @@ - } - } - -+ 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 { -@@ -325,7 +338,15 @@ - fd := make([]Handle, len(attr.Files)) - for i := range attr.Files { - if attr.Files[i] > 0 { -- err := DuplicateHandle(p, Handle(attr.Files[i]), parentProcess, &fd[i], 0, true, DUPLICATE_SAME_ACCESS) -+ 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) - if err != nil { - return 0, 0, err - } -@@ -356,6 +377,14 @@ - - 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 -Index: src/runtime/syscall_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/runtime/syscall_windows.go b/src/runtime/syscall_windows.go ---- a/src/runtime/syscall_windows.go (revision 979d6d8bab3823ff572ace26767fd2ce3cf351ae) -+++ b/src/runtime/syscall_windows.go (revision ac3e93c061779dfefc0dd13a5b6e6f764a25621e) -@@ -413,10 +413,20 @@ - - const _LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800 - -+// When available, this function will use LoadLibraryEx with the filename -+// parameter and the important SEARCH_SYSTEM32 argument. But on systems that -+// do not have that option, absoluteFilepath should contain a fallback -+// to the full path inside of system32 for use with vanilla LoadLibrary. -+// - //go:linkname syscall_loadsystemlibrary syscall.loadsystemlibrary --func syscall_loadsystemlibrary(filename *uint16) (handle, err uintptr) { -- handle, _, err = syscall_SyscallN(uintptr(unsafe.Pointer(_LoadLibraryExW)), uintptr(unsafe.Pointer(filename)), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32) -+func syscall_loadsystemlibrary(filename *uint16, absoluteFilepath *uint16) (handle, err uintptr) { -+ if useLoadLibraryEx { -+ handle, _, err = syscall_SyscallN(uintptr(unsafe.Pointer(_LoadLibraryExW)), uintptr(unsafe.Pointer(filename)), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32) -+ } else { -+ handle, _, err = syscall_SyscallN(uintptr(unsafe.Pointer(_LoadLibraryW)), uintptr(unsafe.Pointer(absoluteFilepath))) -+ } - KeepAlive(filename) -+ KeepAlive(absoluteFilepath) - if handle != 0 { - err = 0 - } -Index: src/syscall/dll_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/syscall/dll_windows.go b/src/syscall/dll_windows.go ---- a/src/syscall/dll_windows.go (revision 979d6d8bab3823ff572ace26767fd2ce3cf351ae) -+++ b/src/syscall/dll_windows.go (revision ac3e93c061779dfefc0dd13a5b6e6f764a25621e) -@@ -45,7 +45,7 @@ - //go:noescape - func SyscallN(trap uintptr, args ...uintptr) (r1, r2 uintptr, err Errno) - func loadlibrary(filename *uint16) (handle uintptr, err Errno) --func loadsystemlibrary(filename *uint16) (handle uintptr, err Errno) -+func loadsystemlibrary(filename *uint16, absoluteFilepath *uint16) (handle uintptr, err Errno) - func getprocaddress(handle uintptr, procname *uint8) (proc uintptr, err Errno) - - // A DLL implements access to a single DLL. -@@ -54,6 +54,9 @@ - Handle Handle - } - -+//go:linkname getSystemDirectory -+func getSystemDirectory() string // Implemented in runtime package. -+ - // LoadDLL loads the named DLL file into memory. - // - // If name is not an absolute path and is not a known system DLL used by -@@ -70,7 +73,11 @@ - var h uintptr - var e Errno - if sysdll.IsSystemDLL[name] { -- h, e = loadsystemlibrary(namep) -+ absoluteFilepathp, err := UTF16PtrFromString(getSystemDirectory() + name) -+ if err != nil { -+ return nil, err -+ } -+ h, e = loadsystemlibrary(namep, absoluteFilepathp) - } else { - h, e = loadlibrary(namep) - } diff --git a/mihomo/.github/patch/go1.25.patch b/mihomo/.github/patch/go1.25.patch deleted file mode 100644 index 3e5ebed357..0000000000 --- a/mihomo/.github/patch/go1.25.patch +++ /dev/null @@ -1,884 +0,0 @@ -Subject: [PATCH] Revert "os: remove 5ms sleep on Windows in (*Process).Wait" -Fix os.RemoveAll not working on Windows7 -Revert "runtime: always use LoadLibraryEx to load system libraries" -Revert "syscall: remove Windows 7 console handle workaround" -Revert "net: remove sysSocket fallback for Windows 7" -Revert "crypto/rand,runtime: switch RtlGenRandom for ProcessPrng" ---- -Index: src/crypto/internal/sysrand/rand_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/crypto/internal/sysrand/rand_windows.go b/src/crypto/internal/sysrand/rand_windows.go ---- a/src/crypto/internal/sysrand/rand_windows.go (revision 439ff996f0ee506fc2eb84b7f11ffc360a6299f2) -+++ b/src/crypto/internal/sysrand/rand_windows.go (revision 466f6c7a29bc098b0d4c987b803c779222894a11) -@@ -7,5 +7,26 @@ - import "internal/syscall/windows" - - func read(b []byte) error { -- return windows.ProcessPrng(b) -+ // 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. -+ return batched(windows.RtlGenRandom, 1<<31-1)(b) -+} -+ -+// batched returns a function that calls f to populate a []byte by chunking it -+// into subslices of, at most, readMax bytes. -+func batched(f func([]byte) error, readMax int) func([]byte) error { -+ return func(out []byte) error { -+ for len(out) > 0 { -+ read := len(out) -+ if read > readMax { -+ read = readMax -+ } -+ if err := f(out[:read]); err != nil { -+ return err -+ } -+ out = out[read:] -+ } -+ return nil -+ } - } -Index: src/crypto/rand/rand.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/crypto/rand/rand.go b/src/crypto/rand/rand.go ---- a/src/crypto/rand/rand.go (revision 439ff996f0ee506fc2eb84b7f11ffc360a6299f2) -+++ b/src/crypto/rand/rand.go (revision 466f6c7a29bc098b0d4c987b803c779222894a11) -@@ -22,7 +22,7 @@ - // - On legacy Linux (< 3.17), Reader opens /dev/urandom on first use. - // - On macOS, iOS, and OpenBSD Reader, uses arc4random_buf(3). - // - On NetBSD, Reader uses the kern.arandom sysctl. --// - On Windows, Reader uses the ProcessPrng API. -+// - On Windows systems, Reader uses the RtlGenRandom API. - // - On js/wasm, Reader uses the Web Crypto API. - // - On wasip1/wasm, Reader uses random_get. - // -Index: src/internal/syscall/windows/syscall_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/internal/syscall/windows/syscall_windows.go b/src/internal/syscall/windows/syscall_windows.go ---- a/src/internal/syscall/windows/syscall_windows.go (revision 439ff996f0ee506fc2eb84b7f11ffc360a6299f2) -+++ b/src/internal/syscall/windows/syscall_windows.go (revision 466f6c7a29bc098b0d4c987b803c779222894a11) -@@ -419,7 +419,7 @@ - //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 ProcessPrng(buf []byte) (err error) = bcryptprimitives.ProcessPrng -+//sys RtlGenRandom(buf []byte) (err error) = advapi32.SystemFunction036 - - type FILE_ID_BOTH_DIR_INFO struct { - NextEntryOffset uint32 -Index: src/internal/syscall/windows/zsyscall_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/internal/syscall/windows/zsyscall_windows.go b/src/internal/syscall/windows/zsyscall_windows.go ---- a/src/internal/syscall/windows/zsyscall_windows.go (revision 439ff996f0ee506fc2eb84b7f11ffc360a6299f2) -+++ b/src/internal/syscall/windows/zsyscall_windows.go (revision 466f6c7a29bc098b0d4c987b803c779222894a11) -@@ -38,7 +38,6 @@ - - var ( - 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")) -@@ -65,7 +64,7 @@ - procSetEntriesInAclW = modadvapi32.NewProc("SetEntriesInAclW") - procSetNamedSecurityInfoW = modadvapi32.NewProc("SetNamedSecurityInfoW") - procSetTokenInformation = modadvapi32.NewProc("SetTokenInformation") -- procProcessPrng = modbcryptprimitives.NewProc("ProcessPrng") -+ procSystemFunction036 = modadvapi32.NewProc("SystemFunction036") - procGetAdaptersAddresses = modiphlpapi.NewProc("GetAdaptersAddresses") - procCreateEventW = modkernel32.NewProc("CreateEventW") - procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort") -@@ -270,12 +269,12 @@ - return - } - --func ProcessPrng(buf []byte) (err error) { -+func RtlGenRandom(buf []byte) (err error) { - var _p0 *byte - if len(buf) > 0 { - _p0 = &buf[0] - } -- r1, _, e1 := syscall.SyscallN(procProcessPrng.Addr(), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf))) -+ r1, _, e1 := syscall.SyscallN(procSystemFunction036.Addr(), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf))) - if r1 == 0 { - err = errnoErr(e1) - } -Index: src/runtime/os_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go ---- a/src/runtime/os_windows.go (revision 439ff996f0ee506fc2eb84b7f11ffc360a6299f2) -+++ b/src/runtime/os_windows.go (revision f6bddda4e8ff58a957462a1a09562924d5f3d05c) -@@ -39,8 +39,8 @@ - //go:cgo_import_dynamic runtime._GetSystemInfo GetSystemInfo%1 "kernel32.dll" - //go:cgo_import_dynamic runtime._GetThreadContext GetThreadContext%2 "kernel32.dll" - //go:cgo_import_dynamic runtime._SetThreadContext SetThreadContext%2 "kernel32.dll" --//go:cgo_import_dynamic runtime._LoadLibraryExW LoadLibraryExW%3 "kernel32.dll" - //go:cgo_import_dynamic runtime._LoadLibraryW LoadLibraryW%1 "kernel32.dll" -+//go:cgo_import_dynamic runtime._LoadLibraryA LoadLibraryA%1 "kernel32.dll" - //go:cgo_import_dynamic runtime._PostQueuedCompletionStatus PostQueuedCompletionStatus%4 "kernel32.dll" - //go:cgo_import_dynamic runtime._QueryPerformanceCounter QueryPerformanceCounter%1 "kernel32.dll" - //go:cgo_import_dynamic runtime._QueryPerformanceFrequency QueryPerformanceFrequency%1 "kernel32.dll" -@@ -74,7 +74,6 @@ - // Following syscalls are available on every Windows PC. - // All these variables are set by the Windows executable - // loader before the Go program starts. -- _AddVectoredContinueHandler, - _AddVectoredExceptionHandler, - _CloseHandle, - _CreateEventA, -@@ -97,8 +96,8 @@ - _GetSystemInfo, - _GetThreadContext, - _SetThreadContext, -- _LoadLibraryExW, - _LoadLibraryW, -+ _LoadLibraryA, - _PostQueuedCompletionStatus, - _QueryPerformanceCounter, - _QueryPerformanceFrequency, -@@ -127,8 +126,23 @@ - _WriteFile, - _ stdFunction - -- // Use ProcessPrng to generate cryptographically random data. -- _ProcessPrng stdFunction -+ // Following syscalls are only available on some Windows PCs. -+ // We will load syscalls, if available, before using them. -+ _AddDllDirectory, -+ _AddVectoredContinueHandler, -+ _LoadLibraryExA, -+ _LoadLibraryExW, -+ _ 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 - - // Load ntdll.dll manually during startup, otherwise Mingw - // links wrong printf function to cgo executable (see issue -@@ -145,13 +159,6 @@ - _ stdFunction - ) - --var ( -- 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} --) -- - // Function to be called by windows CreateThread - // to start new os thread. - func tstart_stdcall(newm *m) -@@ -244,8 +251,18 @@ - return unsafe.String(&sysDirectory[0], sysDirectoryLen) - } - --func windowsLoadSystemLib(name []uint16) uintptr { -- return stdcall3(_LoadLibraryExW, uintptr(unsafe.Pointer(&name[0])), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32) -+//go:linkname syscall_getSystemDirectory syscall.getSystemDirectory -+func syscall_getSystemDirectory() string { -+ return unsafe.String(&sysDirectory[0], sysDirectoryLen) -+} -+ -+func windowsLoadSystemLib(name []byte) uintptr { -+ if useLoadLibraryEx { -+ return stdcall3(_LoadLibraryExA, uintptr(unsafe.Pointer(&name[0])), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32) -+ } else { -+ absName := append(sysDirectory[:sysDirectoryLen], name...) -+ return stdcall1(_LoadLibraryA, uintptr(unsafe.Pointer(&absName[0]))) -+ } - } - - //go:linkname windows_QueryPerformanceCounter internal/syscall/windows.QueryPerformanceCounter -@@ -263,13 +280,28 @@ - } - - func loadOptionalSyscalls() { -- bcryptPrimitives := windowsLoadSystemLib(bcryptprimitivesdll[:]) -- if bcryptPrimitives == 0 { -- throw("bcryptprimitives.dll not found") -+ var kernel32dll = []byte("kernel32.dll\000") -+ k32 := stdcall1(_LoadLibraryA, uintptr(unsafe.Pointer(&kernel32dll[0]))) -+ if k32 == 0 { -+ throw("kernel32.dll not found") - } -- _ProcessPrng = windowsFindfunc(bcryptPrimitives, []byte("ProcessPrng\000")) -+ _AddDllDirectory = windowsFindfunc(k32, []byte("AddDllDirectory\000")) -+ _AddVectoredContinueHandler = windowsFindfunc(k32, []byte("AddVectoredContinueHandler\000")) -+ _LoadLibraryExA = windowsFindfunc(k32, []byte("LoadLibraryExA\000")) -+ _LoadLibraryExW = windowsFindfunc(k32, []byte("LoadLibraryExW\000")) -+ useLoadLibraryEx = (_LoadLibraryExW != nil && _LoadLibraryExA != nil && _AddDllDirectory != nil) -+ -+ initSysDirectory() - -- n32 := windowsLoadSystemLib(ntdlldll[:]) -+ var advapi32dll = []byte("advapi32.dll\000") -+ a32 := windowsLoadSystemLib(advapi32dll) -+ if a32 == 0 { -+ throw("advapi32.dll not found") -+ } -+ _RtlGenRandom = windowsFindfunc(a32, []byte("SystemFunction036\000")) -+ -+ var ntdll = []byte("ntdll.dll\000") -+ n32 := windowsLoadSystemLib(ntdll) - if n32 == 0 { - throw("ntdll.dll not found") - } -@@ -298,7 +330,7 @@ - context uintptr - } - -- powrprof := windowsLoadSystemLib(powrprofdll[:]) -+ powrprof := windowsLoadSystemLib([]byte("powrprof.dll\000")) - if powrprof == 0 { - return // Running on Windows 7, where we don't need it anyway. - } -@@ -357,6 +389,22 @@ - // in sys_windows_386.s and sys_windows_amd64.s: - func getlasterror() uint32 - -+// When loading DLLs, we prefer to use LoadLibraryEx with -+// LOAD_LIBRARY_SEARCH_* flags, if available. LoadLibraryEx is not -+// available on old Windows, though, and the LOAD_LIBRARY_SEARCH_* -+// flags are not available on some versions of Windows without a -+// security patch. -+// -+// https://msdn.microsoft.com/en-us/library/ms684179(v=vs.85).aspx says: -+// "Windows 7, Windows Server 2008 R2, Windows Vista, and Windows -+// Server 2008: The LOAD_LIBRARY_SEARCH_* flags are available on -+// systems that have KB2533623 installed. To determine whether the -+// flags are available, use GetProcAddress to get the address of the -+// AddDllDirectory, RemoveDllDirectory, or SetDefaultDllDirectories -+// function. If GetProcAddress succeeds, the LOAD_LIBRARY_SEARCH_* -+// flags can be used with LoadLibraryEx." -+var useLoadLibraryEx bool -+ - var timeBeginPeriodRetValue uint32 - - // osRelaxMinNS indicates that sysmon shouldn't osRelax if the next -@@ -430,7 +478,8 @@ - // Only load winmm.dll if we need it. - // This avoids a dependency on winmm.dll for Go programs - // that run on new Windows versions. -- m32 := windowsLoadSystemLib(winmmdll[:]) -+ var winmmdll = []byte("winmm.dll\000") -+ m32 := windowsLoadSystemLib(winmmdll) - if m32 == 0 { - print("runtime: LoadLibraryExW failed; errno=", getlasterror(), "\n") - throw("winmm.dll not found") -@@ -471,6 +520,28 @@ - canUseLongPaths = true - } - -+var osVersionInfo struct { -+ majorVersion uint32 -+ minorVersion uint32 -+ buildNumber uint32 -+} -+ -+func initOsVersionInfo() { -+ info := _OSVERSIONINFOW{} -+ info.osVersionInfoSize = uint32(unsafe.Sizeof(info)) -+ stdcall1(_RtlGetVersion, uintptr(unsafe.Pointer(&info))) -+ osVersionInfo.majorVersion = info.majorVersion -+ osVersionInfo.minorVersion = info.minorVersion -+ osVersionInfo.buildNumber = info.buildNumber -+} -+ -+//go:linkname rtlGetNtVersionNumbers syscall.rtlGetNtVersionNumbers -+func rtlGetNtVersionNumbers(majorVersion *uint32, minorVersion *uint32, buildNumber *uint32) { -+ *majorVersion = osVersionInfo.majorVersion -+ *minorVersion = osVersionInfo.minorVersion -+ *buildNumber = osVersionInfo.buildNumber -+} -+ - func osinit() { - asmstdcallAddr = unsafe.Pointer(abi.FuncPCABI0(asmstdcall)) - -@@ -483,8 +554,8 @@ - initHighResTimer() - timeBeginPeriodRetValue = osRelax(false) - -- initSysDirectory() - initLongPathSupport() -+ initOsVersionInfo() - - numCPUStartup = getCPUCount() - -@@ -500,7 +571,7 @@ - //go:nosplit - func readRandom(r []byte) int { - n := 0 -- if stdcall2(_ProcessPrng, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 { -+ if stdcall2(_RtlGenRandom, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 { - n = len(r) - } - return n -Index: src/net/hook_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/hook_windows.go b/src/net/hook_windows.go ---- a/src/net/hook_windows.go (revision 466f6c7a29bc098b0d4c987b803c779222894a11) -+++ b/src/net/hook_windows.go (revision 1bdabae205052afe1dadb2ad6f1ba612cdbc532a) -@@ -13,6 +13,7 @@ - hostsFilePath = windows.GetSystemDirectory() + "/Drivers/etc/hosts" - - // 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 -Index: src/net/internal/socktest/main_test.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/internal/socktest/main_test.go b/src/net/internal/socktest/main_test.go ---- a/src/net/internal/socktest/main_test.go (revision 466f6c7a29bc098b0d4c987b803c779222894a11) -+++ b/src/net/internal/socktest/main_test.go (revision 1bdabae205052afe1dadb2ad6f1ba612cdbc532a) -@@ -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 && !windows -+//go:build !js && !plan9 && !wasip1 - - package socktest_test - -Index: src/net/internal/socktest/main_windows_test.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/internal/socktest/main_windows_test.go b/src/net/internal/socktest/main_windows_test.go -new file mode 100644 ---- /dev/null (revision 1bdabae205052afe1dadb2ad6f1ba612cdbc532a) -+++ b/src/net/internal/socktest/main_windows_test.go (revision 1bdabae205052afe1dadb2ad6f1ba612cdbc532a) -@@ -0,0 +1,22 @@ -+// 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 -+} -Index: src/net/internal/socktest/sys_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/internal/socktest/sys_windows.go b/src/net/internal/socktest/sys_windows.go ---- a/src/net/internal/socktest/sys_windows.go (revision 466f6c7a29bc098b0d4c987b803c779222894a11) -+++ b/src/net/internal/socktest/sys_windows.go (revision 1bdabae205052afe1dadb2ad6f1ba612cdbc532a) -@@ -9,6 +9,38 @@ - "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) -Index: src/net/main_windows_test.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/main_windows_test.go b/src/net/main_windows_test.go ---- a/src/net/main_windows_test.go (revision 466f6c7a29bc098b0d4c987b803c779222894a11) -+++ b/src/net/main_windows_test.go (revision 1bdabae205052afe1dadb2ad6f1ba612cdbc532a) -@@ -12,6 +12,7 @@ - - var ( - // Placeholders for saving original socket system calls. -+ origSocket = socketFunc - origWSASocket = wsaSocketFunc - origClosesocket = poll.CloseFunc - origConnect = connectFunc -@@ -21,6 +22,7 @@ - ) - - func installTestHooks() { -+ socketFunc = sw.Socket - wsaSocketFunc = sw.WSASocket - poll.CloseFunc = sw.Closesocket - connectFunc = sw.Connect -@@ -30,6 +32,7 @@ - } - - func uninstallTestHooks() { -+ socketFunc = origSocket - wsaSocketFunc = origWSASocket - poll.CloseFunc = origClosesocket - connectFunc = origConnect -Index: src/net/sock_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/sock_windows.go b/src/net/sock_windows.go ---- a/src/net/sock_windows.go (revision 466f6c7a29bc098b0d4c987b803c779222894a11) -+++ b/src/net/sock_windows.go (revision 1bdabae205052afe1dadb2ad6f1ba612cdbc532a) -@@ -20,6 +20,21 @@ - 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) - } -Index: src/syscall/exec_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/syscall/exec_windows.go b/src/syscall/exec_windows.go ---- a/src/syscall/exec_windows.go (revision 466f6c7a29bc098b0d4c987b803c779222894a11) -+++ b/src/syscall/exec_windows.go (revision a90777dcf692dd2168577853ba743b4338721b06) -@@ -14,7 +14,6 @@ - "unsafe" - ) - --// ForkLock is not used on Windows. - var ForkLock sync.RWMutex - - // EscapeArg rewrites command line argument s as prescribed -@@ -254,6 +253,9 @@ - var zeroProcAttr ProcAttr - var zeroSysProcAttr SysProcAttr - -+//go:linkname rtlGetNtVersionNumbers -+func rtlGetNtVersionNumbers(majorVersion *uint32, minorVersion *uint32, buildNumber *uint32) -+ - func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle uintptr, err error) { - if len(argv0) == 0 { - return 0, 0, EWINDOWS -@@ -317,6 +319,17 @@ - } - } - -+ 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 { -@@ -325,7 +338,15 @@ - fd := make([]Handle, len(attr.Files)) - for i := range attr.Files { - if attr.Files[i] > 0 { -- err := DuplicateHandle(p, Handle(attr.Files[i]), parentProcess, &fd[i], 0, true, DUPLICATE_SAME_ACCESS) -+ 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) - if err != nil { - return 0, 0, err - } -@@ -356,6 +377,14 @@ - - 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 -Index: src/runtime/syscall_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/runtime/syscall_windows.go b/src/runtime/syscall_windows.go ---- a/src/runtime/syscall_windows.go (revision a90777dcf692dd2168577853ba743b4338721b06) -+++ b/src/runtime/syscall_windows.go (revision f6bddda4e8ff58a957462a1a09562924d5f3d05c) -@@ -413,10 +413,20 @@ - - const _LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800 - -+// When available, this function will use LoadLibraryEx with the filename -+// parameter and the important SEARCH_SYSTEM32 argument. But on systems that -+// do not have that option, absoluteFilepath should contain a fallback -+// to the full path inside of system32 for use with vanilla LoadLibrary. -+// - //go:linkname syscall_loadsystemlibrary syscall.loadsystemlibrary --func syscall_loadsystemlibrary(filename *uint16) (handle, err uintptr) { -- handle, _, err = syscall_SyscallN(uintptr(unsafe.Pointer(_LoadLibraryExW)), uintptr(unsafe.Pointer(filename)), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32) -+func syscall_loadsystemlibrary(filename *uint16, absoluteFilepath *uint16) (handle, err uintptr) { -+ if useLoadLibraryEx { -+ handle, _, err = syscall_SyscallN(uintptr(unsafe.Pointer(_LoadLibraryExW)), uintptr(unsafe.Pointer(filename)), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32) -+ } else { -+ handle, _, err = syscall_SyscallN(uintptr(unsafe.Pointer(_LoadLibraryW)), uintptr(unsafe.Pointer(absoluteFilepath))) -+ } - KeepAlive(filename) -+ KeepAlive(absoluteFilepath) - if handle != 0 { - err = 0 - } -Index: src/syscall/dll_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/syscall/dll_windows.go b/src/syscall/dll_windows.go ---- a/src/syscall/dll_windows.go (revision a90777dcf692dd2168577853ba743b4338721b06) -+++ b/src/syscall/dll_windows.go (revision f6bddda4e8ff58a957462a1a09562924d5f3d05c) -@@ -45,7 +45,7 @@ - //go:noescape - func SyscallN(trap uintptr, args ...uintptr) (r1, r2 uintptr, err Errno) - func loadlibrary(filename *uint16) (handle uintptr, err Errno) --func loadsystemlibrary(filename *uint16) (handle uintptr, err Errno) -+func loadsystemlibrary(filename *uint16, absoluteFilepath *uint16) (handle uintptr, err Errno) - func getprocaddress(handle uintptr, procname *uint8) (proc uintptr, err Errno) - - // A DLL implements access to a single DLL. -@@ -54,6 +54,9 @@ - Handle Handle - } - -+//go:linkname getSystemDirectory -+func getSystemDirectory() string // Implemented in runtime package. -+ - // LoadDLL loads the named DLL file into memory. - // - // If name is not an absolute path and is not a known system DLL used by -@@ -70,7 +73,11 @@ - var h uintptr - var e Errno - if sysdll.IsSystemDLL[name] { -- h, e = loadsystemlibrary(namep) -+ absoluteFilepathp, err := UTF16PtrFromString(getSystemDirectory() + name) -+ if err != nil { -+ return nil, err -+ } -+ h, e = loadsystemlibrary(namep, absoluteFilepathp) - } else { - h, e = loadlibrary(namep) - } -Index: src/os/removeall_at.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/os/removeall_at.go b/src/os/removeall_at.go ---- a/src/os/removeall_at.go (revision f6bddda4e8ff58a957462a1a09562924d5f3d05c) -+++ b/src/os/removeall_at.go (revision bed309eff415bcb3c77dd4bc3277b682b89a388d) -@@ -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 unix || wasip1 || windows -+//go:build unix || wasip1 - - package os - -@@ -175,3 +175,25 @@ - } - return newDirFile(fd, name) - } -+ -+func rootRemoveAll(r *Root, name string) error { -+ // Consistency with os.RemoveAll: Strip trailing /s from the name, -+ // so RemoveAll("not_a_directory/") succeeds. -+ for len(name) > 0 && IsPathSeparator(name[len(name)-1]) { -+ name = name[:len(name)-1] -+ } -+ if endsWithDot(name) { -+ // Consistency with os.RemoveAll: Return EINVAL when trying to remove . -+ return &PathError{Op: "RemoveAll", Path: name, Err: syscall.EINVAL} -+ } -+ _, err := doInRoot(r, name, nil, func(parent sysfdType, name string) (struct{}, error) { -+ return struct{}{}, removeAllFrom(parent, name) -+ }) -+ if IsNotExist(err) { -+ return nil -+ } -+ if err != nil { -+ return &PathError{Op: "RemoveAll", Path: name, Err: underlyingError(err)} -+ } -+ return err -+} -Index: src/os/removeall_noat.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/os/removeall_noat.go b/src/os/removeall_noat.go ---- a/src/os/removeall_noat.go (revision f6bddda4e8ff58a957462a1a09562924d5f3d05c) -+++ b/src/os/removeall_noat.go (revision bed309eff415bcb3c77dd4bc3277b682b89a388d) -@@ -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 && wasm) || plan9 -+//go:build (js && wasm) || plan9 || windows - - package os - -@@ -140,3 +140,22 @@ - } - return err - } -+ -+func rootRemoveAll(r *Root, name string) error { -+ if endsWithDot(name) { -+ // Consistency with os.RemoveAll: Return EINVAL when trying to remove . -+ return &PathError{Op: "RemoveAll", Path: name, Err: syscall.EINVAL} -+ } -+ if err := checkPathEscapesLstat(r, name); err != nil { -+ if err == syscall.ENOTDIR { -+ // Some intermediate path component is not a directory. -+ // RemoveAll treats this as success (since the target doesn't exist). -+ return nil -+ } -+ return &PathError{Op: "RemoveAll", Path: name, Err: err} -+ } -+ if err := RemoveAll(joinPath(r.root.name, name)); err != nil { -+ return &PathError{Op: "RemoveAll", Path: name, Err: underlyingError(err)} -+ } -+ return nil -+} -Index: src/os/root_noopenat.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/os/root_noopenat.go b/src/os/root_noopenat.go ---- a/src/os/root_noopenat.go (revision f6bddda4e8ff58a957462a1a09562924d5f3d05c) -+++ b/src/os/root_noopenat.go (revision bed309eff415bcb3c77dd4bc3277b682b89a388d) -@@ -11,7 +11,6 @@ - "internal/filepathlite" - "internal/stringslite" - "sync/atomic" -- "syscall" - "time" - ) - -@@ -185,25 +184,6 @@ - } - return nil - } -- --func rootRemoveAll(r *Root, name string) error { -- if endsWithDot(name) { -- // Consistency with os.RemoveAll: Return EINVAL when trying to remove . -- return &PathError{Op: "RemoveAll", Path: name, Err: syscall.EINVAL} -- } -- if err := checkPathEscapesLstat(r, name); err != nil { -- if err == syscall.ENOTDIR { -- // Some intermediate path component is not a directory. -- // RemoveAll treats this as success (since the target doesn't exist). -- return nil -- } -- return &PathError{Op: "RemoveAll", Path: name, Err: err} -- } -- if err := RemoveAll(joinPath(r.root.name, name)); err != nil { -- return &PathError{Op: "RemoveAll", Path: name, Err: underlyingError(err)} -- } -- return nil --} - - func rootReadlink(r *Root, name string) (string, error) { - if err := checkPathEscapesLstat(r, name); err != nil { -Index: src/os/root_openat.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/os/root_openat.go b/src/os/root_openat.go ---- a/src/os/root_openat.go (revision f6bddda4e8ff58a957462a1a09562924d5f3d05c) -+++ b/src/os/root_openat.go (revision bed309eff415bcb3c77dd4bc3277b682b89a388d) -@@ -196,28 +196,6 @@ - return nil - } - --func rootRemoveAll(r *Root, name string) error { -- // Consistency with os.RemoveAll: Strip trailing /s from the name, -- // so RemoveAll("not_a_directory/") succeeds. -- for len(name) > 0 && IsPathSeparator(name[len(name)-1]) { -- name = name[:len(name)-1] -- } -- if endsWithDot(name) { -- // Consistency with os.RemoveAll: Return EINVAL when trying to remove . -- return &PathError{Op: "RemoveAll", Path: name, Err: syscall.EINVAL} -- } -- _, err := doInRoot(r, name, nil, func(parent sysfdType, name string) (struct{}, error) { -- return struct{}{}, removeAllFrom(parent, name) -- }) -- if IsNotExist(err) { -- return nil -- } -- if err != nil { -- return &PathError{Op: "RemoveAll", Path: name, Err: underlyingError(err)} -- } -- return err --} -- - func rootRename(r *Root, oldname, newname string) error { - _, err := doInRoot(r, oldname, nil, func(oldparent sysfdType, oldname string) (struct{}, error) { - _, err := doInRoot(r, newname, nil, func(newparent sysfdType, newname string) (struct{}, error) { -Index: src/os/root_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/os/root_windows.go b/src/os/root_windows.go ---- a/src/os/root_windows.go (revision f6bddda4e8ff58a957462a1a09562924d5f3d05c) -+++ b/src/os/root_windows.go (revision bed309eff415bcb3c77dd4bc3277b682b89a388d) -@@ -402,3 +402,14 @@ - } - return fi.Mode(), nil - } -+ -+func checkPathEscapes(r *Root, name string) error { -+ if !filepathlite.IsLocal(name) { -+ return errPathEscapes -+ } -+ return nil -+} -+ -+func checkPathEscapesLstat(r *Root, name string) error { -+ return checkPathEscapes(r, name) -+} -Index: src/os/exec_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/os/exec_windows.go b/src/os/exec_windows.go ---- a/src/os/exec_windows.go (revision bed309eff415bcb3c77dd4bc3277b682b89a388d) -+++ b/src/os/exec_windows.go (revision 34b899c2fb39b092db4fa67c4417e41dc046be4b) -@@ -10,6 +10,7 @@ - "runtime" - "syscall" - "time" -+ _ "unsafe" - ) - - // Note that Process.handle is never nil because Windows always requires -@@ -49,9 +50,23 @@ - // than statusDone. - p.doRelease(statusReleased) - -+ var maj, min, build uint32 -+ rtlGetNtVersionNumbers(&maj, &min, &build) -+ if maj < 10 { -+ // NOTE(brainman): It seems that sometimes process is not dead -+ // when WaitForSingleObject returns. But we do not know any -+ // other way to wait for it. Sleeping for a while seems to do -+ // the trick sometimes. -+ // See https://golang.org/issue/25965 for details. -+ time.Sleep(5 * time.Millisecond) -+ } -+ - return &ProcessState{p.Pid, syscall.WaitStatus{ExitCode: ec}, &u}, nil - } - -+//go:linkname rtlGetNtVersionNumbers syscall.rtlGetNtVersionNumbers -+func rtlGetNtVersionNumbers(majorVersion *uint32, minorVersion *uint32, buildNumber *uint32) -+ - func (p *Process) signal(sig Signal) error { - handle, status := p.handleTransientAcquire() - switch status { diff --git a/mihomo/.github/patch/go1.26.patch b/mihomo/.github/patch/go1.26.patch deleted file mode 100644 index c2400e797f..0000000000 --- a/mihomo/.github/patch/go1.26.patch +++ /dev/null @@ -1,883 +0,0 @@ -Subject: [PATCH] Revert "os: remove 5ms sleep on Windows in (*Process).Wait" -Fix os.RemoveAll not working on Windows7 -Revert "runtime: always use LoadLibraryEx to load system libraries" -Revert "syscall: remove Windows 7 console handle workaround" -Revert "net: remove sysSocket fallback for Windows 7" -Revert "crypto/rand,runtime: switch RtlGenRandom for ProcessPrng" ---- -Index: src/crypto/internal/sysrand/rand_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/crypto/internal/sysrand/rand_windows.go b/src/crypto/internal/sysrand/rand_windows.go ---- a/src/crypto/internal/sysrand/rand_windows.go (revision e87b10ea2a2c6c65b80c4374af42b9c02ac9fb20) -+++ b/src/crypto/internal/sysrand/rand_windows.go (revision 4b29590aa510e05686ea53de16e1e571d22203d8) -@@ -7,5 +7,26 @@ - import "internal/syscall/windows" - - func read(b []byte) error { -- return windows.ProcessPrng(b) -+ // 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. -+ return batched(windows.RtlGenRandom, 1<<31-1)(b) -+} -+ -+// batched returns a function that calls f to populate a []byte by chunking it -+// into subslices of, at most, readMax bytes. -+func batched(f func([]byte) error, readMax int) func([]byte) error { -+ return func(out []byte) error { -+ for len(out) > 0 { -+ read := len(out) -+ if read > readMax { -+ read = readMax -+ } -+ if err := f(out[:read]); err != nil { -+ return err -+ } -+ out = out[read:] -+ } -+ return nil -+ } - } -Index: src/crypto/rand/rand.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/crypto/rand/rand.go b/src/crypto/rand/rand.go ---- a/src/crypto/rand/rand.go (revision e87b10ea2a2c6c65b80c4374af42b9c02ac9fb20) -+++ b/src/crypto/rand/rand.go (revision 4b29590aa510e05686ea53de16e1e571d22203d8) -@@ -25,7 +25,7 @@ - // - On legacy Linux (< 3.17), Reader opens /dev/urandom on first use. - // - On macOS, iOS, and OpenBSD Reader, uses arc4random_buf(3). - // - On NetBSD, Reader uses the kern.arandom sysctl. --// - On Windows, Reader uses the ProcessPrng API. -+// - On Windows systems, Reader uses the RtlGenRandom API. - // - On js/wasm, Reader uses the Web Crypto API. - // - On wasip1/wasm, Reader uses random_get. - // -Index: src/internal/syscall/windows/syscall_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/internal/syscall/windows/syscall_windows.go b/src/internal/syscall/windows/syscall_windows.go ---- a/src/internal/syscall/windows/syscall_windows.go (revision e87b10ea2a2c6c65b80c4374af42b9c02ac9fb20) -+++ b/src/internal/syscall/windows/syscall_windows.go (revision 4b29590aa510e05686ea53de16e1e571d22203d8) -@@ -421,7 +421,7 @@ - //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 ProcessPrng(buf []byte) (err error) = bcryptprimitives.ProcessPrng -+//sys RtlGenRandom(buf []byte) (err error) = advapi32.SystemFunction036 - - type FILE_ID_BOTH_DIR_INFO struct { - NextEntryOffset uint32 -Index: src/internal/syscall/windows/zsyscall_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/internal/syscall/windows/zsyscall_windows.go b/src/internal/syscall/windows/zsyscall_windows.go ---- a/src/internal/syscall/windows/zsyscall_windows.go (revision e87b10ea2a2c6c65b80c4374af42b9c02ac9fb20) -+++ b/src/internal/syscall/windows/zsyscall_windows.go (revision 4b29590aa510e05686ea53de16e1e571d22203d8) -@@ -38,7 +38,6 @@ - - var ( - 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")) -@@ -65,7 +64,7 @@ - procSetEntriesInAclW = modadvapi32.NewProc("SetEntriesInAclW") - procSetNamedSecurityInfoW = modadvapi32.NewProc("SetNamedSecurityInfoW") - procSetTokenInformation = modadvapi32.NewProc("SetTokenInformation") -- procProcessPrng = modbcryptprimitives.NewProc("ProcessPrng") -+ procSystemFunction036 = modadvapi32.NewProc("SystemFunction036") - procGetAdaptersAddresses = modiphlpapi.NewProc("GetAdaptersAddresses") - procCreateEventW = modkernel32.NewProc("CreateEventW") - procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort") -@@ -271,12 +270,12 @@ - return - } - --func ProcessPrng(buf []byte) (err error) { -+func RtlGenRandom(buf []byte) (err error) { - var _p0 *byte - if len(buf) > 0 { - _p0 = &buf[0] - } -- r1, _, e1 := syscall.SyscallN(procProcessPrng.Addr(), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf))) -+ r1, _, e1 := syscall.SyscallN(procSystemFunction036.Addr(), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf))) - if r1 == 0 { - err = errnoErr(e1) - } -Index: src/runtime/os_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go ---- a/src/runtime/os_windows.go (revision e87b10ea2a2c6c65b80c4374af42b9c02ac9fb20) -+++ b/src/runtime/os_windows.go (revision ce2e1a3d2c3c0d7277b4102841db1697147d2923) -@@ -40,7 +40,8 @@ - //go:cgo_import_dynamic runtime._GetSystemInfo GetSystemInfo%1 "kernel32.dll" - //go:cgo_import_dynamic runtime._GetThreadContext GetThreadContext%2 "kernel32.dll" - //go:cgo_import_dynamic runtime._SetThreadContext SetThreadContext%2 "kernel32.dll" --//go:cgo_import_dynamic runtime._LoadLibraryExW LoadLibraryExW%3 "kernel32.dll" -+//go:cgo_import_dynamic runtime._LoadLibraryW LoadLibraryW%1 "kernel32.dll" -+//go:cgo_import_dynamic runtime._LoadLibraryA LoadLibraryA%1 "kernel32.dll" - //go:cgo_import_dynamic runtime._PostQueuedCompletionStatus PostQueuedCompletionStatus%4 "kernel32.dll" - //go:cgo_import_dynamic runtime._QueryPerformanceCounter QueryPerformanceCounter%1 "kernel32.dll" - //go:cgo_import_dynamic runtime._QueryPerformanceFrequency QueryPerformanceFrequency%1 "kernel32.dll" -@@ -74,7 +75,6 @@ - // Following syscalls are available on every Windows PC. - // All these variables are set by the Windows executable - // loader before the Go program starts. -- _AddVectoredContinueHandler, - _AddVectoredExceptionHandler, - _CloseHandle, - _CreateEventA, -@@ -97,7 +97,8 @@ - _GetSystemInfo, - _GetThreadContext, - _SetThreadContext, -- _LoadLibraryExW, -+ _LoadLibraryW, -+ _LoadLibraryA, - _PostQueuedCompletionStatus, - _QueryPerformanceCounter, - _QueryPerformanceFrequency, -@@ -126,8 +127,23 @@ - _WriteFile, - _ stdFunction - -- // Use ProcessPrng to generate cryptographically random data. -- _ProcessPrng stdFunction -+ // Following syscalls are only available on some Windows PCs. -+ // We will load syscalls, if available, before using them. -+ _AddDllDirectory, -+ _AddVectoredContinueHandler, -+ _LoadLibraryExA, -+ _LoadLibraryExW, -+ _ 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 - - // Load ntdll.dll manually during startup, otherwise Mingw - // links wrong printf function to cgo executable (see issue -@@ -144,13 +160,6 @@ - _ stdFunction - ) - --var ( -- 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} --) -- - // Function to be called by windows CreateThread - // to start new os thread. - func tstart_stdcall(newm *m) -@@ -242,9 +251,40 @@ - return unsafe.String(&sysDirectory[0], sysDirectoryLen) - } - --func windowsLoadSystemLib(name []uint16) uintptr { -- const _LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800 -- return stdcall(_LoadLibraryExW, uintptr(unsafe.Pointer(&name[0])), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32) -+//go:linkname syscall_getSystemDirectory syscall.getSystemDirectory -+func syscall_getSystemDirectory() string { -+ return unsafe.String(&sysDirectory[0], sysDirectoryLen) -+} -+ -+func windowsLoadSystemLib(name []byte) uintptr { -+ if useLoadLibraryEx { -+ return stdcall(_LoadLibraryExA, uintptr(unsafe.Pointer(&name[0])), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32) -+ } else { -+ absName := append(sysDirectory[:sysDirectoryLen], name...) -+ return stdcall(_LoadLibraryA, uintptr(unsafe.Pointer(&absName[0]))) -+ } -+} -+ -+const _LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800 -+ -+// When available, this function will use LoadLibraryEx with the filename -+// parameter and the important SEARCH_SYSTEM32 argument. But on systems that -+// do not have that option, absoluteFilepath should contain a fallback -+// to the full path inside of system32 for use with vanilla LoadLibrary. -+// -+//go:linkname syscall_loadsystemlibrary syscall.loadsystemlibrary -+func syscall_loadsystemlibrary(filename *uint16, absoluteFilepath *uint16) (handle, err uintptr) { -+ if useLoadLibraryEx { -+ handle, _, err = syscall_syscalln(uintptr(unsafe.Pointer(_LoadLibraryExW)), 3, uintptr(unsafe.Pointer(filename)), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32) -+ } else { -+ handle, _, err = syscall_syscalln(uintptr(unsafe.Pointer(_LoadLibraryW)), 1, uintptr(unsafe.Pointer(absoluteFilepath))) -+ } -+ KeepAlive(filename) -+ KeepAlive(absoluteFilepath) -+ if handle != 0 { -+ err = 0 -+ } -+ return - } - - //go:linkname windows_QueryPerformanceCounter internal/syscall/windows.QueryPerformanceCounter -@@ -262,13 +302,28 @@ - } - - func loadOptionalSyscalls() { -- bcryptPrimitives := windowsLoadSystemLib(bcryptprimitivesdll[:]) -- if bcryptPrimitives == 0 { -- throw("bcryptprimitives.dll not found") -+ var kernel32dll = []byte("kernel32.dll\000") -+ k32 := stdcall(_LoadLibraryA, uintptr(unsafe.Pointer(&kernel32dll[0]))) -+ if k32 == 0 { -+ throw("kernel32.dll not found") - } -- _ProcessPrng = windowsFindfunc(bcryptPrimitives, []byte("ProcessPrng\000")) -+ _AddDllDirectory = windowsFindfunc(k32, []byte("AddDllDirectory\000")) -+ _AddVectoredContinueHandler = windowsFindfunc(k32, []byte("AddVectoredContinueHandler\000")) -+ _LoadLibraryExA = windowsFindfunc(k32, []byte("LoadLibraryExA\000")) -+ _LoadLibraryExW = windowsFindfunc(k32, []byte("LoadLibraryExW\000")) -+ useLoadLibraryEx = (_LoadLibraryExW != nil && _LoadLibraryExA != nil && _AddDllDirectory != nil) -+ -+ initSysDirectory() - -- n32 := windowsLoadSystemLib(ntdlldll[:]) -+ var advapi32dll = []byte("advapi32.dll\000") -+ a32 := windowsLoadSystemLib(advapi32dll) -+ if a32 == 0 { -+ throw("advapi32.dll not found") -+ } -+ _RtlGenRandom = windowsFindfunc(a32, []byte("SystemFunction036\000")) -+ -+ var ntdll = []byte("ntdll.dll\000") -+ n32 := windowsLoadSystemLib(ntdll) - if n32 == 0 { - throw("ntdll.dll not found") - } -@@ -297,7 +352,7 @@ - context uintptr - } - -- powrprof := windowsLoadSystemLib(powrprofdll[:]) -+ powrprof := windowsLoadSystemLib([]byte("powrprof.dll\000")) - if powrprof == 0 { - return // Running on Windows 7, where we don't need it anyway. - } -@@ -351,6 +406,22 @@ - // in sys_windows_386.s and sys_windows_amd64.s: - func getlasterror() uint32 - -+// When loading DLLs, we prefer to use LoadLibraryEx with -+// LOAD_LIBRARY_SEARCH_* flags, if available. LoadLibraryEx is not -+// available on old Windows, though, and the LOAD_LIBRARY_SEARCH_* -+// flags are not available on some versions of Windows without a -+// security patch. -+// -+// https://msdn.microsoft.com/en-us/library/ms684179(v=vs.85).aspx says: -+// "Windows 7, Windows Server 2008 R2, Windows Vista, and Windows -+// Server 2008: The LOAD_LIBRARY_SEARCH_* flags are available on -+// systems that have KB2533623 installed. To determine whether the -+// flags are available, use GetProcAddress to get the address of the -+// AddDllDirectory, RemoveDllDirectory, or SetDefaultDllDirectories -+// function. If GetProcAddress succeeds, the LOAD_LIBRARY_SEARCH_* -+// flags can be used with LoadLibraryEx." -+var useLoadLibraryEx bool -+ - var timeBeginPeriodRetValue uint32 - - // osRelaxMinNS indicates that sysmon shouldn't osRelax if the next -@@ -417,7 +488,8 @@ - // Only load winmm.dll if we need it. - // This avoids a dependency on winmm.dll for Go programs - // that run on new Windows versions. -- m32 := windowsLoadSystemLib(winmmdll[:]) -+ var winmmdll = []byte("winmm.dll\000") -+ m32 := windowsLoadSystemLib(winmmdll) - if m32 == 0 { - print("runtime: LoadLibraryExW failed; errno=", getlasterror(), "\n") - throw("winmm.dll not found") -@@ -458,6 +530,28 @@ - canUseLongPaths = true - } - -+var osVersionInfo struct { -+ majorVersion uint32 -+ minorVersion uint32 -+ buildNumber uint32 -+} -+ -+func initOsVersionInfo() { -+ info := windows.OSVERSIONINFOW{} -+ info.OSVersionInfoSize = uint32(unsafe.Sizeof(info)) -+ stdcall(_RtlGetVersion, uintptr(unsafe.Pointer(&info))) -+ osVersionInfo.majorVersion = info.MajorVersion -+ osVersionInfo.minorVersion = info.MinorVersion -+ osVersionInfo.buildNumber = info.BuildNumber -+} -+ -+//go:linkname rtlGetNtVersionNumbers syscall.rtlGetNtVersionNumbers -+func rtlGetNtVersionNumbers(majorVersion *uint32, minorVersion *uint32, buildNumber *uint32) { -+ *majorVersion = osVersionInfo.majorVersion -+ *minorVersion = osVersionInfo.minorVersion -+ *buildNumber = osVersionInfo.buildNumber -+} -+ - func osinit() { - asmstdcallAddr = unsafe.Pointer(windows.AsmStdCallAddr()) - -@@ -470,8 +564,8 @@ - initHighResTimer() - timeBeginPeriodRetValue = osRelax(false) - -- initSysDirectory() - initLongPathSupport() -+ initOsVersionInfo() - - numCPUStartup = getCPUCount() - -@@ -487,7 +581,7 @@ - //go:nosplit - func readRandom(r []byte) int { - n := 0 -- if stdcall(_ProcessPrng, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 { -+ if stdcall(_RtlGenRandom, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 { - n = len(r) - } - return n -Index: src/net/hook_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/hook_windows.go b/src/net/hook_windows.go ---- a/src/net/hook_windows.go (revision 4b29590aa510e05686ea53de16e1e571d22203d8) -+++ b/src/net/hook_windows.go (revision 2263b05b2fa6ce228fde1899587baf109f1e2e0a) -@@ -13,6 +13,7 @@ - hostsFilePath = windows.GetSystemDirectory() + "/Drivers/etc/hosts" - - // 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 -Index: src/net/internal/socktest/main_test.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/internal/socktest/main_test.go b/src/net/internal/socktest/main_test.go ---- a/src/net/internal/socktest/main_test.go (revision 4b29590aa510e05686ea53de16e1e571d22203d8) -+++ b/src/net/internal/socktest/main_test.go (revision 2263b05b2fa6ce228fde1899587baf109f1e2e0a) -@@ -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 && !windows -+//go:build !js && !plan9 && !wasip1 - - package socktest_test - -Index: src/net/internal/socktest/main_windows_test.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/internal/socktest/main_windows_test.go b/src/net/internal/socktest/main_windows_test.go -new file mode 100644 ---- /dev/null (revision 2263b05b2fa6ce228fde1899587baf109f1e2e0a) -+++ b/src/net/internal/socktest/main_windows_test.go (revision 2263b05b2fa6ce228fde1899587baf109f1e2e0a) -@@ -0,0 +1,22 @@ -+// 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 -+} -Index: src/net/internal/socktest/sys_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/internal/socktest/sys_windows.go b/src/net/internal/socktest/sys_windows.go ---- a/src/net/internal/socktest/sys_windows.go (revision 4b29590aa510e05686ea53de16e1e571d22203d8) -+++ b/src/net/internal/socktest/sys_windows.go (revision 2263b05b2fa6ce228fde1899587baf109f1e2e0a) -@@ -9,6 +9,38 @@ - "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) -Index: src/net/main_windows_test.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/main_windows_test.go b/src/net/main_windows_test.go ---- a/src/net/main_windows_test.go (revision 4b29590aa510e05686ea53de16e1e571d22203d8) -+++ b/src/net/main_windows_test.go (revision 2263b05b2fa6ce228fde1899587baf109f1e2e0a) -@@ -12,6 +12,7 @@ - - var ( - // Placeholders for saving original socket system calls. -+ origSocket = socketFunc - origWSASocket = wsaSocketFunc - origClosesocket = poll.CloseFunc - origConnect = connectFunc -@@ -21,6 +22,7 @@ - ) - - func installTestHooks() { -+ socketFunc = sw.Socket - wsaSocketFunc = sw.WSASocket - poll.CloseFunc = sw.Closesocket - connectFunc = sw.Connect -@@ -30,6 +32,7 @@ - } - - func uninstallTestHooks() { -+ socketFunc = origSocket - wsaSocketFunc = origWSASocket - poll.CloseFunc = origClosesocket - connectFunc = origConnect -Index: src/net/sock_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/net/sock_windows.go b/src/net/sock_windows.go ---- a/src/net/sock_windows.go (revision 4b29590aa510e05686ea53de16e1e571d22203d8) -+++ b/src/net/sock_windows.go (revision 2263b05b2fa6ce228fde1899587baf109f1e2e0a) -@@ -20,6 +20,21 @@ - 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) - } -Index: src/syscall/exec_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/syscall/exec_windows.go b/src/syscall/exec_windows.go ---- a/src/syscall/exec_windows.go (revision 4b29590aa510e05686ea53de16e1e571d22203d8) -+++ b/src/syscall/exec_windows.go (revision ae41f7abdd5d7b8b51db2c03bf819ac66b8e1eb1) -@@ -15,7 +15,6 @@ - "unsafe" - ) - --// ForkLock is not used on Windows. - var ForkLock sync.RWMutex - - // EscapeArg rewrites command line argument s as prescribed -@@ -304,6 +303,9 @@ - var zeroProcAttr ProcAttr - var zeroSysProcAttr SysProcAttr - -+//go:linkname rtlGetNtVersionNumbers -+func rtlGetNtVersionNumbers(majorVersion *uint32, minorVersion *uint32, buildNumber *uint32) -+ - func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle uintptr, err error) { - if len(argv0) == 0 { - return 0, 0, EWINDOWS -@@ -367,6 +369,17 @@ - } - } - -+ 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 { -@@ -375,7 +388,15 @@ - fd := make([]Handle, len(attr.Files)) - for i := range attr.Files { - if attr.Files[i] > 0 { -- err := DuplicateHandle(p, Handle(attr.Files[i]), parentProcess, &fd[i], 0, true, DUPLICATE_SAME_ACCESS) -+ 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) - if err != nil { - return 0, 0, err - } -@@ -406,6 +427,14 @@ - - 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 -Index: src/syscall/dll_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/syscall/dll_windows.go b/src/syscall/dll_windows.go ---- a/src/syscall/dll_windows.go (revision ae41f7abdd5d7b8b51db2c03bf819ac66b8e1eb1) -+++ b/src/syscall/dll_windows.go (revision ce2e1a3d2c3c0d7277b4102841db1697147d2923) -@@ -119,14 +119,7 @@ - } - - //go:linkname loadsystemlibrary --func loadsystemlibrary(filename *uint16) (uintptr, Errno) { -- const _LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800 -- handle, _, err := SyscallN(uintptr(__LoadLibraryExW), uintptr(unsafe.Pointer(filename)), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32) -- if handle != 0 { -- err = 0 -- } -- return handle, err --} -+func loadsystemlibrary(filename *uint16, absoluteFilepath *uint16) (handle uintptr, err Errno) - - //go:linkname getprocaddress - func getprocaddress(handle uintptr, procname *uint8) (uintptr, Errno) { -@@ -143,6 +136,9 @@ - Handle Handle - } - -+//go:linkname getSystemDirectory -+func getSystemDirectory() string // Implemented in runtime package. -+ - // LoadDLL loads the named DLL file into memory. - // - // If name is not an absolute path and is not a known system DLL used by -@@ -159,7 +155,11 @@ - var h uintptr - var e Errno - if sysdll.IsSystemDLL[name] { -- h, e = loadsystemlibrary(namep) -+ absoluteFilepathp, err := UTF16PtrFromString(getSystemDirectory() + name) -+ if err != nil { -+ return nil, err -+ } -+ h, e = loadsystemlibrary(namep, absoluteFilepathp) - } else { - h, e = loadlibrary(namep) - } -Index: src/os/removeall_at.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/os/removeall_at.go b/src/os/removeall_at.go ---- a/src/os/removeall_at.go (revision ce2e1a3d2c3c0d7277b4102841db1697147d2923) -+++ b/src/os/removeall_at.go (revision 4ea1045cf3124221f055dbd2f3d2c9822934f661) -@@ -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 unix || wasip1 || windows -+//go:build unix || wasip1 - - package os - -@@ -175,3 +175,25 @@ - } - return newDirFile(fd, name) - } -+ -+func rootRemoveAll(r *Root, name string) error { -+ // Consistency with os.RemoveAll: Strip trailing /s from the name, -+ // so RemoveAll("not_a_directory/") succeeds. -+ for len(name) > 0 && IsPathSeparator(name[len(name)-1]) { -+ name = name[:len(name)-1] -+ } -+ if endsWithDot(name) { -+ // Consistency with os.RemoveAll: Return EINVAL when trying to remove . -+ return &PathError{Op: "RemoveAll", Path: name, Err: syscall.EINVAL} -+ } -+ _, err := doInRoot(r, name, nil, func(parent sysfdType, name string) (struct{}, error) { -+ return struct{}{}, removeAllFrom(parent, name) -+ }) -+ if IsNotExist(err) { -+ return nil -+ } -+ if err != nil { -+ return &PathError{Op: "RemoveAll", Path: name, Err: underlyingError(err)} -+ } -+ return err -+} -Index: src/os/removeall_noat.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/os/removeall_noat.go b/src/os/removeall_noat.go ---- a/src/os/removeall_noat.go (revision ce2e1a3d2c3c0d7277b4102841db1697147d2923) -+++ b/src/os/removeall_noat.go (revision 4ea1045cf3124221f055dbd2f3d2c9822934f661) -@@ -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 && wasm) || plan9 -+//go:build (js && wasm) || plan9 || windows - - package os - -@@ -140,3 +140,22 @@ - } - return err - } -+ -+func rootRemoveAll(r *Root, name string) error { -+ if endsWithDot(name) { -+ // Consistency with os.RemoveAll: Return EINVAL when trying to remove . -+ return &PathError{Op: "RemoveAll", Path: name, Err: syscall.EINVAL} -+ } -+ if err := checkPathEscapesLstat(r, name); err != nil { -+ if err == syscall.ENOTDIR { -+ // Some intermediate path component is not a directory. -+ // RemoveAll treats this as success (since the target doesn't exist). -+ return nil -+ } -+ return &PathError{Op: "RemoveAll", Path: name, Err: err} -+ } -+ if err := RemoveAll(joinPath(r.root.name, name)); err != nil { -+ return &PathError{Op: "RemoveAll", Path: name, Err: underlyingError(err)} -+ } -+ return nil -+} -Index: src/os/root_noopenat.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/os/root_noopenat.go b/src/os/root_noopenat.go ---- a/src/os/root_noopenat.go (revision ce2e1a3d2c3c0d7277b4102841db1697147d2923) -+++ b/src/os/root_noopenat.go (revision 4ea1045cf3124221f055dbd2f3d2c9822934f661) -@@ -11,7 +11,6 @@ - "internal/filepathlite" - "internal/stringslite" - "sync/atomic" -- "syscall" - "time" - ) - -@@ -185,25 +184,6 @@ - } - return nil - } -- --func rootRemoveAll(r *Root, name string) error { -- if endsWithDot(name) { -- // Consistency with os.RemoveAll: Return EINVAL when trying to remove . -- return &PathError{Op: "RemoveAll", Path: name, Err: syscall.EINVAL} -- } -- if err := checkPathEscapesLstat(r, name); err != nil { -- if err == syscall.ENOTDIR { -- // Some intermediate path component is not a directory. -- // RemoveAll treats this as success (since the target doesn't exist). -- return nil -- } -- return &PathError{Op: "RemoveAll", Path: name, Err: err} -- } -- if err := RemoveAll(joinPath(r.root.name, name)); err != nil { -- return &PathError{Op: "RemoveAll", Path: name, Err: underlyingError(err)} -- } -- return nil --} - - func rootReadlink(r *Root, name string) (string, error) { - if err := checkPathEscapesLstat(r, name); err != nil { -Index: src/os/root_openat.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/os/root_openat.go b/src/os/root_openat.go ---- a/src/os/root_openat.go (revision ce2e1a3d2c3c0d7277b4102841db1697147d2923) -+++ b/src/os/root_openat.go (revision 4ea1045cf3124221f055dbd2f3d2c9822934f661) -@@ -196,28 +196,6 @@ - return nil - } - --func rootRemoveAll(r *Root, name string) error { -- // Consistency with os.RemoveAll: Strip trailing /s from the name, -- // so RemoveAll("not_a_directory/") succeeds. -- for len(name) > 0 && IsPathSeparator(name[len(name)-1]) { -- name = name[:len(name)-1] -- } -- if endsWithDot(name) { -- // Consistency with os.RemoveAll: Return EINVAL when trying to remove . -- return &PathError{Op: "RemoveAll", Path: name, Err: syscall.EINVAL} -- } -- _, err := doInRoot(r, name, nil, func(parent sysfdType, name string) (struct{}, error) { -- return struct{}{}, removeAllFrom(parent, name) -- }) -- if IsNotExist(err) { -- return nil -- } -- if err != nil { -- return &PathError{Op: "RemoveAll", Path: name, Err: underlyingError(err)} -- } -- return err --} -- - func rootRename(r *Root, oldname, newname string) error { - _, err := doInRoot(r, oldname, nil, func(oldparent sysfdType, oldname string) (struct{}, error) { - _, err := doInRoot(r, newname, nil, func(newparent sysfdType, newname string) (struct{}, error) { -Index: src/os/root_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/os/root_windows.go b/src/os/root_windows.go ---- a/src/os/root_windows.go (revision ce2e1a3d2c3c0d7277b4102841db1697147d2923) -+++ b/src/os/root_windows.go (revision 4ea1045cf3124221f055dbd2f3d2c9822934f661) -@@ -402,3 +402,14 @@ - } - return fi.Mode(), nil - } -+ -+func checkPathEscapes(r *Root, name string) error { -+ if !filepathlite.IsLocal(name) { -+ return errPathEscapes -+ } -+ return nil -+} -+ -+func checkPathEscapesLstat(r *Root, name string) error { -+ return checkPathEscapes(r, name) -+} -Index: src/os/exec_windows.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/src/os/exec_windows.go b/src/os/exec_windows.go ---- a/src/os/exec_windows.go (revision 4ea1045cf3124221f055dbd2f3d2c9822934f661) -+++ b/src/os/exec_windows.go (revision 8149d992682ce76c6af804b507878e19fc966f7b) -@@ -10,6 +10,7 @@ - "runtime" - "syscall" - "time" -+ _ "unsafe" - ) - - // Note that Process.handle is never nil because Windows always requires -@@ -49,9 +50,23 @@ - // than statusDone. - p.doRelease(statusReleased) - -+ var maj, min, build uint32 -+ rtlGetNtVersionNumbers(&maj, &min, &build) -+ if maj < 10 { -+ // NOTE(brainman): It seems that sometimes process is not dead -+ // when WaitForSingleObject returns. But we do not know any -+ // other way to wait for it. Sleeping for a while seems to do -+ // the trick sometimes. -+ // See https://golang.org/issue/25965 for details. -+ time.Sleep(5 * time.Millisecond) -+ } -+ - return &ProcessState{p.Pid, syscall.WaitStatus{ExitCode: ec}, &u}, nil - } - -+//go:linkname rtlGetNtVersionNumbers syscall.rtlGetNtVersionNumbers -+func rtlGetNtVersionNumbers(majorVersion *uint32, minorVersion *uint32, buildNumber *uint32) -+ - func (p *Process) signal(sig Signal) error { - handle, status := p.handleTransientAcquire() - switch status { diff --git a/mihomo/.github/workflows/build.yml b/mihomo/.github/workflows/build.yml index 2b8c1671e6..d978787047 100644 --- a/mihomo/.github/workflows/build.yml +++ b/mihomo/.github/workflows/build.yml @@ -29,13 +29,6 @@ jobs: strategy: matrix: jobs: - - { goos: darwin, goarch: amd64, goamd64: v1, output: amd64-compatible } # old style file name will be removed in next released - - { goos: darwin, goarch: amd64, goamd64: v3, output: amd64 } - - { goos: darwin, goarch: amd64, goamd64: v1, output: amd64-v1 } - - { goos: darwin, goarch: amd64, goamd64: v2, output: amd64-v2 } - - { goos: darwin, goarch: amd64, goamd64: v3, output: amd64-v3 } - - { goos: darwin, goarch: arm64, output: arm64 } - - { goos: linux, goarch: '386', go386: sse2, output: '386', debian: i386, rpm: i386} - { goos: linux, goarch: '386', go386: softfloat, output: '386-softfloat' } - { goos: linux, goarch: amd64, goamd64: v1, output: amd64-compatible} # old style file name will be removed in next released @@ -59,6 +52,15 @@ jobs: - { goos: linux, goarch: s390x, output: s390x, debian: s390x, rpm: s390x } - { goos: linux, goarch: ppc64le, output: ppc64le, debian: ppc64el, rpm: ppc64le } + # Go 1.26 with special patch can work on macOS 10.13 High Sierra + # https://github.com/MetaCubeX/go/commits/release-branch.go1.26/ + - { goos: darwin, goarch: amd64, goamd64: v1, output: amd64-compatible } # old style file name will be removed in next released + - { goos: darwin, goarch: amd64, goamd64: v3, output: amd64 } + - { goos: darwin, goarch: amd64, goamd64: v1, output: amd64-v1 } + - { goos: darwin, goarch: amd64, goamd64: v2, output: amd64-v2 } + - { goos: darwin, goarch: amd64, goamd64: v3, output: amd64-v3 } + - { goos: darwin, goarch: arm64, output: arm64 } + # Go 1.26 with special patch can work on Windows 7 # https://github.com/MetaCubeX/go/commits/release-branch.go1.26/ - { goos: windows, goarch: '386', output: '386' } @@ -159,17 +161,17 @@ jobs: - name: Set up Go if: ${{ matrix.jobs.goversion == '' }} - uses: actions/setup-go@v6 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c with: + go-download-base-url: 'https://github.com/MetaCubeX/go/releases/download/build' go-version: '1.26' - check-latest: true # Always check for the latest patch release - name: Set up Go if: ${{ matrix.jobs.goversion != '' && matrix.jobs.goversion != 'custom' }} - uses: actions/setup-go@v6 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c with: + go-download-base-url: 'https://github.com/MetaCubeX/go/releases/download/build' go-version: ${{ matrix.jobs.goversion }} - check-latest: true # Always check for the latest patch release - name: Set up Go1.26 loongarch abi1 if: ${{ matrix.jobs.goarch == 'loong64' && matrix.jobs.abi == '1' }} @@ -178,6 +180,12 @@ jobs: go-download-base-url: 'https://github.com/MetaCubeX/loongarch64-golang/releases/download/1.26.0' go-version: 1.26.0 + - name: Verify Go installation + run: go version + + - name: Verify Go env + run: go env + # TODO: remove after issue77731 fixed, see: https://github.com/golang/go/issues/77731 - name: Fix issue77731 for Golang1.26 if: ${{ matrix.jobs.goversion == '' }} @@ -199,89 +207,6 @@ jobs: cd $(go env GOROOT) patch --verbose -p 1 < $GITHUB_WORKSPACE/.github/patch/issue77930.patch - # this patch file only works on golang1.26.x - # that means after golang1.27 release it must be changed - # see: https://github.com/MetaCubeX/go/commits/release-branch.go1.26/ - # 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" - # f0894a00f4b756d4b9b4078af2e686b359493583: "os: remove 5ms sleep on Windows in (*Process).Wait" - # sepical fix: - # - os.RemoveAll not working on Windows7 - - name: Revert Golang1.26 commit for Windows7/8 - if: ${{ matrix.jobs.goos == 'windows' && matrix.jobs.goversion == '' }} - run: | - cd $(go env GOROOT) - patch --verbose -p 1 < $GITHUB_WORKSPACE/.github/patch/go1.26.patch - - # this patch file only works on golang1.25.x - # that means after golang1.26 release it must be changed - # see: https://github.com/MetaCubeX/go/commits/release-branch.go1.25/ - # 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" - # f0894a00f4b756d4b9b4078af2e686b359493583: "os: remove 5ms sleep on Windows in (*Process).Wait" - # sepical fix: - # - os.RemoveAll not working on Windows7 - - name: Revert Golang1.25 commit for Windows7/8 - if: ${{ matrix.jobs.goos == 'windows' && matrix.jobs.goversion == '1.25' }} - run: | - cd $(go env GOROOT) - patch --verbose -p 1 < $GITHUB_WORKSPACE/.github/patch/go1.25.patch - - # this patch file only works on golang1.24.x - # that means after golang1.25 release it must be changed - # see: https://github.com/MetaCubeX/go/commits/release-branch.go1.24/ - # 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.24 commit for Windows7/8 - if: ${{ matrix.jobs.goos == 'windows' && matrix.jobs.goversion == '1.24' }} - run: | - cd $(go env GOROOT) - patch --verbose -p 1 < $GITHUB_WORKSPACE/.github/patch/go1.24.patch - - # 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" - # 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 == '1.23' }} - run: | - cd $(go env GOROOT) - patch --verbose -p 1 < $GITHUB_WORKSPACE/.github/patch/go1.23.patch - - # 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) - patch --verbose -p 1 < $GITHUB_WORKSPACE/.github/patch/go1.22.patch - - # modify from https://github.com/restic/restic/issues/4636#issuecomment-1896455557 - - name: Revert Golang1.21 commit for Windows7/8 - if: ${{ matrix.jobs.goos == 'windows' && matrix.jobs.goversion == '1.21' }} - run: | - cd $(go env GOROOT) - patch --verbose -p 1 < $GITHUB_WORKSPACE/.github/patch/go1.21.patch - - name: Set variables run: | VERSION="${GITHUB_REF_NAME,,}-$(git rev-parse --short HEAD)" @@ -316,6 +241,7 @@ jobs: - name: Test if: ${{ matrix.jobs.test == 'test' }} run: | + export SKIP_CONCURRENT_TEST=1 go test ./... echo "---test with_gvisor---" go test ./... -tags "with_gvisor" -count=1 diff --git a/mihomo/.github/workflows/test.yml b/mihomo/.github/workflows/test.yml index 13a08a4ad5..e72e7b2f3d 100644 --- a/mihomo/.github/workflows/test.yml +++ b/mihomo/.github/workflows/test.yml @@ -44,11 +44,17 @@ jobs: steps: - uses: actions/checkout@v6 - - name: Setup Go - uses: actions/setup-go@v6 + - name: Set up Go + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c with: + go-download-base-url: 'https://github.com/MetaCubeX/go/releases/download/build' go-version: ${{ matrix.go-version }} - check-latest: true # Always check for the latest patch release + + - name: Verify Go installation + run: go version + + - name: Verify Go env + run: go env # TODO: remove after issue77975 fixed, see: https://github.com/golang/go/issues/77975 - name: Fix issue77975 for Golang1.26 @@ -57,12 +63,6 @@ jobs: cd $(go env GOROOT) patch --verbose -p 1 < $GITHUB_WORKSPACE/.github/patch/issue77975.patch - - name: Revert Golang commit for Windows7/8 - if: ${{ runner.os == 'Windows' && matrix.go-version != '1.20' }} - run: | - cd $(go env GOROOT) - patch --verbose -p 1 < $GITHUB_WORKSPACE/.github/patch/go${{matrix.go-version}}.patch - - name: Remove inbound test for macOS if: ${{ runner.os == 'macOS' }} run: | diff --git a/mihomo/adapter/outbound/vless.go b/mihomo/adapter/outbound/vless.go index 78ccb6676f..dfe7d6b67e 100644 --- a/mihomo/adapter/outbound/vless.go +++ b/mihomo/adapter/outbound/vless.go @@ -17,6 +17,7 @@ import ( "github.com/metacubex/mihomo/transport/vless" "github.com/metacubex/mihomo/transport/vless/encryption" "github.com/metacubex/mihomo/transport/vmess" + "github.com/metacubex/mihomo/transport/xhttp" "github.com/metacubex/http" vmessSing "github.com/metacubex/sing-vmess" @@ -60,6 +61,7 @@ type VlessOption struct { HTTP2Opts HTTP2Options `proxy:"h2-opts,omitempty"` GrpcOpts GrpcOptions `proxy:"grpc-opts,omitempty"` WSOpts WSOptions `proxy:"ws-opts,omitempty"` + XHTTPOpts XHTTPOptions `proxy:"xhttp-opts,omitempty"` WSHeaders map[string]string `proxy:"ws-headers,omitempty"` SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` Fingerprint string `proxy:"fingerprint,omitempty"` @@ -69,6 +71,16 @@ type VlessOption struct { ClientFingerprint string `proxy:"client-fingerprint,omitempty"` } +type XHTTPOptions struct { + Path string `proxy:"path,omitempty"` + Host string `proxy:"host,omitempty"` + Mode string `proxy:"mode,omitempty"` + Headers map[string]string `proxy:"headers,omitempty"` + ScMaxConcurrentPosts int `proxy:"sc-max-concurrent-posts,omitempty"` + NoGRPCHeader bool `proxy:"no-grpc-header,omitempty"` + XPaddingBytes string `proxy:"x-padding-bytes,omitempty"` +} + func (v *Vless) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.Metadata) (_ net.Conn, err error) { switch v.option.Network { case "ws": @@ -150,6 +162,8 @@ func (v *Vless) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.M c, err = vmess.StreamH2Conn(ctx, c, h2Opts) case "grpc": break // already handle in gun transport + case "xhttp": + break // already handle in dialXHTTPConn default: // default tcp network // handle TLS @@ -228,13 +242,83 @@ func (v *Vless) streamTLSConn(ctx context.Context, conn net.Conn, isH2 bool) (ne return conn, nil } +func (v *Vless) dialXHTTPConn(ctx context.Context) (net.Conn, error) { + requestHost := v.option.XHTTPOpts.Host + if requestHost == "" { + if v.option.ServerName != "" { + requestHost = v.option.ServerName + } else { + requestHost = v.option.Server + } + } + + cfg := &xhttp.Config{ + Host: requestHost, + Path: v.option.XHTTPOpts.Path, + Mode: v.option.XHTTPOpts.Mode, + Headers: v.option.XHTTPOpts.Headers, + NoGRPCHeader: v.option.XHTTPOpts.NoGRPCHeader, + XPaddingBytes: v.option.XHTTPOpts.XPaddingBytes, + } + + mode := cfg.EffectiveMode(v.realityConfig != nil) + + switch mode { + case "stream-one": + return xhttp.DialStreamOne( + ctx, + v.option.Server, + v.option.Port, + cfg, + func(ctx context.Context) (net.Conn, error) { + return v.dialer.DialContext(ctx, "tcp", v.addr) + }, + func(ctx context.Context, raw net.Conn, isH2 bool) (net.Conn, error) { + return v.streamTLSConn(ctx, raw, isH2) + }, + ) + case "packet-up": + return xhttp.DialPacketUp( + ctx, + v.option.Server, + v.option.Port, + cfg, + func(ctx context.Context) (net.Conn, error) { + return v.dialer.DialContext(ctx, "tcp", v.addr) + }, + func(ctx context.Context, raw net.Conn, isH2 bool) (net.Conn, error) { + return v.streamTLSConn(ctx, raw, isH2) + }, + ) + default: + return nil, fmt.Errorf("xhttp mode %s is not implemented yet", mode) + } +} + // DialContext implements C.ProxyAdapter func (v *Vless) DialContext(ctx context.Context, metadata *C.Metadata) (_ C.Conn, err error) { + if v.option.Network == "xhttp" { + c, err := v.dialXHTTPConn(ctx) + if err != nil { + return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error()) + } + + c, err = v.streamConnContext(ctx, c, metadata) + if err != nil { + safeConnClose(c, err) + return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error()) + } + + return NewConn(c, v), nil + } + var c net.Conn - // gun transport - if v.gunTransport != nil { + switch v.option.Network { + case "xhttp": + c, err = v.dialXHTTPConn(ctx) + case "grpc": // gun transport c, err = v.gunTransport.Dial() - } else { + default: c, err = v.dialer.DialContext(ctx, "tcp", v.addr) } if err != nil { @@ -256,11 +340,14 @@ func (v *Vless) ListenPacketContext(ctx context.Context, metadata *C.Metadata) ( if err = v.ResolveUDP(ctx, metadata); err != nil { return nil, err } + var c net.Conn - // gun transport - if v.gunTransport != nil { + switch v.option.Network { + case "xhttp": + c, err = v.dialXHTTPConn(ctx) + case "grpc": // gun transport c, err = v.gunTransport.Dial() - } else { + default: c, err = v.dialer.DialContext(ctx, "tcp", v.addr) } if err != nil { diff --git a/mihomo/common/contextutils/withoutcancel_compact.go b/mihomo/common/contextutils/withoutcancel_compact.go new file mode 100644 index 0000000000..942b6503f1 --- /dev/null +++ b/mihomo/common/contextutils/withoutcancel_compact.go @@ -0,0 +1,26 @@ +package contextutils + +import ( + "context" + "time" +) + +type withoutCancelCtx struct { + c context.Context +} + +func (withoutCancelCtx) Deadline() (deadline time.Time, ok bool) { + return +} + +func (withoutCancelCtx) Done() <-chan struct{} { + return nil +} + +func (withoutCancelCtx) Err() error { + return nil +} + +func (c withoutCancelCtx) Value(key any) any { + return c.c.Value(key) +} diff --git a/mihomo/common/contextutils/withoutcancel_go120.go b/mihomo/common/contextutils/withoutcancel_go120.go new file mode 100644 index 0000000000..44c6a941b1 --- /dev/null +++ b/mihomo/common/contextutils/withoutcancel_go120.go @@ -0,0 +1,12 @@ +//go:build !go1.21 + +package contextutils + +import "context" + +func WithoutCancel(parent context.Context) context.Context { + if parent == nil { + panic("cannot create context from nil parent") + } + return withoutCancelCtx{parent} +} diff --git a/mihomo/common/contextutils/withoutcancel_go121.go b/mihomo/common/contextutils/withoutcancel_go121.go new file mode 100644 index 0000000000..f955db9270 --- /dev/null +++ b/mihomo/common/contextutils/withoutcancel_go121.go @@ -0,0 +1,9 @@ +//go:build go1.21 + +package contextutils + +import "context" + +func WithoutCancel(parent context.Context) context.Context { + return context.WithoutCancel(parent) +} diff --git a/mihomo/listener/config/vless.go b/mihomo/listener/config/vless.go index 7facaf25f3..931b4fd8bf 100644 --- a/mihomo/listener/config/vless.go +++ b/mihomo/listener/config/vless.go @@ -19,6 +19,7 @@ type VlessServer struct { Users []VlessUser Decryption string WsPath string + XHTTPConfig XHTTPConfig GrpcServiceName string Certificate string PrivateKey string @@ -29,6 +30,12 @@ type VlessServer struct { MuxOption sing.MuxOption `yaml:"mux-option" json:"mux-option,omitempty"` } +type XHTTPConfig struct { + Path string + Host string + Mode string +} + func (t VlessServer) String() string { b, _ := json.Marshal(t) return string(b) diff --git a/mihomo/listener/inbound/common_test.go b/mihomo/listener/inbound/common_test.go index b1be5e4d57..eabdb6b111 100644 --- a/mihomo/listener/inbound/common_test.go +++ b/mihomo/listener/inbound/common_test.go @@ -9,6 +9,7 @@ import ( "io" "net" "net/netip" + "os" "strconv" "sync" "testing" @@ -61,7 +62,6 @@ type TestTunnel struct { HandleUDPPacketFn func(packet C.UDPPacket, metadata *C.Metadata) NatTableFn func() C.NatTable CloseFn func() error - DoTestFn func(t *testing.T, proxy C.ProxyAdapter) DoSequentialTestFn func(t *testing.T, proxy C.ProxyAdapter) DoConcurrentTestFn func(t *testing.T, proxy C.ProxyAdapter) } @@ -83,7 +83,8 @@ func (tt *TestTunnel) Close() error { } func (tt *TestTunnel) DoTest(t *testing.T, proxy C.ProxyAdapter) { - tt.DoTestFn(t, proxy) + tt.DoSequentialTestFn(t, proxy) + tt.DoConcurrentTestFn(t, proxy) } func (tt *TestTunnel) DoSequentialTest(t *testing.T, proxy C.ProxyAdapter) { @@ -236,6 +237,9 @@ func NewHttpTestTunnel() *TestTunnel { concurrentTestFn := func(t *testing.T, proxy C.ProxyAdapter) { // Concurrent testing to detect stress t.Run("Concurrent", func(t *testing.T) { + if skip, _ := strconv.ParseBool(os.Getenv("SKIP_CONCURRENT_TEST")); skip { + t.Skip("skip concurrent test") + } wg := sync.WaitGroup{} num := len(httpData) / 1024 for i := 1; i <= num; i++ { @@ -296,11 +300,7 @@ func NewHttpTestTunnel() *TestTunnel { } <-c.ch }, - CloseFn: ln.Close, - DoTestFn: func(t *testing.T, proxy C.ProxyAdapter) { - sequentialTestFn(t, proxy) - concurrentTestFn(t, proxy) - }, + CloseFn: ln.Close, DoSequentialTestFn: sequentialTestFn, DoConcurrentTestFn: concurrentTestFn, } diff --git a/mihomo/listener/inbound/mux_test.go b/mihomo/listener/inbound/mux_test.go index 0f632220e6..09facdb87b 100644 --- a/mihomo/listener/inbound/mux_test.go +++ b/mihomo/listener/inbound/mux_test.go @@ -6,9 +6,11 @@ import ( "github.com/metacubex/mihomo/adapter/outbound" "github.com/stretchr/testify/assert" + "golang.org/x/exp/slices" ) -var singMuxProtocolList = []string{"smux", "yamux"} // don't test "h2mux" because it has some confused bugs +var singMuxProtocolList = []string{"h2mux", "smux", "yamux"} +var singMuxProtocolListLong = []string{"yamux"} // don't test "smux", "h2mux" because it has some confused bugs // notCloseProxyAdapter is a proxy adapter that does not close the underlying outbound.ProxyAdapter. // The outbound.SingMux will close the underlying outbound.ProxyAdapter when it is closed, but we don't want to close it. @@ -37,7 +39,10 @@ func testSingMux(t *testing.T, tunnel *TestTunnel, out outbound.ProxyAdapter) { } defer out.Close() - tunnel.DoTest(t, out) + tunnel.DoSequentialTest(t, out) + if slices.Contains(singMuxProtocolListLong, protocol) { + tunnel.DoConcurrentTest(t, out) + } }) } }) diff --git a/mihomo/listener/inbound/vless.go b/mihomo/listener/inbound/vless.go index d8109f8443..1a88cc7411 100644 --- a/mihomo/listener/inbound/vless.go +++ b/mihomo/listener/inbound/vless.go @@ -14,6 +14,7 @@ type VlessOption struct { Users []VlessUser `inbound:"users"` Decryption string `inbound:"decryption,omitempty"` WsPath string `inbound:"ws-path,omitempty"` + XHTTPConfig XHTTPConfig `inbound:"xhttp-config,omitempty"` GrpcServiceName string `inbound:"grpc-service-name,omitempty"` Certificate string `inbound:"certificate,omitempty"` PrivateKey string `inbound:"private-key,omitempty"` @@ -30,6 +31,20 @@ type VlessUser struct { Flow string `inbound:"flow,omitempty"` } +type XHTTPConfig struct { + Path string `inbound:"path,omitempty"` + Host string `inbound:"host,omitempty"` + Mode string `inbound:"mode,omitempty"` +} + +func (o XHTTPConfig) Build() LC.XHTTPConfig { + return LC.XHTTPConfig{ + Path: o.Path, + Host: o.Host, + Mode: o.Mode, + } +} + func (o VlessOption) Equal(config C.InboundConfig) bool { return optionToString(o) == optionToString(config) } @@ -63,6 +78,7 @@ func NewVless(options *VlessOption) (*Vless, error) { Users: users, Decryption: options.Decryption, WsPath: options.WsPath, + XHTTPConfig: options.XHTTPConfig.Build(), GrpcServiceName: options.GrpcServiceName, Certificate: options.Certificate, PrivateKey: options.PrivateKey, diff --git a/mihomo/listener/inbound/vless_test.go b/mihomo/listener/inbound/vless_test.go index e12e95567d..5b5d68f89d 100644 --- a/mihomo/listener/inbound/vless_test.go +++ b/mihomo/listener/inbound/vless_test.go @@ -340,3 +340,54 @@ func TestInboundVless_Reality_Grpc(t *testing.T) { testInboundVless(t, inboundOptions, outboundOptions) }) } + +func TestInboundVless_XHTTP(t *testing.T) { + inboundOptions := inbound.VlessOption{ + Certificate: tlsCertificate, + PrivateKey: tlsPrivateKey, + XHTTPConfig: inbound.XHTTPConfig{ + Path: "/vless-xhttp", + Host: "example.com", + Mode: "auto", + }, + } + outboundOptions := outbound.VlessOption{ + TLS: true, + Fingerprint: tlsFingerprint, + Network: "xhttp", + XHTTPOpts: outbound.XHTTPOptions{ + Path: "/vless-xhttp", + Host: "example.com", + Mode: "auto", + }, + } + testInboundVlessTLS(t, inboundOptions, outboundOptions, false) +} + +func TestInboundVless_Reality_XHTTP(t *testing.T) { + inboundOptions := inbound.VlessOption{ + RealityConfig: inbound.RealityConfig{ + Dest: net.JoinHostPort(realityDest, "443"), + PrivateKey: realityPrivateKey, + ShortID: []string{realityShortid}, + ServerNames: []string{realityDest}, + }, + XHTTPConfig: inbound.XHTTPConfig{ + Mode: "auto", + }, + } + outboundOptions := outbound.VlessOption{ + TLS: true, + ServerName: realityDest, + RealityOpts: outbound.RealityOptions{ + PublicKey: realityPublickey, + ShortID: realityShortid, + }, + ClientFingerprint: "chrome", + Network: "xhttp", + XHTTPOpts: outbound.XHTTPOptions{ + Mode: "auto", + }, + } + testInboundVless(t, inboundOptions, outboundOptions) +} diff --git a/mihomo/listener/sing_vless/server.go b/mihomo/listener/sing_vless/server.go index 83ba577924..76566df986 100644 --- a/mihomo/listener/sing_vless/server.go +++ b/mihomo/listener/sing_vless/server.go @@ -17,11 +17,13 @@ import ( "github.com/metacubex/mihomo/transport/gun" "github.com/metacubex/mihomo/transport/vless/encryption" mihomoVMess "github.com/metacubex/mihomo/transport/vmess" + "github.com/metacubex/mihomo/transport/xhttp" "github.com/metacubex/http" "github.com/metacubex/sing/common" "github.com/metacubex/sing/common/metadata" "github.com/metacubex/tls" + "golang.org/x/exp/slices" ) type Listener struct { @@ -144,7 +146,27 @@ func New(config LC.VlessServer, tunnel C.Tunnel, additions ...inbound.Addition) }) tlsConfig.NextProtos = append([]string{"h2"}, tlsConfig.NextProtos...) // h2 must before http/1.1 } - + if config.XHTTPConfig.Mode != "" { + switch config.XHTTPConfig.Mode { + case "auto": + default: + return nil, errors.New("unsupported xhttp mode") + } + } + if config.XHTTPConfig.Path != "" || config.XHTTPConfig.Host != "" || config.XHTTPConfig.Mode != "" { + httpServer.Handler = xhttp.NewServerHandler(xhttp.ServerOption{ + Path: config.XHTTPConfig.Path, + Host: config.XHTTPConfig.Host, + Mode: config.XHTTPConfig.Mode, + ConnHandler: func(conn net.Conn) { + sl.HandleConn(conn, tunnel, additions...) + }, + HttpHandler: httpServer.Handler, + }) + if !slices.Contains(tlsConfig.NextProtos, "h2") { + tlsConfig.NextProtos = append([]string{"h2"}, tlsConfig.NextProtos...) + } + } for _, addr := range strings.Split(config.Listen, ",") { addr := addr diff --git a/mihomo/transport/xhttp/client.go b/mihomo/transport/xhttp/client.go new file mode 100644 index 0000000000..093ee1d238 --- /dev/null +++ b/mihomo/transport/xhttp/client.go @@ -0,0 +1,293 @@ +package xhttp + +import ( + "context" + "crypto/rand" + "encoding/hex" + "fmt" + "io" + "net" + "net/url" + "strconv" + "sync" + "time" + + "github.com/metacubex/mihomo/common/contextutils" + "github.com/metacubex/mihomo/transport/gun" + + "github.com/metacubex/http" + "github.com/metacubex/http/httptrace" + "github.com/metacubex/tls" +) + +type DialRawFunc func(ctx context.Context) (net.Conn, error) +type WrapTLSFunc func(ctx context.Context, conn net.Conn, isH2 bool) (net.Conn, error) + +type PacketUpConn struct { + ctx context.Context + cfg *Config + address string + port int + host string + sessionID string + client *http.Client + writeMu sync.Mutex + seq uint64 + reader io.ReadCloser + gun.NetAddr + + // deadlines + deadline *time.Timer +} + +func (c *PacketUpConn) Read(b []byte) (int, error) { + return c.reader.Read(b) +} + +func (c *PacketUpConn) Write(b []byte) (int, error) { + c.writeMu.Lock() + defer c.writeMu.Unlock() + + u := url.URL{ + Scheme: "https", + Host: c.host, + Path: c.cfg.NormalizedPath(), + } + + req, err := http.NewRequestWithContext(c.ctx, http.MethodPost, u.String(), nil) + if err != nil { + return 0, err + } + + seqStr := strconv.FormatUint(c.seq, 10) + c.seq++ + + if err := c.cfg.FillPacketRequest(req, c.sessionID, seqStr, b); err != nil { + return 0, err + } + req.Host = c.host + + resp, err := c.client.Do(req) + if err != nil { + return 0, err + } + defer resp.Body.Close() + _, _ = io.Copy(io.Discard, resp.Body) + + if resp.StatusCode != http.StatusOK { + return 0, fmt.Errorf("xhttp packet-up bad status: %s", resp.Status) + } + + return len(b), nil +} + +func (c *PacketUpConn) Close() error { + if c.reader != nil { + return c.reader.Close() + } + return nil +} + +func (c *PacketUpConn) SetReadDeadline(t time.Time) error { return c.SetDeadline(t) } +func (c *PacketUpConn) SetWriteDeadline(t time.Time) error { return c.SetDeadline(t) } + +func (c *PacketUpConn) SetDeadline(t time.Time) error { + if t.IsZero() { + if c.deadline != nil { + c.deadline.Stop() + c.deadline = nil + } + return nil + } + d := time.Until(t) + if c.deadline != nil { + c.deadline.Reset(d) + return nil + } + c.deadline = time.AfterFunc(d, func() { + c.Close() + }) + return nil +} + +func DialStreamOne( + ctx context.Context, + address string, + port int, + cfg *Config, + dialRaw DialRawFunc, + wrapTLS WrapTLSFunc, +) (net.Conn, error) { + host := cfg.Host + if host == "" { + host = address + } + + requestURL := url.URL{ + Scheme: "https", + Host: host, + Path: cfg.NormalizedPath(), + } + + transport := &http.Http2Transport{ + DialTLSContext: func(ctx context.Context, network, addr string, _ *tls.Config) (net.Conn, error) { + raw, err := dialRaw(ctx) + if err != nil { + return nil, err + } + wrapped, err := wrapTLS(ctx, raw, true) + if err != nil { + _ = raw.Close() + return nil, err + } + return wrapped, nil + }, + } + + client := &http.Client{ + Transport: transport, + } + + pr, pw := io.Pipe() + + conn := &Conn{ + writer: pw, + } + + trace := &httptrace.ClientTrace{ + GotConn: func(connInfo httptrace.GotConnInfo) { + conn.SetLocalAddr(connInfo.Conn.LocalAddr()) + conn.SetRemoteAddr(connInfo.Conn.RemoteAddr()) + }, + } + + req, err := http.NewRequestWithContext(httptrace.WithClientTrace(contextutils.WithoutCancel(ctx), trace), http.MethodPost, requestURL.String(), pr) + if err != nil { + _ = pr.Close() + _ = pw.Close() + return nil, err + } + req.Host = host + + if err := cfg.FillStreamRequest(req); err != nil { + _ = pr.Close() + _ = pw.Close() + return nil, err + } + + type respResult struct { + resp *http.Response + err error + } + + respCh := make(chan respResult, 1) + + go func() { + resp, err := client.Do(req) + respCh <- respResult{resp: resp, err: err} + }() + + result := <-respCh + if result.err != nil { + _ = pr.Close() + _ = pw.Close() + return nil, result.err + } + if result.resp.StatusCode < 200 || result.resp.StatusCode >= 300 { + _ = result.resp.Body.Close() + _ = pr.Close() + _ = pw.Close() + return nil, fmt.Errorf("xhttp stream-one bad status: %s", result.resp.Status) + } + conn.reader = result.resp.Body + conn.onClose = func() { + _ = result.resp.Body.Close() + _ = pr.Close() + } + + return conn, nil +} + +func DialPacketUp( + ctx context.Context, + address string, + port int, + cfg *Config, + dialRaw DialRawFunc, + wrapTLS WrapTLSFunc, +) (net.Conn, error) { + host := cfg.Host + if host == "" { + host = address + } + + transport := &http.Http2Transport{ + DialTLSContext: func(ctx context.Context, network string, addr string, _ *tls.Config) (net.Conn, error) { + raw, err := dialRaw(ctx) + if err != nil { + return nil, err + } + wrapped, err := wrapTLS(ctx, raw, true) + if err != nil { + _ = raw.Close() + return nil, err + } + return wrapped, nil + }, + } + + client := &http.Client{Transport: transport} + + sessionID := newSessionID() + + downloadURL := url.URL{ + Scheme: "https", + Host: host, + Path: cfg.NormalizedPath(), + } + + conn := &PacketUpConn{ + ctx: contextutils.WithoutCancel(ctx), + cfg: cfg, + address: address, + port: port, + host: host, + sessionID: sessionID, + client: client, + seq: 0, + } + + trace := &httptrace.ClientTrace{ + GotConn: func(connInfo httptrace.GotConnInfo) { + conn.SetLocalAddr(connInfo.Conn.LocalAddr()) + conn.SetRemoteAddr(connInfo.Conn.RemoteAddr()) + }, + } + + req, err := http.NewRequestWithContext(httptrace.WithClientTrace(conn.ctx, trace), http.MethodGet, downloadURL.String(), nil) + if err != nil { + return nil, err + } + if err := cfg.FillDownloadRequest(req, sessionID); err != nil { + return nil, err + } + req.Host = host + + resp, err := client.Do(req) + if err != nil { + return nil, err + } + if resp.StatusCode != http.StatusOK { + _ = resp.Body.Close() + return nil, fmt.Errorf("xhttp packet-up download bad status: %s", resp.Status) + } + conn.reader = resp.Body + + return conn, nil +} + +func newSessionID() string { + var b [16]byte + _, _ = rand.Read(b[:]) + return hex.EncodeToString(b[:]) +} diff --git a/mihomo/transport/xhttp/config.go b/mihomo/transport/xhttp/config.go new file mode 100644 index 0000000000..eda9e26bf2 --- /dev/null +++ b/mihomo/transport/xhttp/config.go @@ -0,0 +1,209 @@ +package xhttp + +import ( + "bytes" + "fmt" + "io" + "math/rand" + "strconv" + "strings" + + "github.com/metacubex/http" +) + +type Config struct { + Host string + Path string + Mode string + Headers map[string]string + NoGRPCHeader bool + XPaddingBytes string +} + +func (c *Config) NormalizedMode() string { + if c.Mode == "" { + return "auto" + } + return c.Mode +} + +func (c *Config) EffectiveMode(hasReality bool) string { + mode := c.NormalizedMode() + if mode != "auto" { + return mode + } + if hasReality { + return "stream-one" + } + return "packet-up" +} + +func (c *Config) NormalizedPath() string { + path := c.Path + if path == "" { + path = "/" + } + if !strings.HasPrefix(path, "/") { + path = "/" + path + } + if !strings.HasSuffix(path, "/") { + path += "/" + } + return path +} + +func (c *Config) RequestHeader() http.Header { + h := http.Header{} + for k, v := range c.Headers { + h.Set(k, v) + } + + if h.Get("User-Agent") == "" { + h.Set("User-Agent", "Mozilla/5.0") + } + if h.Get("Accept") == "" { + h.Set("Accept", "*/*") + } + if h.Get("Accept-Language") == "" { + h.Set("Accept-Language", "en-US,en;q=0.9") + } + if h.Get("Cache-Control") == "" { + h.Set("Cache-Control", "no-cache") + } + if h.Get("Pragma") == "" { + h.Set("Pragma", "no-cache") + } + + return h +} + +func (c *Config) RandomPadding() (string, error) { + paddingRange := c.XPaddingBytes + if paddingRange == "" { + paddingRange = "100-1000" + } + + minVal, maxVal, err := parseRange(paddingRange) + if err != nil { + return "", err + } + if minVal < 0 || maxVal < minVal { + return "", fmt.Errorf("invalid x-padding-bytes range: %s", paddingRange) + } + if maxVal == 0 { + return "", nil + } + + n := minVal + if maxVal > minVal { + n = minVal + rand.Intn(maxVal-minVal+1) + } + + return strings.Repeat("X", n), nil +} + +func parseRange(s string) (int, int, error) { + parts := strings.Split(strings.TrimSpace(s), "-") + if len(parts) == 1 { + v, err := strconv.Atoi(parts[0]) + if err != nil { + return 0, 0, err + } + return v, v, nil + } + if len(parts) != 2 { + return 0, 0, fmt.Errorf("invalid range: %s", s) + } + + minVal, err := strconv.Atoi(strings.TrimSpace(parts[0])) + if err != nil { + return 0, 0, err + } + maxVal, err := strconv.Atoi(strings.TrimSpace(parts[1])) + if err != nil { + return 0, 0, err + } + return minVal, maxVal, nil +} + +func (c *Config) FillStreamRequest(req *http.Request) error { + req.Header = c.RequestHeader() + + paddingValue, err := c.RandomPadding() + if err != nil { + return err + } + + if paddingValue != "" { + rawURL := req.URL.String() + sep := "?" + if strings.Contains(rawURL, "?") { + sep = "&" + } + req.Header.Set("Referer", rawURL+sep+"x_padding="+paddingValue) + } + + if req.Body != nil && !c.NoGRPCHeader { + req.Header.Set("Content-Type", "application/grpc") + } + + return nil +} + +func appendToPath(path, value string) string { + if strings.HasSuffix(path, "/") { + return path + value + } + return path + "/" + value +} + +func (c *Config) ApplyMetaToRequest(req *http.Request, sessionID string, seqStr string) { + if sessionID != "" { + req.URL.Path = appendToPath(req.URL.Path, sessionID) + } + if seqStr != "" { + req.URL.Path = appendToPath(req.URL.Path, seqStr) + } +} + +func (c *Config) FillPacketRequest(req *http.Request, sessionID string, seqStr string, payload []byte) error { + req.Header = c.RequestHeader() + req.Body = io.NopCloser(bytes.NewReader(payload)) + req.ContentLength = int64(len(payload)) + + paddingValue, err := c.RandomPadding() + if err != nil { + return err + } + if paddingValue != "" { + rawURL := req.URL.String() + sep := "?" + if strings.Contains(rawURL, "?") { + sep = "&" + } + req.Header.Set("Referer", rawURL+sep+"x_padding="+paddingValue) + } + + c.ApplyMetaToRequest(req, sessionID, seqStr) + return nil +} + +func (c *Config) FillDownloadRequest(req *http.Request, sessionID string) error { + req.Header = c.RequestHeader() + + paddingValue, err := c.RandomPadding() + if err != nil { + return err + } + if paddingValue != "" { + rawURL := req.URL.String() + sep := "?" + if strings.Contains(rawURL, "?") { + sep = "&" + } + req.Header.Set("Referer", rawURL+sep+"x_padding="+paddingValue) + } + + c.ApplyMetaToRequest(req, sessionID, "") + return nil +} diff --git a/mihomo/transport/xhttp/conn.go b/mihomo/transport/xhttp/conn.go new file mode 100644 index 0000000000..8816a0d037 --- /dev/null +++ b/mihomo/transport/xhttp/conn.go @@ -0,0 +1,64 @@ +package xhttp + +import ( + "io" + "time" + + "github.com/metacubex/mihomo/transport/gun" +) + +type Conn struct { + writer io.WriteCloser + reader io.ReadCloser + onClose func() + gun.NetAddr + + // deadlines + deadline *time.Timer +} + +func (c *Conn) Write(b []byte) (int, error) { + return c.writer.Write(b) +} + +func (c *Conn) Read(b []byte) (int, error) { + return c.reader.Read(b) +} + +func (c *Conn) Close() error { + if c.onClose != nil { + c.onClose() + } + + err := c.writer.Close() + err2 := c.reader.Close() + if err != nil { + return err + } + if err2 != nil { + return err2 + } + return nil +} + +func (c *Conn) SetReadDeadline(t time.Time) error { return c.SetDeadline(t) } +func (c *Conn) SetWriteDeadline(t time.Time) error { return c.SetDeadline(t) } + +func (c *Conn) SetDeadline(t time.Time) error { + if t.IsZero() { + if c.deadline != nil { + c.deadline.Stop() + c.deadline = nil + } + return nil + } + d := time.Until(t) + if c.deadline != nil { + c.deadline.Reset(d) + return nil + } + c.deadline = time.AfterFunc(d, func() { + c.Close() + }) + return nil +} diff --git a/mihomo/transport/xhttp/server.go b/mihomo/transport/xhttp/server.go new file mode 100644 index 0000000000..317b131df3 --- /dev/null +++ b/mihomo/transport/xhttp/server.go @@ -0,0 +1,306 @@ +package xhttp + +import ( + "io" + "net" + "strconv" + "strings" + "sync" + "time" + + N "github.com/metacubex/mihomo/common/net" + + "github.com/metacubex/http" + "github.com/metacubex/http/h2c" +) + +type ServerOption struct { + Path string + Host string + Mode string + ConnHandler func(net.Conn) + HttpHandler http.Handler +} + +type httpServerConn struct { + mu sync.Mutex + w http.ResponseWriter + flusher http.Flusher + reader io.Reader + closed bool + done chan struct{} + once sync.Once +} + +func newHTTPServerConn(w http.ResponseWriter, r io.Reader) *httpServerConn { + flusher, _ := w.(http.Flusher) + return &httpServerConn{ + w: w, + flusher: flusher, + reader: r, + done: make(chan struct{}), + } +} + +func (c *httpServerConn) Read(b []byte) (int, error) { + return c.reader.Read(b) +} + +func (c *httpServerConn) Write(b []byte) (int, error) { + c.mu.Lock() + defer c.mu.Unlock() + + if c.closed { + return 0, io.ErrClosedPipe + } + + n, err := c.w.Write(b) + if err == nil && c.flusher != nil { + c.flusher.Flush() + } + return n, err +} + +func (c *httpServerConn) Close() error { + c.once.Do(func() { + c.mu.Lock() + c.closed = true + c.mu.Unlock() + close(c.done) + }) + return nil +} + +func (c *httpServerConn) Wait() <-chan struct{} { + return c.done +} + +type httpSession struct { + uploadQueue *uploadQueue + connected chan struct{} + once sync.Once +} + +func newHTTPSession() *httpSession { + return &httpSession{ + uploadQueue: NewUploadQueue(), + connected: make(chan struct{}), + } +} + +func (s *httpSession) markConnected() { + s.once.Do(func() { + close(s.connected) + }) +} + +type requestHandler struct { + path string + host string + mode string + connHandler func(net.Conn) + httpHandler http.Handler + + mu sync.Mutex + sessions map[string]*httpSession +} + +func NewServerHandler(opt ServerOption) http.Handler { + path := opt.Path + if path == "" { + path = "/" + } + if !strings.HasPrefix(path, "/") { + path = "/" + path + } + if !strings.HasSuffix(path, "/") { + path += "/" + } + + // using h2c.NewHandler to ensure we can work in plain http2 + // and some tls conn is not *tls.Conn (like *reality.Conn) + return h2c.NewHandler(&requestHandler{ + path: path, + host: opt.Host, + mode: opt.Mode, + connHandler: opt.ConnHandler, + httpHandler: opt.HttpHandler, + sessions: map[string]*httpSession{}, + }, &http.Http2Server{ + IdleTimeout: 30 * time.Second, + }) +} + +func (h *requestHandler) getOrCreateSession(sessionID string) *httpSession { + h.mu.Lock() + defer h.mu.Unlock() + + s, ok := h.sessions[sessionID] + if ok { + return s + } + + s = newHTTPSession() + h.sessions[sessionID] = s + return s +} + +func (h *requestHandler) deleteSession(sessionID string) { + h.mu.Lock() + defer h.mu.Unlock() + + if s, ok := h.sessions[sessionID]; ok { + _ = s.uploadQueue.Close() + delete(h.sessions, sessionID) + } +} + +func (h *requestHandler) getSession(sessionID string) *httpSession { + h.mu.Lock() + defer h.mu.Unlock() + return h.sessions[sessionID] +} + +func (h *requestHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if h.httpHandler != nil && !strings.HasPrefix(r.URL.Path, h.path) { + h.httpHandler.ServeHTTP(w, r) + return + } + + if h.host != "" && !equalHost(r.Host, h.host) { + http.NotFound(w, r) + return + } + + if !strings.HasPrefix(r.URL.Path, h.path) { + http.NotFound(w, r) + return + } + + rest := strings.TrimPrefix(r.URL.Path, h.path) + parts := splitNonEmpty(rest) + + // stream-one: POST /path + if r.Method == http.MethodPost && len(parts) == 0 { + w.Header().Set("X-Accel-Buffering", "no") + w.Header().Set("Cache-Control", "no-store") + w.WriteHeader(http.StatusOK) + if flusher, ok := w.(http.Flusher); ok { + flusher.Flush() + } + + httpSC := newHTTPServerConn(w, r.Body) + conn := &Conn{ + writer: httpSC, + reader: httpSC, + } + conn.SetAddrFromRequest(r) + + go h.connHandler(N.NewDeadlineConn(conn)) + + select { + case <-r.Context().Done(): + case <-httpSC.Wait(): + } + + _ = conn.Close() + return + } + + // packet-up download: GET /path/{session} + if r.Method == http.MethodGet && len(parts) == 1 { + sessionID := parts[0] + session := h.getOrCreateSession(sessionID) + session.markConnected() + + w.Header().Set("X-Accel-Buffering", "no") + w.Header().Set("Cache-Control", "no-store") + w.WriteHeader(http.StatusOK) + if flusher, ok := w.(http.Flusher); ok { + flusher.Flush() + } + + httpSC := newHTTPServerConn(w, r.Body) + conn := &Conn{ + writer: httpSC, + reader: session.uploadQueue, + onClose: func() { + h.deleteSession(sessionID) + }, + } + conn.SetAddrFromRequest(r) + + go h.connHandler(N.NewDeadlineConn(conn)) + + select { + case <-r.Context().Done(): + case <-httpSC.Wait(): + } + + _ = conn.Close() + return + } + + // packet-up upload: POST /path/{session}/{seq} + if r.Method == http.MethodPost && len(parts) == 2 { + sessionID := parts[0] + seq, err := strconv.ParseUint(parts[1], 10, 64) + if err != nil { + http.Error(w, "invalid xhttp seq", http.StatusBadRequest) + return + } + + session := h.getSession(sessionID) + if session == nil { + http.Error(w, "unknown xhttp session", http.StatusBadRequest) + return + } + + body, err := io.ReadAll(r.Body) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + if err := session.uploadQueue.Push(Packet{ + Seq: seq, + Payload: body, + }); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + if len(body) == 0 { + w.Header().Set("Cache-Control", "no-store") + } + w.WriteHeader(http.StatusOK) + return + } + + http.NotFound(w, r) +} + +func splitNonEmpty(s string) []string { + raw := strings.Split(s, "/") + out := make([]string, 0, len(raw)) + for _, v := range raw { + if v != "" { + out = append(out, v) + } + } + return out +} + +func equalHost(a, b string) bool { + a = strings.ToLower(a) + b = strings.ToLower(b) + + if ah, _, err := net.SplitHostPort(a); err == nil { + a = ah + } + if bh, _, err := net.SplitHostPort(b); err == nil { + b = bh + } + + return a == b +} diff --git a/mihomo/transport/xhttp/upload_queue.go b/mihomo/transport/xhttp/upload_queue.go new file mode 100644 index 0000000000..bb9cbc4927 --- /dev/null +++ b/mihomo/transport/xhttp/upload_queue.go @@ -0,0 +1,78 @@ +package xhttp + +import ( + "io" + "sync" +) + +type Packet struct { + Seq uint64 + Payload []byte +} + +type uploadQueue struct { + mu sync.Mutex + cond *sync.Cond + packets map[uint64][]byte + nextSeq uint64 + buf []byte + closed bool +} + +func NewUploadQueue() *uploadQueue { + q := &uploadQueue{ + packets: make(map[uint64][]byte), + } + q.cond = sync.NewCond(&q.mu) + return q +} + +func (q *uploadQueue) Push(p Packet) error { + q.mu.Lock() + defer q.mu.Unlock() + + if q.closed { + return io.ErrClosedPipe + } + + cp := make([]byte, len(p.Payload)) + copy(cp, p.Payload) + q.packets[p.Seq] = cp + q.cond.Broadcast() + return nil +} + +func (q *uploadQueue) Read(b []byte) (int, error) { + q.mu.Lock() + defer q.mu.Unlock() + + for { + if len(q.buf) > 0 { + n := copy(b, q.buf) + q.buf = q.buf[n:] + return n, nil + } + + if payload, ok := q.packets[q.nextSeq]; ok { + delete(q.packets, q.nextSeq) + q.nextSeq++ + q.buf = payload + continue + } + + if q.closed { + return 0, io.EOF + } + + q.cond.Wait() + } +} + +func (q *uploadQueue) Close() error { + q.mu.Lock() + defer q.mu.Unlock() + + q.closed = true + q.cond.Broadcast() + return nil +} diff --git a/openwrt-packages/luci-theme-atmaterial_new/Makefile b/openwrt-packages/luci-theme-atmaterial_new/Makefile index 88b3cbcdbc..62a87e6954 100644 --- a/openwrt-packages/luci-theme-atmaterial_new/Makefile +++ b/openwrt-packages/luci-theme-atmaterial_new/Makefile @@ -7,9 +7,11 @@ include $(TOPDIR)/rules.mk LUCI_TITLE:=Atmaterial_new kenzo LUCI_DEPENDS:= +PKG_NAME:=luci-theme-atmaterial_new PKG_VERSION:=1.2 PKG_RELEASE:=2 include $(TOPDIR)/feeds/luci/luci.mk # call BuildPackage - OpenWrt buildroot signature +$(eval $(call BuildPackage,$(PKG_NAME))) diff --git a/openwrt-packages/luci-theme-atmaterial_new/htdocs/luci-static/atmaterial/css/style.css b/openwrt-packages/luci-theme-atmaterial_new/htdocs/luci-static/atmaterial/css/style.css index 6a353b6388..e2694b3804 100644 --- a/openwrt-packages/luci-theme-atmaterial_new/htdocs/luci-static/atmaterial/css/style.css +++ b/openwrt-packages/luci-theme-atmaterial_new/htdocs/luci-static/atmaterial/css/style.css @@ -495,6 +495,9 @@ header > .container > a[class="brand"]:after { .main > .main-left > .nav > .slide > a[data-title="Docker"]:before { content: "\e025"; } +.main > .main-left > .nav > .slide > a[data-title="iStore"]:before { + content: "\e01d"; +} .main > .main-left > .nav > .slide > a[data-title="NAS"]:before { content: "\e00b"; } @@ -506,6 +509,9 @@ header > .container > a[class="brand"]:after { .main > .main-left > .nav > .slide > a[data-title="Bandwidth Monitor"]:before { content: "\e00f"; } +.main > .main-left > .nav > .slide > a[data-title="Statistics"]:before { + content: "\e00f"; +} .main > .main-left > .nav > li > a[data-title="Logout"] { padding-left:36px; @@ -2104,3 +2110,19 @@ body.lang_pl.node-main-login .cbi-value-title { min-width: 25rem; } } + +/* luci-app-dockerman */ +#cbi-dockerd > .cbi-section > br, +#cbi-docker > .cbi-section > br { + display: none; +} + +/* luci-app-istorex (Quick Start) */ +#app #main #page .app-container_body .btn-f, +#app #main #page .app-container_body .btn-r { + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + width: 30px; +} diff --git a/openwrt-packages/luci-theme-atmaterial_new/htdocs/luci-static/atmaterial_Brown/css/style.css b/openwrt-packages/luci-theme-atmaterial_new/htdocs/luci-static/atmaterial_Brown/css/style.css index 16649b065a..ad69147a6f 100644 --- a/openwrt-packages/luci-theme-atmaterial_new/htdocs/luci-static/atmaterial_Brown/css/style.css +++ b/openwrt-packages/luci-theme-atmaterial_new/htdocs/luci-static/atmaterial_Brown/css/style.css @@ -502,6 +502,9 @@ color: #8965e0!important; content: "\e02c"; color: #00b2ee!important; } +.main > .main-left > .nav > .slide > a[data-title="iStore"]:before { + content: "\e01d"; +} .main > .main-left > .nav > .slide > a[data-title="NAS"]:before { content: "\e00b"; color: #f3a4b5!important; @@ -521,6 +524,9 @@ color: #8965e0!important; content: "\e00f"; color: #2dce89!important; } +.main > .main-left > .nav > .slide > a[data-title="Statistics"]:before { + content: "\e00f"; +} .main > .main-left > .nav > li > a[data-title="Logout"] { padding-left:36px; @@ -2119,3 +2125,19 @@ body.lang_pl.node-main-login .cbi-value-title { min-width: 25rem; } } + +/* luci-app-dockerman */ +#cbi-dockerd > .cbi-section > br, +#cbi-docker > .cbi-section > br { + display: none; +} + +/* luci-app-istorex (Quick Start) */ +#app #main #page .app-container_body .btn-f, +#app #main #page .app-container_body .btn-r { + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + width: 30px; +} diff --git a/openwrt-packages/luci-theme-atmaterial_new/htdocs/luci-static/atmaterial_red/css/style.css b/openwrt-packages/luci-theme-atmaterial_new/htdocs/luci-static/atmaterial_red/css/style.css index e2eb6183f8..7b4e17dde2 100644 --- a/openwrt-packages/luci-theme-atmaterial_new/htdocs/luci-static/atmaterial_red/css/style.css +++ b/openwrt-packages/luci-theme-atmaterial_new/htdocs/luci-static/atmaterial_red/css/style.css @@ -495,6 +495,9 @@ header > .container > a[class="brand"]:after { .main > .main-left > .nav > .slide > a[data-title="Docker"]:before { content: "\e027"; } +.main > .main-left > .nav > .slide > a[data-title="iStore"]:before { + content: "\e01d"; +} .main > .main-left > .nav > .slide > a[data-title="NAS"]:before { content: "\e00b"; } @@ -506,6 +509,9 @@ header > .container > a[class="brand"]:after { .main > .main-left > .nav > .slide > a[data-title="Bandwidth Monitor"]:before { content: "\e00f"; } +.main > .main-left > .nav > .slide > a[data-title="Statistics"]:before { + content: "\e00f"; +} .main > .main-left > .nav > li > a[data-title="Logout"] { padding-left:36px; @@ -2104,3 +2110,35 @@ body.lang_pl.node-main-login .cbi-value-title { min-width: 25rem; } } + +/* luci-app-dockerman */ +#cbi-dockerd > .cbi-section > br, +#cbi-docker > .cbi-section > br { + display: none; +} + +/* luci-app-istorex (Quick Start) */ +#app #main #page .app-container_body .btn-f, +#app #main #page .app-container_body .btn-r { + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + width: 30px; +} + +/* luci-app-dockerman */ +#cbi-dockerd > .cbi-section > br, +#cbi-docker > .cbi-section > br { + display: none; +} + +/* luci-app-istorex (Quick Start) */ +#app #main #page .app-container_body .btn-f, +#app #main #page .app-container_body .btn-r { + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + width: 30px; +} diff --git a/openwrt-passwall/luci-app-passwall/luasrc/controller/passwall.lua b/openwrt-passwall/luci-app-passwall/luasrc/controller/passwall.lua index c5be01c3ed..8121d45c8d 100644 --- a/openwrt-passwall/luci-app-passwall/luasrc/controller/passwall.lua +++ b/openwrt-passwall/luci-app-passwall/luasrc/controller/passwall.lua @@ -484,17 +484,11 @@ function copy_node() local uuid = api.gen_short_uuid() uci:section(appname, "nodes", uuid) for k, v in pairs(uci:get_all(appname, section)) do - local filter = k:find("%.") - if filter and filter == 1 then - else - xpcall(function() - uci:set(appname, uuid, k, v) - end, - function(e) - end) + if not k:match("^%.") and k ~= "group" then + if k == "remarks" then v = (v or "") .. "(1)" end + uci:set(appname, uuid, k, v) end end - uci:delete(appname, uuid, "group") uci:set(appname, uuid, "add_mode", 1) api.uci_save(uci, appname) http.redirect(api.url("node_config", uuid)) diff --git a/shadowsocks-rust/Dockerfile b/shadowsocks-rust/Dockerfile index 16ec7d895b..899aab678b 100644 --- a/shadowsocks-rust/Dockerfile +++ b/shadowsocks-rust/Dockerfile @@ -1,9 +1,11 @@ -FROM --platform=$BUILDPLATFORM rust:1.94.0-alpine3.23 AS builder +FROM --platform=$BUILDPLATFORM rust:1.94.1-alpine3.23 AS builder ARG TARGETARCH +ARG BUILDARCH RUN set -x \ - && apk add --no-cache musl-dev + && apk add --no-cache musl-dev gcc \ + && echo "Building for ${TARGETARCH} on ${BUILDARCH}" WORKDIR /root/shadowsocks-rust @@ -33,8 +35,15 @@ RUN case "$TARGETARCH" in \ && wget "https://github.com/AaronChen0/musl-cc-mirror/releases/download/2021-09-23/$MUSL-cross.tgz" \ && ( echo "$SHA512" "$MUSL-cross.tgz" | sha512sum -c ) \ && tar -xzf "$MUSL-cross.tgz" -C /root/ \ - && PATH="/root/$MUSL-cross/bin:$PATH" \ - && CC=/root/$MUSL-cross/bin/$MUSL-gcc \ + && CROSS="/root/$MUSL-cross/bin/$MUSL-gcc" \ + && if [ -x "$CROSS" ] && "$CROSS" --version >/dev/null 2>&1; then \ + CC="/root/$MUSL-cross/bin/$MUSL-gcc"; \ + PATH="/root/$MUSL-cross/bin:$PATH"; \ + echo "INFO: Using downloaded cross toolchain: $CC"; \ + else \ + CC="gcc"; \ + echo "WARN: downloaded cross toolchain is not executable on this builder, fallback to native gcc"; \ + fi \ && echo "CC=$CC" \ && rustup target add "$RUST_TARGET" \ && RUSTFLAGS="-C linker=$CC" CC=$CC cargo build --target "$RUST_TARGET" --release --features "full" \ diff --git a/small/luci-app-passwall/luasrc/controller/passwall.lua b/small/luci-app-passwall/luasrc/controller/passwall.lua index c5be01c3ed..8121d45c8d 100644 --- a/small/luci-app-passwall/luasrc/controller/passwall.lua +++ b/small/luci-app-passwall/luasrc/controller/passwall.lua @@ -484,17 +484,11 @@ function copy_node() local uuid = api.gen_short_uuid() uci:section(appname, "nodes", uuid) for k, v in pairs(uci:get_all(appname, section)) do - local filter = k:find("%.") - if filter and filter == 1 then - else - xpcall(function() - uci:set(appname, uuid, k, v) - end, - function(e) - end) + if not k:match("^%.") and k ~= "group" then + if k == "remarks" then v = (v or "") .. "(1)" end + uci:set(appname, uuid, k, v) end end - uci:delete(appname, uuid, "group") uci:set(appname, uuid, "add_mode", 1) api.uci_save(uci, appname) http.redirect(api.url("node_config", uuid)) diff --git a/small/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua b/small/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua index d1b575cda5..d89fa115d8 100644 --- a/small/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua +++ b/small/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua @@ -288,7 +288,7 @@ end o.cfgvalue = function(self, section) return self.map.uci:get("shadowsocksr", "@server_subscribe[0]", "xray_hy2_type") or "hysteria2" end -o.rmempty = false +o.rmempty = true o.write = function(self, section, value) -- 更新 server_subscribe 的 xray_hy2_type local old_value = self.map.uci:get("shadowsocksr", "@server_subscribe[0]", "xray_hy2_type") @@ -358,7 +358,7 @@ o.cfgvalue = function(self, section) return self.map.uci:get("shadowsocksr", "@server_subscribe[0]", "ss_type") or "ss-rust" end o:depends("type", "ss") -o.rmempty = false +o.rmempty = true o.write = function(self, section, value) -- 更新 server_subscribe 的 ss_type local old_value = self.map.uci:get("shadowsocksr", "@server_subscribe[0]", "ss_type") diff --git a/small/luci-app-ssr-plus/root/usr/share/shadowsocksr/gen_config.lua b/small/luci-app-ssr-plus/root/usr/share/shadowsocksr/gen_config.lua index a5bb7d9cc5..8f43b978b6 100755 --- a/small/luci-app-ssr-plus/root/usr/share/shadowsocksr/gen_config.lua +++ b/small/luci-app-ssr-plus/root/usr/share/shadowsocksr/gen_config.lua @@ -452,33 +452,10 @@ end hysteriaSettings = (server.v2ray_protocol == "hysteria2") and { -- hysteria2 version = 2, - auth = server.hy2_auth, - congestion = server.hy2_tcpcongestion or nil, - up = tonumber(server.uplink_capacity) and tonumber(server.uplink_capacity) .. " mbps" or nil, - down = tonumber(server.downlink_capacity) and tonumber(server.downlink_capacity) .. " mbps" or nil, - udphop = (server.flag_port_hopping == "1") and { - port = string.gsub(server.port_range, ":", "-"), - interval = (function() - local v = tonumber((server.hopinterval or "30"):match("^%d+")) - return (v and v >= 5) and v or 30 - end)() - } or nil, - initStreamReceiveWindow = (server.flag_quicparam == "1" and server.initstreamreceivewindow) and tonumber(server.initstreamreceivewindow) or nil, - maxStreamReceiveWindow = (server.flag_quicparam == "1" and server.maxstreamreceivewindow) and tonumber(server.maxstreamreceivewindow) or nil, - initConnectionReceiveWindow = (server.flag_quicparam == "1" and server.initconnreceivewindow) and tonumber(server.initconnreceivewindow) or nil, - maxConnectionReceiveWindow = (server.flag_quicparam == "1" and server.maxconnreceivewindow) and tonumber(server.maxconnreceivewindow) or nil, - maxIdleTimeout = (server.flag_quicparam == "1" and (function() - local timeoutStr = tostring(server.maxidletimeout or "") - local timeout = tonumber(timeoutStr:match("^%d+")) - if timeout and timeout >= 4 and timeout <= 120 then - return timeout - end - end)()) or 30, - keepAlivePeriod = (server.flag_quicparam == "1" and server.keepaliveperiod) and tonumber(server.keepaliveperiod) or nil, - disablePathMTUDiscovery = (server.flag_quicparam == "1" and tostring(server.disablepathmtudiscovery) == "1") and true or nil + auth = server.hy2_auth } or nil, finalmask = (function() - local finalmask + local finalmask = {} if server.transport == "kcp" then local map = {none = "none", srtp = "header-srtp", utp = "header-utp", ["wechat-video"] = "header-wechat", dtls = "header-dtls", wireguard = "header-wireguard", dns = "header-dns"} @@ -495,15 +472,39 @@ end c.settings = { password = server.seed } end udp[#udp+1] = c - finalmask = { udp = udp } - elseif (server.flag_obfs == "1" and (server.v2ray_protocol == "hysteria2" and server.obfs_type and server.obfs_type ~= "")) then - finalmask = { - udp = {{ + finalmask.udp = udp + elseif server.v2ray_protocol == "hysteria2" then + if (server.flag_obfs == "1" and (server.obfs_type and server.obfs_type ~= "")) then + finalmask.udp = {{ type = server.obfs_type, settings = server.salamander and { password = server.salamander } or nil }} + end + local up = tonumber(server.uplink_capacity) or 0 + local down = tonumber(server.downlink_capacity) or 0 + finalmask.quicParams = { + congestion = server.hy2_tcpcongestion or nil, + brutalUp = up > 0 and (up .. "mbps") or nil, + brutalDown = down > 0 and (down .. "mbps") or nil, + udpHop = (server.flag_port_hopping == "1") and { + ports = string.gsub(server.port_range, ":", "-"), + interval = (function(v) + v = tonumber((v or "30"):match("^%d+")) + return (v and v >= 5) and v or 30 + end)(server.hopinterval) + } or nil, + initStreamReceiveWindow = (server.flag_quicparam == "1" and server.initstreamreceivewindow) and tonumber(server.initstreamreceivewindow) or nil, + maxStreamReceiveWindow = (server.flag_quicparam == "1" and server.maxstreamreceivewindow) and tonumber(server.maxstreamreceivewindow) or nil, + initConnectionReceiveWindow = (server.flag_quicparam == "1" and server.initconnreceivewindow) and tonumber(server.initconnreceivewindow) or nil, + maxConnectionReceiveWindow = (server.flag_quicparam == "1" and server.maxconnreceivewindow) and tonumber(server.maxconnreceivewindow) or nil, + maxIdleTimeout = (server.flag_quicparam == "1" and (function(t) + t = tonumber(tostring(t or "30"):match("^%d+")) + return (t and t >= 4 and t <= 120) and t or 30 + end)(server.maxidletimeout) or 30), + keepAlivePeriod = (server.flag_quicparam == "1" and server.keepaliveperiod) and tonumber(server.keepaliveperiod) or nil, + disablePathMTUDiscovery = (server.flag_quicparam == "1" and tostring(server.disablepathmtudiscovery) == "1") and true or nil } end if server.finalmask and server.finalmask ~= "" then diff --git a/small/tuic-client/Makefile b/small/tuic-client/Makefile index f86406cacd..939236c5e7 100644 --- a/small/tuic-client/Makefile +++ b/small/tuic-client/Makefile @@ -5,12 +5,12 @@ include $(TOPDIR)/rules.mk PKG_NAME:=tuic-client -PKG_VERSION:=1.7.1 +PKG_VERSION:=1.7.2 PKG_RELEASE:=1 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://codeload.github.com/Itsusinn/tuic/tar.gz/v$(PKG_VERSION)? -PKG_HASH:=f07f717e75c0e2ad0a67ade71a4b396639b706da7412b808b33987b30525a51a +PKG_HASH:=7a08641826cb7e166ae6db06f367ffeeeddd17f11ee5ae23435ec5701099149a PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION) PKG_MAINTAINER:=Tianling Shen diff --git a/small/v2ray-core/Makefile b/small/v2ray-core/Makefile index 8c8ac8a5d7..9e06fbfc1f 100644 --- a/small/v2ray-core/Makefile +++ b/small/v2ray-core/Makefile @@ -5,12 +5,12 @@ include $(TOPDIR)/rules.mk PKG_NAME:=v2ray-core -PKG_VERSION:=5.47.0 +PKG_VERSION:=5.48.0 PKG_RELEASE:=1 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://codeload.github.com/v2fly/v2ray-core/tar.gz/v$(PKG_VERSION)? -PKG_HASH:=90ef85f8f2c5478fc1e4f455b40eaf35a1738e06ef519b85a62c0763e2391405 +PKG_HASH:=7900d9ec61014c1b87c149e43aa4f3b03be4bc756cbfe9a75926d5a7ac86105e PKG_LICENSE:=MIT PKG_LICENSE_FILES:=LICENSE diff --git a/small/v2ray-plugin/Makefile b/small/v2ray-plugin/Makefile index aeb0c9d4b1..cd3ba4f8c0 100644 --- a/small/v2ray-plugin/Makefile +++ b/small/v2ray-plugin/Makefile @@ -6,12 +6,12 @@ include $(TOPDIR)/rules.mk PKG_NAME:=v2ray-plugin -PKG_VERSION:=5.46.0 +PKG_VERSION:=5.48.0 PKG_RELEASE:=1 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://codeload.github.com/teddysun/v2ray-plugin/tar.gz/v$(PKG_VERSION)? -PKG_HASH:=d9d654cf0bdd5281b0c0a35b7a06e9092df7d54b985da98e7247bf3d154f7653 +PKG_HASH:=cb7d1fefc9c5f30f5dd2b966e5a2a0846857da9c239febe8c676a0e096eeed7c PKG_LICENSE:=MIT PKG_LICENSE_FILES:=LICENSE diff --git a/v2ray-core/core.go b/v2ray-core/core.go index 96da415fce..8b427f02f1 100644 --- a/v2ray-core/core.go +++ b/v2ray-core/core.go @@ -18,7 +18,7 @@ import ( ) var ( - version = "5.47.0" + version = "5.48.0" build = "Custom" codename = "V2Fly, a community-driven edition of V2Ray." intro = "A unified platform for anti-censorship." diff --git a/v2ray-core/go.mod b/v2ray-core/go.mod index 1c8783e809..953c9f47ee 100644 --- a/v2ray-core/go.mod +++ b/v2ray-core/go.mod @@ -43,7 +43,7 @@ require ( golang.org/x/sync v0.20.0 golang.org/x/sys v0.42.0 golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb - google.golang.org/grpc v1.79.2 + google.golang.org/grpc v1.79.3 google.golang.org/protobuf v1.36.11 gopkg.in/yaml.v3 v3.0.1 gvisor.dev/gvisor v0.0.0-20260218152508-eed1cebf761e diff --git a/v2ray-core/go.sum b/v2ray-core/go.sum index db6d244356..414491a64f 100644 --- a/v2ray-core/go.sum +++ b/v2ray-core/go.sum @@ -835,8 +835,8 @@ google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8 google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.79.2 h1:fRMD94s2tITpyJGtBBn7MkMseNpOZU8ZxgC3MMBaXRU= -google.golang.org/grpc v1.79.2/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= +google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE= +google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/v2rayng/.github/workflows/build.yml b/v2rayng/.github/workflows/build.yml index 6c92eecc0d..8e4d0cf345 100644 --- a/v2rayng/.github/workflows/build.yml +++ b/v2rayng/.github/workflows/build.yml @@ -22,7 +22,7 @@ jobs: fetch-depth: '0' - name: Setup Android SDK - uses: android-actions/setup-android@v3.2.0 + uses: android-actions/setup-android@v4.0.0 with: log-accepted-android-sdk-licenses: false cmdline-tools-version: '13114758' diff --git a/v2rayng/V2rayNG/app/src/main/res/layout/activity_main.xml b/v2rayng/V2rayNG/app/src/main/res/layout/activity_main.xml index 8764821354..54716e7156 100644 --- a/v2rayng/V2rayNG/app/src/main/res/layout/activity_main.xml +++ b/v2rayng/V2rayNG/app/src/main/res/layout/activity_main.xml @@ -94,6 +94,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|end" + android:layout_marginBottom="-16dp" android:layout_marginEnd="@dimen/padding_spacing_dp16"> diff --git a/xray-core/common/log/logger.go b/xray-core/common/log/logger.go index 7c100aa19f..538eda2d03 100644 --- a/xray-core/common/log/logger.go +++ b/xray-core/common/log/logger.go @@ -36,7 +36,7 @@ type serverityLogger struct { func NewLogger(logWriterCreator WriterCreator) Handler { return &generalLogger{ creator: logWriterCreator, - buffer: make(chan Message, 16), + buffer: make(chan Message, 128), access: semaphore.New(1), done: done.New(), } @@ -46,7 +46,7 @@ func ReplaceWithSeverityLogger(serverity Severity) { w := CreateStdoutLogWriter() g := &generalLogger{ creator: w, - buffer: make(chan Message, 16), + buffer: make(chan Message, 128), access: semaphore.New(1), done: done.New(), } diff --git a/xray-core/core/core.go b/xray-core/core/core.go index b48a6c735b..c5a8130fa4 100644 --- a/xray-core/core/core.go +++ b/xray-core/core/core.go @@ -20,7 +20,7 @@ import ( var ( Version_x byte = 26 Version_y byte = 3 - Version_z byte = 23 + Version_z byte = 27 ) var ( diff --git a/xray-core/infra/conf/transport_internet.go b/xray-core/infra/conf/transport_internet.go index 7a302aecce..849f0e38ee 100644 --- a/xray-core/infra/conf/transport_internet.go +++ b/xray-core/infra/conf/transport_internet.go @@ -1425,10 +1425,11 @@ func (c *FragmentMask) Build() (proto.Message, error) { } type NoiseItem struct { - Rand Int32Range `json:"rand"` - Type string `json:"type"` - Packet json.RawMessage `json:"packet"` - Delay Int32Range `json:"delay"` + Rand Int32Range `json:"rand"` + RandRange *Int32Range `json:"randRange"` + Type string `json:"type"` + Packet json.RawMessage `json:"packet"` + Delay Int32Range `json:"delay"` } type NoiseMask struct { @@ -1445,16 +1446,24 @@ func (c *NoiseMask) Build() (proto.Message, error) { noiseSlice := make([]*noise.Item, 0, len(c.Noise)) for _, item := range c.Noise { + if item.RandRange == nil { + item.RandRange = &Int32Range{From: 0, To: 255} + } + if item.RandRange.From < 0 || item.RandRange.To > 255 { + return nil, errors.New("invalid randRange") + } var err error if item.Packet, err = PraseByteSlice(item.Packet, item.Type); err != nil { return nil, err } noiseSlice = append(noiseSlice, &noise.Item{ - RandMin: int64(item.Rand.From), - RandMax: int64(item.Rand.To), - Packet: item.Packet, - DelayMin: int64(item.Delay.From), - DelayMax: int64(item.Delay.To), + RandMin: int64(item.Rand.From), + RandMax: int64(item.Rand.To), + RandRangeMin: item.RandRange.From, + RandRangeMax: item.RandRange.To, + Packet: item.Packet, + DelayMin: int64(item.Delay.From), + DelayMax: int64(item.Delay.To), }) } diff --git a/xray-core/proxy/wireguard/bind.go b/xray-core/proxy/wireguard/bind.go index 8e7ecb04dc..f4ec6f2542 100644 --- a/xray-core/proxy/wireguard/bind.go +++ b/xray-core/proxy/wireguard/bind.go @@ -2,27 +2,23 @@ package wireguard import ( "context" - "errors" + gonet "net" "net/netip" + "runtime" "strconv" - "sync" "golang.zx2c4.com/wireguard/conn" + "golang.zx2c4.com/wireguard/device" + "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/features/dns" "github.com/xtls/xray-core/transport/internet" ) type netReadInfo struct { - // status - waiter sync.WaitGroup - // param - buff []byte - // result - bytes int + buff []byte endpoint conn.Endpoint - err error } // reduce duplicated code @@ -32,6 +28,7 @@ type netBind struct { workers int readQueue chan *netReadInfo + closedCh chan struct{} } // SetMark implements conn.Bind @@ -79,27 +76,23 @@ func (bind *netBind) BatchSize() int { // Open implements conn.Bind func (bind *netBind) Open(uport uint16) ([]conn.ReceiveFunc, uint16, error) { - bind.readQueue = make(chan *netReadInfo) + bind.closedCh = make(chan struct{}) + errors.LogDebug(context.Background(), "bind opened") fun := func(bufs [][]byte, sizes []int, eps []conn.Endpoint) (n int, err error) { - defer func() { - if r := recover(); r != nil { - n = 0 - err = errors.New("channel closed") - } - }() - - r, ok := <-bind.readQueue - if !ok { - return 0, errors.New("channel closed") + select { + case r := <-bind.readQueue: + sizes[0], eps[0] = copy(bufs[0], r.buff), r.endpoint + return 1, nil + case <-bind.closedCh: + errors.LogDebug(context.Background(), "recv func closed") + return 0, gonet.ErrClosed } - - copy(bufs[0], r.buff[:r.bytes]) - sizes[0], eps[0] = r.bytes, r.endpoint - r.waiter.Done() - return 1, r.err } workers := bind.workers + if workers <= 0 { + workers = runtime.NumCPU() + } if workers <= 0 { workers = 1 } @@ -113,8 +106,9 @@ func (bind *netBind) Open(uport uint16) ([]conn.ReceiveFunc, uint16, error) { // Close implements conn.Bind func (bind *netBind) Close() error { - if bind.readQueue != nil { - close(bind.readQueue) + errors.LogDebug(context.Background(), "bind closed") + if bind.closedCh != nil { + close(bind.closedCh) } return nil } @@ -134,35 +128,35 @@ func (bind *netBindClient) connectTo(endpoint *netEndpoint) error { } endpoint.conn = c - go func(readQueue chan<- *netReadInfo, endpoint *netEndpoint) { - defer func() { - _ = recover() // handle send on closed channel - }() + go func() { for { - buff := make([]byte, 1700) - i, err := c.Read(buff) + buff := make([]byte, device.MaxMessageSize) + n, err := c.Read(buff) - if i > 3 { + if err != nil { + endpoint.conn = nil + c.Close() + return + } + + if n > 3 { buff[1] = 0 buff[2] = 0 buff[3] = 0 } - r := &netReadInfo{ - buff: buff, - bytes: i, + select { + case bind.readQueue <- &netReadInfo{ + buff: buff[:n], endpoint: endpoint, - err: err, - } - r.waiter.Add(1) - readQueue <- r - r.waiter.Wait() - if err != nil { + }: + case <-bind.closedCh: endpoint.conn = nil + c.Close() return } } - }(bind.readQueue, endpoint) + }() return nil } @@ -206,7 +200,8 @@ func (bind *netBindServer) Send(buff [][]byte, endpoint conn.Endpoint) error { } if nend.conn == nil { - return errors.New("connection not open yet") + errors.LogDebug(context.Background(), nend.dst.NetAddr(), " send on closed peer") + return errors.New("peer closed") } for _, buff := range buff { diff --git a/xray-core/proxy/wireguard/client.go b/xray-core/proxy/wireguard/client.go index efe833a13f..3030ac78c3 100644 --- a/xray-core/proxy/wireguard/client.go +++ b/xray-core/proxy/wireguard/client.go @@ -121,7 +121,8 @@ func (h *Handler) processWireGuard(ctx context.Context, dialer internet.Dialer) IPv4Enable: h.hasIPv4, IPv6Enable: h.hasIPv6, }, - workers: int(h.conf.NumWorkers), + workers: int(h.conf.NumWorkers), + readQueue: make(chan *netReadInfo), }, ctx: ctx, dialer: dialer, diff --git a/xray-core/proxy/wireguard/server.go b/xray-core/proxy/wireguard/server.go index ce18be2298..9e82ed39d7 100644 --- a/xray-core/proxy/wireguard/server.go +++ b/xray-core/proxy/wireguard/server.go @@ -2,8 +2,6 @@ package wireguard import ( "context" - goerrors "errors" - "io" "github.com/xtls/xray-core/common/buf" c "github.com/xtls/xray-core/common/ctx" @@ -51,6 +49,8 @@ func NewServer(ctx context.Context, conf *DeviceConfig) (*Server, error) { IPv4Enable: hasIPv4, IPv6Enable: hasIPv6, }, + workers: int(conf.NumWorkers), + readQueue: make(chan *netReadInfo), }, }, policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager), @@ -93,25 +93,31 @@ func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Con reader := buf.NewPacketReader(conn) for { - mpayload, err := reader.ReadMultiBuffer() + mb, err := reader.ReadMultiBuffer() if err != nil { + nep.conn = nil + buf.ReleaseMulti(mb) return err } - for _, payload := range mpayload { - v, ok := <-s.bindServer.readQueue - if !ok { - return nil - } - i, err := payload.Read(v.buff) + for i, b := range mb { + buff := b.Bytes() - v.bytes = i - v.endpoint = nep - v.err = err - v.waiter.Done() - if err != nil && goerrors.Is(err, io.EOF) { + if b.Len() > 3 { + buff[1] = 0 + buff[2] = 0 + buff[3] = 0 + } + + select { + case s.bindServer.readQueue <- &netReadInfo{ + buff: buff, + endpoint: nep, + }: + case <-s.bindServer.closedCh: nep.conn = nil - return nil + buf.ReleaseMulti(mb[i:]) + return errors.New("bind closed") } } } @@ -138,9 +144,11 @@ func (s *Server) forwardConnection(dest net.Destination, conn net.Conn) { // Currently we have no way to link to the original source address inbound.Source = net.DestinationFromAddr(conn.RemoteAddr()) ctx = session.ContextWithInbound(ctx, &inbound) + content := new(session.Content) if s.info.contentTag != nil { - ctx = session.ContextWithContent(ctx, s.info.contentTag) + content.SniffingRequest = s.info.contentTag.SniffingRequest } + ctx = session.ContextWithContent(ctx, content) ctx = session.SubContextFromMuxInbound(ctx) ctx = log.ContextWithAccessMessage(ctx, &log.AccessMessage{ diff --git a/xray-core/proxy/wireguard/wireguard.go b/xray-core/proxy/wireguard/wireguard.go index 0d75ee0011..4f489114f5 100644 --- a/xray-core/proxy/wireguard/wireguard.go +++ b/xray-core/proxy/wireguard/wireguard.go @@ -8,25 +8,8 @@ import ( "strings" "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/log" - "golang.zx2c4.com/wireguard/device" ) -var wgLogger = &device.Logger{ - Verbosef: func(format string, args ...any) { - log.Record(&log.GeneralMessage{ - Severity: log.Severity_Debug, - Content: fmt.Sprintf(format, args...), - }) - }, - Errorf: func(format string, args ...any) { - log.Record(&log.GeneralMessage{ - Severity: log.Severity_Error, - Content: fmt.Sprintf(format, args...), - }) - }, -} - func init() { common.Must(common.RegisterConfig((*DeviceConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { deviceConfig := config.(*DeviceConfig) diff --git a/xray-core/transport/internet/finalmask/noise/config.pb.go b/xray-core/transport/internet/finalmask/noise/config.pb.go index 16e1b25328..71ba461a6b 100644 --- a/xray-core/transport/internet/finalmask/noise/config.pb.go +++ b/xray-core/transport/internet/finalmask/noise/config.pb.go @@ -25,9 +25,11 @@ type Item struct { state protoimpl.MessageState `protogen:"open.v1"` RandMin int64 `protobuf:"varint,1,opt,name=rand_min,json=randMin,proto3" json:"rand_min,omitempty"` RandMax int64 `protobuf:"varint,2,opt,name=rand_max,json=randMax,proto3" json:"rand_max,omitempty"` - Packet []byte `protobuf:"bytes,3,opt,name=packet,proto3" json:"packet,omitempty"` - DelayMin int64 `protobuf:"varint,4,opt,name=delay_min,json=delayMin,proto3" json:"delay_min,omitempty"` - DelayMax int64 `protobuf:"varint,5,opt,name=delay_max,json=delayMax,proto3" json:"delay_max,omitempty"` + RandRangeMin int32 `protobuf:"varint,3,opt,name=rand_range_min,json=randRangeMin,proto3" json:"rand_range_min,omitempty"` + RandRangeMax int32 `protobuf:"varint,4,opt,name=rand_range_max,json=randRangeMax,proto3" json:"rand_range_max,omitempty"` + Packet []byte `protobuf:"bytes,5,opt,name=packet,proto3" json:"packet,omitempty"` + DelayMin int64 `protobuf:"varint,6,opt,name=delay_min,json=delayMin,proto3" json:"delay_min,omitempty"` + DelayMax int64 `protobuf:"varint,7,opt,name=delay_max,json=delayMax,proto3" json:"delay_max,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -76,6 +78,20 @@ func (x *Item) GetRandMax() int64 { return 0 } +func (x *Item) GetRandRangeMin() int32 { + if x != nil { + return x.RandRangeMin + } + return 0 +} + +func (x *Item) GetRandRangeMax() int32 { + if x != nil { + return x.RandRangeMax + } + return 0 +} + func (x *Item) GetPacket() []byte { if x != nil { return x.Packet @@ -161,13 +177,15 @@ var File_transport_internet_finalmask_noise_config_proto protoreflect.FileDescri const file_transport_internet_finalmask_noise_config_proto_rawDesc = "" + "\n" + - "/transport/internet/finalmask/noise/config.proto\x12'xray.transport.internet.finalmask.noise\"\x8e\x01\n" + + "/transport/internet/finalmask/noise/config.proto\x12'xray.transport.internet.finalmask.noise\"\xda\x01\n" + "\x04Item\x12\x19\n" + "\brand_min\x18\x01 \x01(\x03R\arandMin\x12\x19\n" + - "\brand_max\x18\x02 \x01(\x03R\arandMax\x12\x16\n" + - "\x06packet\x18\x03 \x01(\fR\x06packet\x12\x1b\n" + - "\tdelay_min\x18\x04 \x01(\x03R\bdelayMin\x12\x1b\n" + - "\tdelay_max\x18\x05 \x01(\x03R\bdelayMax\"\x87\x01\n" + + "\brand_max\x18\x02 \x01(\x03R\arandMax\x12$\n" + + "\x0erand_range_min\x18\x03 \x01(\x05R\frandRangeMin\x12$\n" + + "\x0erand_range_max\x18\x04 \x01(\x05R\frandRangeMax\x12\x16\n" + + "\x06packet\x18\x05 \x01(\fR\x06packet\x12\x1b\n" + + "\tdelay_min\x18\x06 \x01(\x03R\bdelayMin\x12\x1b\n" + + "\tdelay_max\x18\a \x01(\x03R\bdelayMax\"\x87\x01\n" + "\x06Config\x12\x1b\n" + "\treset_min\x18\x01 \x01(\x03R\bresetMin\x12\x1b\n" + "\treset_max\x18\x02 \x01(\x03R\bresetMax\x12C\n" + diff --git a/xray-core/transport/internet/finalmask/noise/config.proto b/xray-core/transport/internet/finalmask/noise/config.proto index 552603faf2..d874b973f4 100644 --- a/xray-core/transport/internet/finalmask/noise/config.proto +++ b/xray-core/transport/internet/finalmask/noise/config.proto @@ -9,9 +9,11 @@ option java_multiple_files = true; message Item { int64 rand_min = 1; int64 rand_max = 2; - bytes packet = 3; - int64 delay_min = 4; - int64 delay_max = 5; + int32 rand_range_min = 3; + int32 rand_range_max = 4; + bytes packet = 5; + int64 delay_min = 6; + int64 delay_max = 7; } message Config { diff --git a/xray-core/transport/internet/finalmask/noise/conn.go b/xray-core/transport/internet/finalmask/noise/conn.go index 022fb21bcb..c3ab89ab4d 100644 --- a/xray-core/transport/internet/finalmask/noise/conn.go +++ b/xray-core/transport/internet/finalmask/noise/conn.go @@ -1,12 +1,10 @@ package noise import ( - "crypto/rand" "net" "sync" "time" - "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/crypto" ) @@ -77,7 +75,7 @@ func (c *noiseConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { for _, item := range c.config.Items { if item.RandMax > 0 { item.Packet = make([]byte, crypto.RandBetween(item.RandMin, item.RandMax)) - common.Must2(rand.Read(item.Packet)) + crypto.RandBytesBetween(item.Packet, byte(item.RandRangeMin), byte(item.RandRangeMax)) } c.PacketConn.WriteTo(item.Packet, addr) time.Sleep(time.Duration(crypto.RandBetween(item.DelayMin, item.DelayMax)) * time.Millisecond)