Update On Fri Mar 27 20:08:13 CET 2026

This commit is contained in:
github-action[bot]
2026-03-27 20:08:14 +01:00
parent 766fee8b2d
commit 57d938b548
120 changed files with 6262 additions and 8358 deletions
+1
View File
@@ -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
@@ -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
-182
View File
@@ -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)
-645
View File
@@ -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)
}
-643
View File
@@ -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)
}
-657
View File
@@ -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)
}
-884
View File
@@ -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 {
-883
View File
@@ -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 {
+20 -94
View File
@@ -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
+9 -9
View File
@@ -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: |
+93 -6
View File
@@ -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 {
@@ -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)
}
@@ -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}
}
@@ -0,0 +1,9 @@
//go:build go1.21
package contextutils
import "context"
func WithoutCancel(parent context.Context) context.Context {
return context.WithoutCancel(parent)
}
+7
View File
@@ -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)
+7 -7
View File
@@ -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,
}
+7 -2
View File
@@ -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)
}
})
}
})
+16
View File
@@ -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,
+51
View File
@@ -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)
}
+23 -1
View File
@@ -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
+293
View File
@@ -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[:])
}
+209
View File
@@ -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
}
+64
View File
@@ -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
}
+306
View File
@@ -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
}
@@ -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
}
+5 -1
View File
@@ -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"
+3 -3
View File
@@ -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",
@@ -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<bool>,
/// Use legacy UI (original UI at "/" route)
/// When true, opens legacy window; when false, opens new main window
pub use_legacy_ui: Option<bool>,
/// Window type to use when opening the app window
/// Legacy: opens legacy window; Main: opens new main window
pub window_type: Option<WindowType>,
}
#[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()
}
}
+5 -5
View File
@@ -100,11 +100,11 @@ pub fn is_portable() -> Result<bool> {
Ok(false)
}
#[tauri::command]
#[specta::specta]
pub fn get_device_info() -> Result<crate::utils::hwid::DeviceInfo> {
Ok(crate::utils::hwid::get_device_info())
}
// #[tauri::command]
// #[specta::specta]
// pub fn get_device_info() -> Result<crate::utils::hwid::DeviceInfo> {
// Ok(crate::utils::hwid::get_device_info())
// }
#[tauri::command]
#[specta::specta]
+1 -1
View File
@@ -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,
@@ -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),
}
}
@@ -1 +1,2 @@
export * from './use-core-status'
export * from './use-kv-storage'
@@ -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
}
@@ -75,6 +75,11 @@ export function useKvStorage<T>(
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<Set<string>>(new Set())
const applyMigrate = useCallback((raw: unknown): T => {
return migrateRef.current ? migrateRef.current(raw) : (raw as T)
}, [])
@@ -111,11 +116,31 @@ export function useKvStorage<T>(
}
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<T>(
? (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)
@@ -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__<T> = {
listen: (
@@ -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
*/
@@ -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",
@@ -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": "Только для чтения",
@@ -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": "只读",
@@ -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": "只讀",
@@ -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"
@@ -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()
})
@@ -19,7 +19,7 @@ const ProxyButton = ({
return (
<Button
className={cn(
'group h-16 rounded-3xl font-bold',
'group h-16 rounded-3xl font-bold text-nowrap',
'flex items-center justify-between gap-2',
'data-[active=false]:bg-white dark:data-[active=false]:bg-black',
className,
@@ -0,0 +1,42 @@
import { createContext, useContext, type RefObject } from 'react'
import type {
DndGridItemType,
GridItemConstraints,
ItemRect,
ResizeHandle,
} from './types'
const DndGridContext = createContext<{
displayItems: DndGridItemType[]
getItemRect: (item: DndGridItemType) => ItemRect
dropInfoMap: Record<string, { left: number; top: number }>
activeItemId: string | null
resizingItemId: string | null
disabled: boolean
sourceOnly: boolean
dragIdPrefix: string
isOverlay: boolean
constraintsMapRef: RefObject<Record<string, GridItemConstraints>> & {
current: Record<string, GridItemConstraints>
}
onResizeStart: (
id: string,
handle: ResizeHandle,
startX: number,
startY: number,
) => void
onResizeMove: (currentX: number, currentY: number) => void
onResizeEnd: () => void
} | null>(null)
export const DndGridProvider = DndGridContext.Provider
export function useDndGridContext() {
const ctx = useContext(DndGridContext)
if (!ctx) {
throw new Error('DndGridItem must be used inside DndGrid')
}
return ctx
}
@@ -0,0 +1,258 @@
import {
AnimatePresence,
motion,
useSpring,
type Transition,
} from 'framer-motion'
import { PropsWithChildren, useLayoutEffect, useRef } from 'react'
import { useDraggable } from '@dnd-kit/core'
import { cn } from '@nyanpasu/ui'
import { useDndGridContext } from './context'
import type { GridItemConstraints } from './types'
const SPRING_OPTIONS = {
stiffness: 350,
damping: 35,
} as Transition
const RESIZE_SPRING = {
type: 'spring',
stiffness: 400,
damping: 35,
} as Transition
const INSTANT = {
duration: 0,
} as Transition
export type DndGridItemProps<T extends string> = PropsWithChildren<{
id: T
className?: string
}> &
GridItemConstraints
function ResizeKnob({
onStart,
onMove,
onEnd,
}: {
onStart: (x: number, y: number) => void
onMove: (x: number, y: number) => void
onEnd: () => void
}) {
return (
<motion.div
className={cn(
'absolute -right-0.75 -bottom-0.75 z-20 flex size-7 items-center justify-center',
'text-on-surface',
'touch-none select-none',
)}
data-slot="resize-handle"
onPointerDown={(e) => {
e.preventDefault()
e.stopPropagation()
e.currentTarget.setPointerCapture(e.pointerId)
onStart(e.clientX, e.clientY)
}}
onPointerMove={(e) => {
if (!e.currentTarget.hasPointerCapture(e.pointerId)) {
return
}
onMove(e.clientX, e.clientY)
}}
onPointerUp={(e) => {
if (!e.currentTarget.hasPointerCapture(e.pointerId)) {
return
}
e.currentTarget.releasePointerCapture(e.pointerId)
onEnd()
}}
onPointerCancel={(e) => {
if (!e.currentTarget.hasPointerCapture(e.pointerId)) {
return
}
e.currentTarget.releasePointerCapture(e.pointerId)
onEnd()
}}
initial={{
scale: 0.85,
opacity: 0,
}}
animate={{
scale: 1,
opacity: 1,
}}
exit={{
scale: 0.85,
opacity: 0,
}}
transition={{
type: 'tween',
duration: 0.1,
ease: 'easeOut',
}}
>
<svg
className="size-full cursor-se-resize"
viewBox="11 11 7 7"
fill="none"
data-slot="resize-handle-icon"
>
<path
d="M12 17.25H13A4.25 4.25 0 0 0 17.25 13V12"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
/>
</svg>
</motion.div>
)
}
function DndGridItemDraggable<T extends string>({
id,
className,
children,
minW,
minH,
maxW,
maxH,
}: DndGridItemProps<T>) {
const {
displayItems,
getItemRect,
dropInfoMap,
activeItemId,
resizingItemId,
disabled,
sourceOnly,
dragIdPrefix,
constraintsMapRef,
onResizeStart,
onResizeMove,
onResizeEnd,
} = useDndGridContext()
// Write constraints synchronously during render so they're always up-to-date
// before any resize interaction can occur.
constraintsMapRef.current[id] = { minW, minH, maxW, maxH }
const item = displayItems.find((i) => i.id === id)
// Disable drag while any item is being resized
const { attributes, listeners, setNodeRef } = useDraggable({
id: dragIdPrefix ? `${dragIdPrefix}${id}` : id,
disabled: disabled || !item || resizingItemId !== null,
data: item,
})
const springX = useSpring(0, SPRING_OPTIONS)
const springY = useSpring(0, SPRING_OPTIONS)
const dropInfo = dropInfoMap[id]
const prevDropInfoRef = useRef<typeof dropInfo>(undefined)
useLayoutEffect(() => {
if (!dropInfo || dropInfo === prevDropInfoRef.current || !item) {
return
}
prevDropInfoRef.current = dropInfo
const rect = getItemRect(item)
springX.jump(dropInfo.left - rect.left)
springY.jump(dropInfo.top - rect.top)
springX.set(0)
springY.set(0)
}, [dropInfo, item, getItemRect, springX, springY])
if (!item) {
return null
}
const rect = getItemRect(item)
const isActiveItem = activeItemId === id
const isResizing = resizingItemId === id
return (
<motion.div
ref={setNodeRef}
initial={false}
animate={{
left: rect.left,
top: rect.top,
width: rect.width,
height: rect.height,
}}
transition={
isResizing
? {
left: RESIZE_SPRING,
top: RESIZE_SPRING,
width: RESIZE_SPRING,
height: RESIZE_SPRING,
}
: {
left: INSTANT,
top: INSTANT,
width: INSTANT,
height: INSTANT,
}
}
className={cn('group', className)}
style={{
position: 'absolute',
touchAction: 'none',
opacity: isActiveItem ? 0 : 1,
x: springX,
y: springY,
}}
{...attributes}
{...listeners}
>
{children}
<AnimatePresence>
{!disabled && !sourceOnly && (
<ResizeKnob
onStart={(x, y) => onResizeStart(id, 'bottom-right', x, y)}
onMove={onResizeMove}
onEnd={onResizeEnd}
/>
)}
</AnimatePresence>
</motion.div>
)
}
export function DndGridItem<T extends string>({
id,
className,
children,
minW,
minH,
maxW,
maxH,
}: DndGridItemProps<T>) {
const { isOverlay } = useDndGridContext()
if (isOverlay) {
// Inside DragOverlay: skip positioning and drag logic — just fill the overlay div.
return <div className={cn('size-full', className)}>{children}</div>
}
return (
<DndGridItemDraggable
id={id}
className={className}
minW={minW}
minH={minH}
maxW={maxW}
maxH={maxH}
>
{children}
</DndGridItemDraggable>
)
}
@@ -0,0 +1,168 @@
import { useCallback, useRef, useState, type PropsWithChildren } from 'react'
import {
DndContext,
PointerSensor,
TouchSensor,
useSensor,
useSensors,
type DragEndEvent,
type DragMoveEvent,
type DragStartEvent,
} from '@dnd-kit/core'
import {
DndGridRootContext,
type ActiveDrag,
type GridRegistration,
} from './root-context'
function findGrid(
grids: Map<string, GridRegistration>,
activeId: string,
): { gridId: string; reg: GridRegistration; plainId: string } | null {
for (const [gridId, reg] of grids) {
const { itemIds, dragIdPrefix } = reg
if (dragIdPrefix) {
if (activeId.startsWith(dragIdPrefix)) {
const plain = activeId.slice(dragIdPrefix.length)
if (itemIds.includes(plain)) {
return {
gridId,
reg,
plainId: plain,
}
}
}
} else if (itemIds.includes(activeId)) {
return {
gridId,
reg,
plainId: activeId,
}
}
}
return null
}
export function DndGridRoot({ children }: PropsWithChildren) {
const gridsRef = useRef<Map<string, GridRegistration>>(new Map())
const [activeDrag, setActiveDrag] = useState<ActiveDrag | null>(null)
// Captured at drag-start so the handler survives grid unmount (e.g. sheet closing)
const pendingSourceRef = useRef<{
onSourceDrop?: (itemId: string) => void
itemId: string
} | null>(null)
const registerGrid = useCallback((gridId: string, reg: GridRegistration) => {
gridsRef.current.set(gridId, reg)
}, [])
const unregisterGrid = useCallback((gridId: string) => {
gridsRef.current.delete(gridId)
}, [])
const sensors = useSensors(
useSensor(PointerSensor, { activationConstraint: { distance: 6 } }),
useSensor(TouchSensor, {
activationConstraint: { delay: 200, tolerance: 6 },
}),
)
const handleDragStart = useCallback((e: DragStartEvent) => {
const activeId = String(e.active.id)
const found = findGrid(gridsRef.current, activeId)
if (!found) {
return
}
const { reg, plainId } = found
const { sourceOnly, getCellSize, onSourceDragStart } = reg
const data = e.active.data.current as { w?: number; h?: number } | undefined
const { cellW, cellH, gap } = getCellSize()
const w = data?.w ?? 2
const h = data?.h ?? 2
if (sourceOnly) {
// Capture before onSourceDragStart may unmount the grid
pendingSourceRef.current = {
onSourceDrop: reg.onSourceDrop,
itemId: plainId,
}
onSourceDragStart?.()
} else {
reg.handleDragStart(e)
}
setActiveDrag({
itemId: plainId,
dims: {
width: w * cellW + (w - 1) * gap,
height: h * cellH + (h - 1) * gap,
},
})
}, [])
const handleDragMove = useCallback((e: DragMoveEvent) => {
const activeId = String(e.active.id)
const found = findGrid(gridsRef.current, activeId)
if (!found) {
return
}
const { reg } = found
if (!reg.sourceOnly) {
reg.handleDragMove(e)
}
}, [])
const handleDragEnd = useCallback((e: DragEndEvent) => {
setActiveDrag(null)
// Source drag: use the captured handler (grid may have unmounted already)
if (pendingSourceRef.current) {
const { onSourceDrop, itemId } = pendingSourceRef.current
pendingSourceRef.current = null
onSourceDrop?.(itemId)
return
}
const activeId = String(e.active.id)
const found = findGrid(gridsRef.current, activeId)
if (!found) {
return
}
found.reg.handleDragEnd(e)
}, [])
const handleDragCancel = useCallback(() => {
pendingSourceRef.current = null
setActiveDrag(null)
for (const [, reg] of gridsRef.current) {
if (!reg.sourceOnly) {
reg.handleDragCancel()
}
}
}, [])
return (
<DndGridRootContext.Provider
value={{ registerGrid, unregisterGrid, activeDrag }}
>
<DndContext
sensors={sensors}
onDragStart={handleDragStart}
onDragMove={handleDragMove}
onDragEnd={handleDragEnd}
onDragCancel={handleDragCancel}
>
{children}
</DndContext>
</DndGridRootContext.Provider>
)
}
@@ -0,0 +1,422 @@
import { AnimatePresence, motion } from 'framer-motion'
import {
Fragment,
useCallback,
useEffect,
useLayoutEffect,
useRef,
useState,
} from 'react'
import {
DndContext,
DragOverlay,
PointerSensor,
TouchSensor,
useSensor,
useSensors,
type DragEndEvent,
type DragMoveEvent,
type DragStartEvent,
} from '@dnd-kit/core'
import { cn } from '@nyanpasu/ui'
import { DndGridProvider } from './context'
import { useDndGridRoot, type GridRegistration } from './root-context'
import type {
DndGridItemType,
GridItemConstraints,
GridSize,
ResizeHandle,
} from './types'
import { useGridLayout } from './use-grid-layout'
import { calculateResize, hasOverlap } from './utils'
export interface DndGridProps<T extends string = string> {
items: DndGridItemType<T>[]
onLayoutChange?: (items: DndGridItemType<T>[]) => void
minCellSize?: number
gap?: number
size?: GridSize
onSizeChange?: (
size: GridSize,
constraintsMap: Record<string, GridItemConstraints>,
) => void
children: (item: DndGridItemType<T>) => React.ReactNode
className?: string
disabled?: boolean
sourceOnly?: boolean
dragIdPrefix?: string
gridId?: string
onSourceDrop?: (itemId: string) => void
onSourceDragStart?: () => void
}
export function DndGrid<T extends string = string>({
items,
onLayoutChange,
minCellSize = 96,
gap = 8,
size,
onSizeChange,
children,
className,
disabled = true,
sourceOnly = false,
dragIdPrefix = '',
gridId,
onSourceDrop,
onSourceDragStart,
}: DndGridProps<T>) {
const constraintsMapRef = useRef<Record<string, GridItemConstraints>>({})
const { containerRef, layout, computedSize, getItemRect, snapToGrid } =
useGridLayout(minCellSize, gap, size)
const onSizeChangeRef = useRef(onSizeChange)
onSizeChangeRef.current = onSizeChange
useEffect(() => {
if (computedSize) {
onSizeChangeRef.current?.(computedSize, constraintsMapRef.current)
}
}, [computedSize])
const [activeItem, setActiveItem] = useState<DndGridItemType<T> | null>(null)
const [previewItem, setPreviewItem] = useState<DndGridItemType<T> | null>(
null,
)
const [displayItems, setDisplayItems] = useState<DndGridItemType<T>[]>(items)
const [dropInfoMap, setDropInfoMap] = useState<
Record<string, { left: number; top: number }>
>({})
const isDragging = useRef(false)
const lastValidSnapRef = useRef<DndGridItemType<T> | null>(null)
const resizeStateRef = useRef<{
id: string
handle: ResizeHandle
startItem: DndGridItemType<T>
startX: number
startY: number
} | null>(null)
const resizePreviewRef = useRef<DndGridItemType<T> | null>(null)
const [resizingItemId, setResizingItemId] = useState<string | null>(null)
const [resizePreview, setResizePreview] = useState<DndGridItemType<T> | null>(
null,
)
useEffect(() => {
if (!isDragging.current && !resizeStateRef.current) {
setDisplayItems(items)
}
}, [items])
const effectiveDisplayItems = resizePreview
? displayItems.map((item) =>
item.id === resizePreview.id ? resizePreview : item,
)
: displayItems
const sensors = useSensors(
useSensor(PointerSensor, { activationConstraint: { distance: 6 } }),
useSensor(TouchSensor, {
activationConstraint: { delay: 200, tolerance: 6 },
}),
)
const handleDragStart = useCallback(
({ active }: DragStartEvent) => {
const item = items.find((i) => i.id === active.id)
if (!item) {
return
}
isDragging.current = true
lastValidSnapRef.current = item
setActiveItem(item)
setPreviewItem(item)
setDisplayItems(items)
},
[items],
)
const handleDragMove = useCallback(
({ delta }: DragMoveEvent) => {
if (!activeItem) {
return
}
const snapped = snapToGrid(activeItem, delta.x, delta.y)
// Only update the placeholder when the target cell is free.
if (!hasOverlap(items, activeItem.id, snapped)) {
lastValidSnapRef.current = snapped
setPreviewItem(snapped)
}
// If overlapping, placeholder stays at lastValidSnapRef — no state update needed.
},
[activeItem, items, snapToGrid],
)
const handleDragEnd = useCallback(
({ active, delta }: DragEndEvent) => {
if (!activeItem) {
return
}
const originalRect = getItemRect(activeItem)
const dropLeft = originalRect.left + delta.x
const dropTop = originalRect.top + delta.y
const snapped = snapToGrid(activeItem, delta.x, delta.y)
// Use the snapped position if it's free, otherwise fall back to the last valid snap.
const finalItem = !hasOverlap(items, activeItem.id, snapped)
? snapped
: (lastValidSnapRef.current ?? activeItem)
const newItems = items.map((i) => (i.id === active.id ? finalItem : i))
const id = String(active.id)
isDragging.current = false
lastValidSnapRef.current = null
setActiveItem(null)
setPreviewItem(null)
setDisplayItems(newItems)
setDropInfoMap((prev) => ({
...prev,
[id]: { left: dropLeft, top: dropTop },
}))
onLayoutChange?.(newItems)
},
[activeItem, items, snapToGrid, getItemRect, onLayoutChange],
)
const handleDragCancel = useCallback(() => {
isDragging.current = false
lastValidSnapRef.current = null
setActiveItem(null)
setPreviewItem(null)
setDisplayItems(items)
}, [items])
const onResizeStart = useCallback(
(id: string, handle: ResizeHandle, startX: number, startY: number) => {
const item = items.find((i) => i.id === id)
if (!item || disabled) {
return
}
resizeStateRef.current = {
id,
handle,
startItem: item,
startX,
startY,
}
setResizingItemId(id)
},
[items, disabled],
)
const onResizeMove = useCallback(
(currentX: number, currentY: number) => {
const state = resizeStateRef.current
if (!state) {
return
}
const { cellW, cellH, cols, rows } = layout
const deltaX = currentX - state.startX
const deltaY = currentY - state.startY
const candidate = calculateResize(
state.startItem,
state.handle,
deltaX,
deltaY,
cellW,
cellH,
gap,
cols,
rows,
constraintsMapRef.current[state.id],
)
if (!hasOverlap(items, state.id, candidate)) {
resizePreviewRef.current = candidate
setResizePreview(candidate)
}
},
[items, layout, gap],
)
const onResizeEnd = useCallback(() => {
const preview = resizePreviewRef.current
resizeStateRef.current = null
resizePreviewRef.current = null
setResizingItemId(null)
setResizePreview(null)
if (preview) {
const newItems = items.map((i) => (i.id === preview.id ? preview : i))
setDisplayItems(newItems)
onLayoutChange?.(newItems)
}
}, [items, onLayoutChange])
const rootCtx = useDndGridRoot()
// Stable object mutated in place every render so the root always reads fresh closures
const registrationRef = useRef<GridRegistration>({
itemIds: [],
dragIdPrefix: '',
sourceOnly: false,
handleDragStart: () => {},
handleDragMove: () => {},
handleDragEnd: () => {},
handleDragCancel: () => {},
getCellSize: () => ({ cellW: 0, cellH: 0, gap: 0 }),
})
Object.assign(registrationRef.current, {
itemIds: items.map((i) => i.id),
dragIdPrefix,
sourceOnly,
handleDragStart,
handleDragMove,
handleDragEnd,
handleDragCancel,
getCellSize: () => ({ cellW: layout.cellW, cellH: layout.cellH, gap }),
onSourceDrop,
onSourceDragStart,
})
useLayoutEffect(() => {
if (!rootCtx || !gridId) {
return
}
rootCtx.registerGrid(gridId, registrationRef.current)
return () => {
rootCtx.unregisterGrid(gridId)
}
}, [rootCtx, gridId])
const isManaged = Boolean(rootCtx && gridId)
const overlayRect = activeItem ? getItemRect(activeItem) : null
const placeholderRect = previewItem ? getItemRect(previewItem) : null
const gridContent = (
<DndGridProvider
value={{
displayItems: effectiveDisplayItems,
getItemRect,
dropInfoMap,
activeItemId: activeItem?.id ?? null,
resizingItemId,
disabled,
sourceOnly,
dragIdPrefix,
isOverlay: false,
constraintsMapRef,
onResizeStart,
onResizeMove,
onResizeEnd,
}}
>
<div
ref={containerRef}
className={cn('relative', className)}
data-slot="dnd-grid-container"
>
<AnimatePresence>
{placeholderRect && activeItem && (
<motion.div
key="dnd-grid-placeholder"
data-slot="dnd-grid-placeholder"
layout
className={cn(
'border-primary/40 bg-primary/5 border-2 border-dashed',
'pointer-events-none absolute rounded-2xl',
)}
style={{
left: placeholderRect.left,
top: placeholderRect.top,
width: placeholderRect.width,
height: placeholderRect.height,
}}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ type: 'spring', stiffness: 300, damping: 28 }}
/>
)}
</AnimatePresence>
{effectiveDisplayItems.map((item) => (
<Fragment key={item.id}>{children(item)}</Fragment>
))}
</div>
{!isManaged && (
<DragOverlay dropAnimation={null}>
<AnimatePresence>
{activeItem && overlayRect && (
<motion.div
key="dnd-grid-overlay"
data-slot="dnd-grid-overlay"
className="cursor-grabbing"
style={{ width: overlayRect.width, height: overlayRect.height }}
initial={{ opacity: 0.85 }}
animate={{ opacity: 0.95 }}
exit={{ opacity: 0 }}
transition={{ type: 'tween', duration: 0.1, ease: 'easeOut' }}
>
<DndGridProvider
value={{
displayItems: effectiveDisplayItems,
getItemRect,
dropInfoMap,
activeItemId: activeItem?.id ?? null,
resizingItemId,
disabled,
sourceOnly,
dragIdPrefix,
isOverlay: true,
constraintsMapRef,
onResizeStart,
onResizeMove,
onResizeEnd,
}}
>
{children(activeItem)}
</DndGridProvider>
</motion.div>
)}
</AnimatePresence>
</DragOverlay>
)}
</DndGridProvider>
)
if (isManaged) {
return gridContent
}
return (
<DndContext
sensors={sensors}
onDragStart={handleDragStart}
onDragMove={handleDragMove}
onDragEnd={handleDragEnd}
onDragCancel={handleDragCancel}
>
{gridContent}
</DndContext>
)
}
@@ -0,0 +1,11 @@
export { DndGrid, type DndGridProps } from './dnd-grid'
export { DndGridItem, type DndGridItemProps } from './dnd-grid-item'
export { DndGridProvider } from './context'
export { useDndGridRoot, type ActiveDrag } from './root-context'
export { DndGridRoot } from './dnd-grid-root'
export type {
DndGridItemType,
GridItemConstraints,
GridSize,
ResizeHandle,
} from './types'
@@ -0,0 +1,34 @@
import { createContext, useContext } from 'react'
import type { DragEndEvent, DragMoveEvent, DragStartEvent } from '@dnd-kit/core'
export type GridRegistration = {
itemIds: string[]
dragIdPrefix: string
sourceOnly: boolean
handleDragStart: (e: DragStartEvent) => void
handleDragMove: (e: DragMoveEvent) => void
handleDragEnd: (e: DragEndEvent) => void
handleDragCancel: () => void
getCellSize: () => { cellW: number; cellH: number; gap: number }
onSourceDrop?: (itemId: string) => void
onSourceDragStart?: () => void
}
export type ActiveDrag = {
itemId: string
dims: { width: number; height: number }
}
export type DndGridRootContextValue = {
registerGrid: (gridId: string, reg: GridRegistration) => void
unregisterGrid: (gridId: string) => void
activeDrag: ActiveDrag | null
}
export const DndGridRootContext = createContext<DndGridRootContextValue | null>(
null,
)
export function useDndGridRoot() {
return useContext(DndGridRootContext)
}
@@ -0,0 +1,49 @@
export type ResizeHandle =
| 'top'
| 'top-right'
| 'right'
| 'bottom-right'
| 'bottom'
| 'bottom-left'
| 'left'
| 'top-left'
export type DndGridItemType<T = string> = {
id: T
/** Column start index (0-indexed) */
x: number
/** Row start index (0-indexed) */
y: number
/** Width in grid cells */
w: number
/** Height in grid cells */
h: number
}
export type GridItemConstraints = {
/** Minimum width in grid cells (default: 1) */
minW?: number
/** Minimum height in grid cells (default: 1) */
minH?: number
/** Maximum width in grid cells */
maxW?: number
/** Maximum height in grid cells */
maxH?: number
}
export interface GridSize {
cols: number
rows: number
}
export interface GridLayout extends GridSize {
cellW: number
cellH: number
}
export interface ItemRect {
left: number
top: number
width: number
height: number
}
@@ -0,0 +1,117 @@
import { isEqual } from 'lodash-es'
import { useCallback, useEffect, useRef, useState } from 'react'
import type { DndGridItemType, GridLayout, GridSize, ItemRect } from './types'
export function useGridLayout(
minCellSize: number,
gap: number,
size?: GridSize,
) {
const containerRef = useRef<HTMLDivElement>(null)
const [layout, setLayout] = useState<GridLayout>({
cols: 1,
rows: 1,
cellW: minCellSize,
cellH: minCellSize,
})
const [computedSize, setComputedSize] = useState<GridSize | null>(null)
// Track container dimensions separately so we can recompute cellW/cellH when
// the external `size` override changes without waiting for a resize event.
const containerSizeRef = useRef({ width: 0, height: 0 })
const lastComputedSizeRef = useRef<GridSize | null>(null)
useEffect(() => {
const el = containerRef.current
if (!el) {
return
}
const recalculate = (width: number, height: number) => {
if (width <= 0 || height <= 0) {
return
}
containerSizeRef.current = { width, height }
// Number of cells that fit: solve `n * size + (n-1) * gap <= total`
// => n <= (total + gap) / (size + gap)
const computedCols = Math.max(
1,
Math.floor((width + gap) / (minCellSize + gap)),
)
const computedRows = Math.max(
1,
Math.floor((height + gap) / (minCellSize + gap)),
)
const newComputedSize = {
cols: computedCols,
rows: computedRows,
}
if (!isEqual(newComputedSize, lastComputedSizeRef.current)) {
lastComputedSizeRef.current = newComputedSize
setComputedSize(newComputedSize)
}
const cols = size?.cols ?? computedCols
const rows = size?.rows ?? computedRows
const cellW = (width - gap * (cols - 1)) / cols
const cellH = (height - gap * (rows - 1)) / rows
const nextLayout = { cols, rows, cellW, cellH }
setLayout((prev) => (isEqual(prev, nextLayout) ? prev : nextLayout))
}
const observer = new ResizeObserver(([entry]) => {
const { width, height } = entry.contentRect
recalculate(width, height)
})
observer.observe(el)
const { width, height } = el.getBoundingClientRect()
recalculate(width, height)
return () => {
observer.disconnect()
}
}, [minCellSize, gap, size?.cols, size?.rows])
const getItemRect = useCallback(
(item: DndGridItemType): ItemRect => {
const { cellW, cellH } = layout
return {
left: item.x * (cellW + gap),
top: item.y * (cellH + gap),
width: item.w * cellW + (item.w - 1) * gap,
height: item.h * cellH + (item.h - 1) * gap,
}
},
[layout, gap],
)
/** Convert a pixel delta into a clamped new grid position for the given item */
const snapToGrid = useCallback(
<T extends string>(
item: DndGridItemType<T>,
deltaX: number,
deltaY: number,
): DndGridItemType<T> => {
const { cellW, cellH, cols, rows } = layout
const deltaCols = Math.round(deltaX / (cellW + gap))
const deltaRows = Math.round(deltaY / (cellH + gap))
return {
...item,
x: Math.max(0, Math.min(cols - item.w, item.x + deltaCols)),
y: Math.max(0, Math.min(rows - item.h, item.y + deltaRows)),
}
},
[layout, gap],
)
return { containerRef, layout, computedSize, getItemRect, snapToGrid }
}
@@ -0,0 +1,78 @@
import type {
DndGridItemType,
GridItemConstraints,
ResizeHandle,
} from './types'
export function isOverlap<T extends string>(
a: DndGridItemType<T>,
b: DndGridItemType<T>,
): boolean {
return (
a.x < b.x + b.w && a.x + a.w > b.x && a.y < b.y + b.h && a.y + a.h > b.y
)
}
export function hasOverlap<T extends string>(
items: DndGridItemType<T>[],
movingId: string,
candidate: DndGridItemType<T>,
): boolean {
return items.some(
(item) => item.id !== movingId && isOverlap(candidate, item),
)
}
export function calculateResize<T extends string>(
startItem: DndGridItemType<T>,
handle: ResizeHandle,
deltaX: number,
deltaY: number,
cellW: number,
cellH: number,
gap: number,
cols: number,
rows: number,
constraints: GridItemConstraints = {},
): DndGridItemType<T> {
const minW = constraints.minW ?? 1
const minH = constraints.minH ?? 1
const maxW = constraints.maxW ?? cols
const maxH = constraints.maxH ?? rows
const stepX = cellW + gap
const stepY = cellH + gap
const deltaCols = Math.round(deltaX / stepX)
const deltaRows = Math.round(deltaY / stepY)
let { x, y, w, h } = startItem
if (handle.includes('right')) {
w = Math.max(minW, Math.min(maxW, cols - x, w + deltaCols))
}
if (handle.includes('bottom')) {
h = Math.max(minH, Math.min(maxH, rows - y, h + deltaRows))
}
if (handle.includes('left')) {
const newX = Math.max(0, Math.min(x + w - minW, x + deltaCols))
const newW = Math.min(maxW, w + (x - newX))
x = newX + (w + (x - newX) - newW)
w = newW
}
if (handle.includes('top')) {
const newY = Math.max(0, Math.min(y + h - minH, y + deltaRows))
const newH = Math.min(maxH, h + (y - newY))
y = newY + (h + (y - newY) - newH)
h = newH
}
return {
...startItem,
x,
y,
w,
h,
}
}
@@ -0,0 +1,260 @@
import * as d3 from 'd3'
import { animate } from 'framer-motion'
import { cloneDeep } from 'lodash-es'
import { ComponentPropsWithoutRef, useEffect, useRef } from 'react'
import { cn } from '@nyanpasu/ui'
/**
* Coefficient of variation threshold (std / mean) below which the series is
* considered "stable". CV is scale-independent: 1112 and 9 000=11 000 are
* evaluated on the same relative basis regardless of their absolute magnitude.
*/
const STABLE_CV_THRESHOLD = 0.15
/**
* When the series is stable, the chart only occupies the bottom third of the
* SVG height (topFactor = 2/3 usable band = height - height*(2/3) = h/3).
*/
const STABLE_TOP_FACTOR = 2 / 3
/** When the series has a wide range, use most of the available height. */
const ACTIVE_TOP_FACTOR = 0.35
export const Sparkline = ({
data,
animationDuration = 1,
className,
...props
}: ComponentPropsWithoutRef<'svg'> & {
data: number[]
animationDuration?: number
}) => {
const svgRef = useRef<SVGSVGElement | null>(null)
const gRef = useRef<SVGGElement | null>(null)
const prevDataRef = useRef<number[] | null>(null)
// Tracks the most recently scrolled-off left point so successive cycles share
// the same left guard value, making the curve at x=0 seamless across transitions.
const leftGuardRef = useRef<number | null>(null)
const animRef = useRef<ReturnType<typeof animate> | null>(null)
useEffect(() => {
if (!svgRef.current || !gRef.current) {
return
}
const g = d3.select(gRef.current)
const { width, height } = svgRef.current.getBoundingClientRect()
if (!width || !height) {
return
}
const makePaths = (
points: number[],
xRange: [number, number],
yMax: number,
) => {
const mean = d3.mean(points) ?? 0
const std = d3.deviation(points) ?? 0
const cv = mean > 0 ? std / mean : 0
const topFactor =
yMax === 0
? 1
: cv < STABLE_CV_THRESHOLD
? STABLE_TOP_FACTOR
: ACTIVE_TOP_FACTOR
const x = d3
.scaleLinear()
.domain([0, points.length - 1])
.range(xRange)
const y = d3
.scaleLinear()
.domain([0, yMax])
.range([height, height * topFactor])
const lineGen = d3
.line<number>()
.x((_, i) => x(i))
.y((d) => y(d))
.curve(d3.curveCatmullRom.alpha(0.5))
const areaGen = d3
.area<number>()
.x((_, i) => x(i))
.y0(height)
.y1((d) => y(d))
.curve(d3.curveCatmullRom.alpha(0.5))
return {
line: lineGen(points) ?? '',
area: areaGen(points) ?? '',
}
}
// Prepend/append invisible guard points one step outside each edge so that
// every visible data point is treated as an interior spline node, eliminating
// the endpoint tangent discontinuity that causes boundary wobble.
// SVG overflow:hidden clips the guard region automatically.
const buildPaths = (
points: number[],
xRange: [number, number],
yMax: number,
step: number,
leftGuard?: number,
) => {
const n = points.length
// Fast-path for empty or single-point series to avoid invalid guard math.
if (n === 0) {
// No data → no path.
return { line: '', area: '' }
}
if (n === 1) {
// Single point: render a degenerate path without guard extension.
return makePaths(points, xRange, yMax)
}
const lGuard = leftGuard ?? 2 * points[0] - points[1]
const rGuard = 2 * points[n - 1] - points[n - 2]
return makePaths(
[lGuard, ...points, rGuard],
[xRange[0] - step, xRange[1] + step],
yMax,
)
}
const prevData = prevDataRef.current
prevDataRef.current = cloneDeep(data)
animRef.current?.stop()
animRef.current = null
// Handle short series early to avoid division by zero and invalid indexing.
if (data.length < 2) {
g.selectAll('*').remove()
g.attr('transform', 'translate(0,0)')
leftGuardRef.current = null
return
}
if (!prevData || prevData.length !== data.length) {
const yMax = Math.max(d3.max(data) ?? 0, 1)
const step = width / (data.length - 1)
const { line, area } = buildPaths(
data,
[0, width],
yMax,
step,
leftGuardRef.current ?? undefined,
)
g.selectAll('*').remove()
g.attr('transform', 'translate(0,0)')
g.append('path').attr('class', 'area fill-primary/10').attr('d', area)
g.append('path')
.attr('class', 'line stroke-primary')
.attr('fill', 'none')
.attr('stroke-width', 2)
.attr('d', line)
return
}
const stepWidth = width / (data.length - 1)
// N+1 points: the old leading point (about to scroll off) + the full new data array.
const extPoints = [...prevData, data[data.length - 1]]
const fromYMax = Math.max(d3.max(extPoints) ?? 0, 1)
const toYMax = Math.max(d3.max(data) ?? 0, 1)
const yMaxChanges = Math.abs(fromYMax - toYMax) > 1
// Use the stored left guard so the CatmullRom context at x=0 is identical
// between the N-point path rendered before this animation and the N+1-point
// path at t=0, making the left edge transition seamless.
const leftGuard = leftGuardRef.current ?? undefined
// Render the initial (pre-animation) state.
const { line: initLine, area: initArea } = buildPaths(
extPoints,
[0, width + stepWidth],
fromYMax,
stepWidth,
leftGuard,
)
g.attr('transform', 'translate(0,0)')
g.select('.area').attr('d', initArea)
g.select('.line').attr('d', initLine)
let cancelled = false
const anim = animate(0, 1, {
duration: animationDuration,
ease: 'linear',
onUpdate(t) {
// X-axis: pure linear translation — the scroll must feel constant-speed.
g.attr('transform', `translate(${-stepWidth * t},0)`)
// Y-axis: non-linear easing for the yMax interpolation so the height
// change feels more natural (slow start/end, faster in the middle).
// Because x is driven by the translation and y is driven independently
// by yMax, the two axes never couple — no wobble.
if (yMaxChanges) {
const easedT = d3.easeCubicInOut(t)
const currentYMax = fromYMax + (toYMax - fromYMax) * easedT
const { line, area } = buildPaths(
extPoints,
[0, width + stepWidth],
currentYMax,
stepWidth,
leftGuard,
)
g.select('.area').attr('d', area)
g.select('.line').attr('d', line)
}
},
onComplete() {
if (cancelled) {
return
}
// The scrolled-off point becomes the left guard for the next cycle so
// the curve shape at x=0 stays consistent across animation boundaries.
leftGuardRef.current = prevData[0]
// At t=1 the N+1-point path at -stepWidth and the N-point path at x=0
// occupy identical visual coordinates, so the swap is seamless.
const { line, area } = buildPaths(
data,
[0, width],
toYMax,
stepWidth,
prevData[0],
)
g.attr('transform', 'translate(0,0)')
g.select('.area').attr('d', area)
g.select('.line').attr('d', line)
},
})
animRef.current = anim
return () => {
cancelled = true
anim.stop()
}
}, [data, animationDuration])
return (
<svg
ref={svgRef}
data-slot="sparkline"
className={cn('size-full overflow-hidden', className)}
{...props}
>
<g ref={gRef} />
</svg>
)
}
@@ -1,5 +1,11 @@
import { motion, useAnimationControls } from 'framer-motion'
import { useCallback, useEffect, useRef, useState } from 'react'
import {
useCallback,
useEffect,
useRef,
useState,
type CSSProperties,
} from 'react'
import { sleep } from '@/utils'
import { cn } from '@nyanpasu/ui'
@@ -9,6 +15,8 @@ export default function TextMarquee({
speed = 30,
gap = 32,
pauseDuration = 1,
fadeEdges = false,
fadeWidth = 12,
// pauseOnHover = true,
}: {
children: React.ReactNode
@@ -16,6 +24,8 @@ export default function TextMarquee({
speed?: number
gap?: number
pauseDuration?: number
fadeEdges?: boolean
fadeWidth?: number
// pauseOnHover?: boolean
}) {
const containerRef = useRef<HTMLDivElement>(null)
@@ -171,8 +181,22 @@ export default function TextMarquee({
return (
<div
ref={containerRef}
className={cn('overflow-hidden', className)}
className={cn(
'overflow-hidden',
fadeEdges && [
'mask-[linear-gradient(to_right,transparent_0,black_var(--marquee-fade-width),black_calc(100%-var(--marquee-fade-width)),transparent_100%)]',
'[-webkit-mask-image:linear-gradient(to_right,transparent_0,black_var(--marquee-fade-width),black_calc(100%-var(--marquee-fade-width)),transparent_100%)]',
],
className,
)}
data-slot="text-marquee"
style={
fadeEdges
? ({
'--marquee-fade-width': `${fadeWidth}px`,
} as CSSProperties)
: {}
}
>
{shouldAnimate ? (
<motion.div
@@ -0,0 +1,341 @@
import { ReactNode } from 'react'
import type { DndGridItemType } from '@/components/ui/dnd-grid'
import { CoreShortcutsWidget, ProxyShortcutsWidget } from './widget-shortcut'
import {
ConnectionsWidget,
MemoryWidget,
TrafficDownWidget,
TrafficUpWidget,
} from './widget-sparkline'
export enum WidgetId {
TrafficDown = 'traffic-down',
TrafficUp = 'traffic-up',
Connections = 'connections',
Memory = 'memory',
ProxyShortcuts = 'proxy-shortcuts',
CoreShortcuts = 'core-shortcuts',
}
export type DashboardItem = DndGridItemType<string> & { type: WidgetId }
export type WidgetComponentProps = {
id: string
onCloseClick?: (id: string) => void
}
export const RENDER_MAP: Record<
WidgetId,
(props: WidgetComponentProps) => ReactNode
> = {
[WidgetId.TrafficDown]: TrafficDownWidget,
[WidgetId.TrafficUp]: TrafficUpWidget,
[WidgetId.Connections]: ConnectionsWidget,
[WidgetId.Memory]: MemoryWidget,
[WidgetId.ProxyShortcuts]: ProxyShortcutsWidget,
[WidgetId.CoreShortcuts]: CoreShortcutsWidget,
}
/** Default layout, designed for a 12-column grid. */
export const DEFAULT_ITEMS: DashboardItem[] = [
{
id: WidgetId.TrafficDown,
type: WidgetId.TrafficDown,
x: 0,
y: 0,
w: 3,
h: 2,
},
{
id: WidgetId.TrafficUp,
type: WidgetId.TrafficUp,
x: 3,
y: 0,
w: 3,
h: 2,
},
{
id: WidgetId.Memory,
type: WidgetId.Memory,
x: 6,
y: 0,
w: 3,
h: 2,
},
{
id: WidgetId.Connections,
type: WidgetId.Connections,
x: 9,
y: 0,
w: 3,
h: 2,
},
{
id: WidgetId.ProxyShortcuts,
type: WidgetId.ProxyShortcuts,
x: 0,
y: 2,
w: 3,
h: 3,
},
{
id: WidgetId.CoreShortcuts,
type: WidgetId.CoreShortcuts,
x: 3,
y: 2,
w: 4,
h: 2,
},
]
export const WIDGET_MIN_SIZE_MAP: Record<
WidgetId,
{ minW: number; minH: number }
> = {
[WidgetId.TrafficDown]: { minW: 2, minH: 2 },
[WidgetId.TrafficUp]: { minW: 2, minH: 2 },
[WidgetId.Connections]: { minW: 2, minH: 2 },
[WidgetId.Memory]: { minW: 2, minH: 2 },
[WidgetId.ProxyShortcuts]: { minW: 3, minH: 2 },
[WidgetId.CoreShortcuts]: { minW: 4, minH: 2 },
}
export type LayoutStorage = Record<string, DashboardItem[]>
// preset layouts for common grid sizes
export const DEFAULT_LAYOUTS: LayoutStorage = {
'4x5': [
{
id: WidgetId.TrafficDown,
type: WidgetId.TrafficDown,
x: 0,
y: 0,
w: 2,
h: 2,
},
{
id: WidgetId.TrafficUp,
type: WidgetId.TrafficUp,
x: 2,
y: 0,
w: 2,
h: 2,
},
{
id: WidgetId.Memory,
type: WidgetId.Memory,
x: 0,
y: 2,
w: 2,
h: 2,
},
{
id: WidgetId.Connections,
type: WidgetId.Connections,
x: 2,
y: 2,
w: 2,
h: 2,
},
],
'8x6': [
{
id: WidgetId.TrafficDown,
type: WidgetId.TrafficDown,
x: 0,
y: 0,
w: 2,
h: 2,
},
{
id: WidgetId.TrafficUp,
type: WidgetId.TrafficUp,
x: 2,
y: 0,
w: 2,
h: 2,
},
{
id: WidgetId.Memory,
type: WidgetId.Memory,
x: 4,
y: 0,
w: 2,
h: 2,
},
{
id: WidgetId.Connections,
type: WidgetId.Connections,
x: 6,
y: 0,
w: 2,
h: 2,
},
{
id: WidgetId.ProxyShortcuts,
type: WidgetId.ProxyShortcuts,
x: 0,
y: 2,
w: 3,
h: 2,
},
{
id: WidgetId.CoreShortcuts,
type: WidgetId.CoreShortcuts,
x: 3,
y: 2,
w: 5,
h: 2,
},
],
'12x6': [
{
id: WidgetId.TrafficDown,
type: WidgetId.TrafficDown,
x: 0,
y: 0,
w: 3,
h: 2,
},
{
id: WidgetId.TrafficUp,
type: WidgetId.TrafficUp,
x: 3,
y: 0,
w: 3,
h: 2,
},
{
id: WidgetId.Memory,
type: WidgetId.Memory,
x: 6,
y: 0,
w: 3,
h: 2,
},
{
id: WidgetId.Connections,
type: WidgetId.Connections,
x: 9,
y: 0,
w: 3,
h: 2,
},
{
id: WidgetId.ProxyShortcuts,
type: WidgetId.ProxyShortcuts,
x: 0,
y: 2,
w: 3,
h: 2,
},
{
id: WidgetId.CoreShortcuts,
type: WidgetId.CoreShortcuts,
x: 3,
y: 2,
w: 5,
h: 2,
},
],
'16x6': [
{
id: WidgetId.TrafficDown,
type: WidgetId.TrafficDown,
x: 0,
y: 0,
w: 4,
h: 2,
},
{
id: WidgetId.TrafficUp,
type: WidgetId.TrafficUp,
x: 4,
y: 0,
w: 4,
h: 2,
},
{
id: WidgetId.Memory,
type: WidgetId.Memory,
x: 8,
y: 0,
w: 4,
h: 2,
},
{
id: WidgetId.Connections,
type: WidgetId.Connections,
x: 12,
y: 0,
w: 4,
h: 2,
},
{
id: WidgetId.ProxyShortcuts,
type: WidgetId.ProxyShortcuts,
x: 0,
y: 2,
w: 4,
h: 3,
},
{
id: WidgetId.CoreShortcuts,
type: WidgetId.CoreShortcuts,
x: 4,
y: 2,
w: 5,
h: 2,
},
],
'20x6': [
{
id: WidgetId.TrafficDown,
type: WidgetId.TrafficDown,
x: 0,
y: 0,
w: 5,
h: 2,
},
{
id: WidgetId.TrafficUp,
type: WidgetId.TrafficUp,
x: 5,
y: 0,
w: 5,
h: 2,
},
{
id: WidgetId.Memory,
type: WidgetId.Memory,
x: 10,
y: 0,
w: 5,
h: 2,
},
{
id: WidgetId.Connections,
type: WidgetId.Connections,
x: 15,
y: 0,
w: 5,
h: 2,
},
{
id: WidgetId.ProxyShortcuts,
type: WidgetId.ProxyShortcuts,
x: 0,
y: 2,
w: 5,
h: 3,
},
{
id: WidgetId.CoreShortcuts,
type: WidgetId.CoreShortcuts,
x: 5,
y: 2,
w: 5,
h: 2,
},
],
}
@@ -0,0 +1,67 @@
import AddRounded from '~icons/material-symbols/add-rounded'
import DoneRounded from '~icons/material-symbols/done-rounded'
import { AnimatePresence, motion } from 'framer-motion'
import { Button } from '@/components/ui/button'
import { m } from '@/paraglide/messages'
import { cn } from '@nyanpasu/ui'
import { useDashboardContext } from './provider'
export default function EditAction() {
const { isEditing, setIsEditing, setOpenSheet } = useDashboardContext()
return (
<AnimatePresence>
{isEditing && (
<motion.div
className={cn(
'absolute bottom-8 left-1/2 z-10 -translate-x-1/2',
'flex h-14 min-w-0 items-center justify-between gap-6 px-3',
'dark:border-surface-variant/50 border-surface/50 rounded-full border',
'dark:bg-surface/30 bg-surface-variant/30 backdrop-blur-lg',
)}
data-slot="dashboard-edit-header"
initial={{
scale: 0.8,
opacity: 0,
y: 128,
}}
animate={{
scale: 1,
opacity: 1,
y: 0,
}}
exit={{
scale: 0.8,
opacity: 0,
y: 128,
}}
transition={{
type: 'spring',
bounce: 0,
duration: 0.45,
}}
>
<Button
variant="raised"
className="flex h-8 items-center gap-1 px-3 text-sm text-nowrap"
onClick={() => setOpenSheet(true)}
>
<AddRounded />
<span>{m.dashboard_add_widget()}</span>
</Button>
<Button
variant="flat"
className="flex h-8 items-center gap-1 px-3 text-sm text-nowrap"
onClick={() => setIsEditing(false)}
>
<DoneRounded />
<span>{m.common_save()}</span>
</Button>
</motion.div>
)}
</AnimatePresence>
)
}
@@ -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<T extends DndGridItemType<string>>(
storage: Record<string, T[]>,
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<T extends DndGridItemType<string>>(
storage: Record<string, T[]>,
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<T extends DndGridItemType<string>>(
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<T extends DndGridItemType<string>>(
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<T extends DndGridItemType<string>>(
items: T[],
size: GridSize,
constraints: Record<string, GridItemConstraints>,
): 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
}
@@ -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 (
<DashboardContext.Provider
value={{
openSheet,
setOpenSheet,
isEditing,
setIsEditing,
}}
>
{children}
</DashboardContext.Provider>
)
}
@@ -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<string> & WidgetComponentProps
export default function WidgetItem({
children,
className,
onCloseClick,
...props
}: WidgetItemProps) {
const { disabled, sourceOnly } = useDndGridContext()
return (
<DndGridItem {...props} className={cn('relative', className)}>
{children}
<AnimatePresence>
{!disabled && !sourceOnly && (
<Button
variant="raised"
className={cn(
'absolute -top-1 -right-1 z-10 size-8',
'border-outline/30 border',
)}
icon
onClick={() => onCloseClick?.(props.id)}
asChild
>
<motion.button
initial={{
scale: 0.85,
opacity: 0,
}}
animate={{
scale: 1,
opacity: 1,
}}
exit={{
scale: 0.85,
opacity: 0,
}}
transition={{
type: 'tween',
duration: 0.1,
ease: 'easeOut',
}}
>
<CloseRounded className="size-4" />
</motion.button>
</Button>
)}
</AnimatePresence>
</DndGridItem>
)
}
@@ -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<GridSize>()
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 (
<Drawer.Root open={openSheet} onOpenChange={setOpenSheet}>
<Drawer.Portal>
<Drawer.Overlay className="fixed inset-0 bg-black/30" />
<Drawer.Content
className={cn(
'fixed inset-x-0 bottom-0 z-50 mx-auto max-w-96 min-w-96',
'dark:bg-surface/30 bg-surface-variant/30 backdrop-blur-3xl',
'h-full max-h-1/2 min-h-96 rounded-t-2xl',
'dark:border-surface-variant/50 border-surface/50 border',
'flex flex-col',
)}
aria-describedby={undefined}
>
<div className="flex items-center justify-between gap-4 p-4">
<Drawer.Title className="text-lg font-semibold">
{m.dashboard_add_widget()}
</Drawer.Title>
<Drawer.Close asChild>
<Button variant="raised" className="size-8" icon>
<CloseRounded className="size-4" />
</Button>
</Drawer.Close>
</div>
<ScrollArea
className={cn(
'min-h-0 flex-1',
'[&_[data-slot=scroll-area-viewport]>div]:block!',
'[&_[data-slot=scroll-area-viewport]>div]:h-full',
)}
>
<div className="flex h-full w-full flex-col px-4">
<DndGrid
gridId="sheet"
className="min-h-0 flex-1"
items={sheetItems}
minCellSize={64}
gap={16}
disabled={false}
sourceOnly
dragIdPrefix="sheet:"
onSourceDrop={(id) => onSourceDrop(id as WidgetId)}
onSourceDragStart={onSourceDragStart}
onSizeChange={(size) => setGridSize(size)}
>
{(item) => {
const WidgetComponent = RENDER_MAP[item.id as WidgetId]
return (
<WidgetComponent id={item.id} onCloseClick={() => {}} />
)
}}
</DndGrid>
</div>
</ScrollArea>
</Drawer.Content>
</Drawer.Portal>
</Drawer.Root>
)
}
@@ -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<ProxyStatus>(() => {
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 (
<CardHeader className="flex items-center gap-3">
<span className="shrink-0 font-bold">
{m.dashboard_widget_proxy_status()}
</span>
<Button
variant="raised"
className={cn(
'flex h-6 min-w-0 items-center px-0',
status === ProxyStatus.DISABLED &&
'bg-secondary-container hover:bg-on-secondary',
status === ProxyStatus.OCCUPIED &&
'bg-error-container hover:bg-on-error',
status === ProxyStatus.SYSTEM &&
'bg-primary-container hover:bg-on-primary',
status === ProxyStatus.TUN &&
'bg-tertiary-container hover:bg-on-tertiary',
)}
asChild
>
<Link to="/main/settings/system">
<TextMarquee className="px-2" fadeEdges fadeWidth={8}>
{messages[status]}
</TextMarquee>
</Link>
</Button>
</CardHeader>
)
}
export function ProxyShortcutsWidget({
id,
onCloseClick,
}: WidgetComponentProps) {
return (
<WidgetItem id={id} minW={3} minH={2} onCloseClick={onCloseClick}>
<Card className="flex size-full flex-col justify-between">
<ProxyTitleRow />
<CardContent className="flex-1 gap-3">
<SystemProxyButton className="h-full rounded-3xl" />
<TunModeButton className="h-full rounded-3xl" />
</CardContent>
</Card>
</WidgetItem>
)
}
const CoreStatusBadge = () => {
const {
query: { data: serviceStatus },
} = useSystemService()
const { data: coreStatus } = useCoreStatus()
const message = useMemo<string>(() => {
// 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 (
<div
className={cn(
'flex h-6 min-w-0 items-center rounded-full text-sm',
'bg-surface-variant/50',
)}
data-slot="core-status-badge"
>
<TextMarquee className="px-2" fadeEdges fadeWidth={8}>
{message}
</TextMarquee>
</div>
)
}
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 (
<Button
variant="raised"
className={cn(
'group flex flex-1 items-center gap-4 rounded-2xl pr-3 pl-4',
'bg-surface-variant/30 hover:bg-surface-variant',
)}
data-running={String(isRunning)}
data-slot="current-core-card"
asChild
>
<Link to="/main/settings/clash">
<img
src={currentCoreIcon}
alt={currentCore?.name}
className="size-12 shrink-0"
data-slot="core-icon"
/>
<div
className="flex flex-1 flex-col items-start gap-1 truncate"
data-slot="core-info"
>
<div className="font-semibold" data-slot="core-name">
{currentCore?.name}
</div>
<div
className="text-zinc-700 dark:text-zinc-300"
data-slot="core-version"
>
{currentCore?.currentVersion}
</div>
</div>
<div
className="flex items-center gap-2 truncate pr-2"
data-slot="core-status"
>
<div className="truncate" data-slot="core-status-text">
{isRunning
? m.dashboard_widget_core_status_running()
: m.dashboard_widget_core_status_stopped()}
</div>
<div
className="relative flex size-3 shrink-0"
data-slot="core-status-indicator"
>
<span
className={cn(
'absolute inline-flex size-full animate-ping rounded-full opacity-75',
'group-data-[running=true]:bg-green-500',
'group-data-[running=false]:opacity-0',
)}
/>
<span
className={cn(
'relative inline-flex size-full rounded-full',
'group-data-[running=true]:bg-green-500',
'group-data-[running=false]:bg-gray-400',
)}
/>
</div>
</div>
</Link>
</Button>
)
}
export function CoreShortcutsWidget({
id,
onCloseClick,
}: WidgetComponentProps) {
return (
<WidgetItem id={id} minW={4} minH={2} onCloseClick={onCloseClick}>
<Card className="flex size-full flex-col justify-between">
<CardHeader>
<span className="shrink-0 font-bold">
{m.dashboard_widget_core_status()}
</span>
<CoreStatusBadge />
</CardHeader>
<CardContent className="flex-1">
<CurrentCoreCard />
</CardContent>
</Card>
</WidgetItem>
)
}
@@ -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<typeof Card> & {
data: number[]
} & WidgetItemProps) {
return (
<WidgetItem
id={id}
minH={minH}
minW={minW}
maxW={maxW}
maxH={maxH}
onCloseClick={onCloseClick}
>
<Card
className={cn('relative isolate size-full', className)}
data-slot="widget-sparkline-card"
{...props}
>
<Sparkline data={data} className="absolute inset-0 z-0" />
<CardContent
className="relative z-10 flex size-full flex-col justify-between"
data-slot="widget-sparkline-card-content"
>
{children}
</CardContent>
</Card>
</WidgetItem>
)
}
function SparklineCardTitle({
icon: Icon,
className,
children,
...props
}: ComponentProps<'div'> & {
icon: ComponentType<{
className?: string
}>
}) {
return (
<div
className={cn('flex items-center gap-2', className)}
data-slot="widget-sparkline-card-title"
{...props}
>
<Icon className="size-5 shrink-0" />
<TextMarquee className="font-bold">{children}</TextMarquee>
</div>
)
}
function SparklineCardContent({ className, ...props }: ComponentProps<'div'>) {
return (
<div
className={cn('text-2xl font-bold text-nowrap text-shadow-md', className)}
data-slot="widget-sparkline-card-content"
{...props}
/>
)
}
function SparklineCardBottom({ className, ...props }: ComponentProps<'div'>) {
return (
<div
className={cn(
'text-shadow-background h-5 text-sm text-nowrap text-shadow-xs',
className,
)}
data-slot="widget-sparkline-card-bottom"
{...props}
/>
)
}
export function TrafficDownWidget({ id, onCloseClick }: WidgetComponentProps) {
const { data: clashTraffic } = useClashTraffic()
const {
query: { data: clashConnections },
} = useClashConnections()
const total = clashConnections?.at(-1)?.downloadTotal
return (
<SparklineCard
id={id}
data={padData(
clashTraffic?.map((item) => item.down),
MAX_TRAFFIC_HISTORY,
)}
onCloseClick={onCloseClick}
>
<SparklineCardTitle icon={ArrowDownwardRounded}>
{m.dashboard_widget_traffic_download()}
</SparklineCardTitle>
<SparklineCardContent>
{filesize(clashTraffic?.at(-1)?.down ?? 0)}/s
</SparklineCardContent>
<SparklineCardBottom>
{total !== undefined &&
m.dashboard_widget_traffic_total({
value: filesize(total),
})}
</SparklineCardBottom>
</SparklineCard>
)
}
export function TrafficUpWidget({ id, onCloseClick }: WidgetComponentProps) {
const { data: clashTraffic } = useClashTraffic()
const {
query: { data: clashConnections },
} = useClashConnections()
const total = clashConnections?.at(-1)?.uploadTotal
return (
<SparklineCard
id={id}
data={padData(
clashTraffic?.map((item) => item.up),
MAX_TRAFFIC_HISTORY,
)}
onCloseClick={onCloseClick}
>
<SparklineCardTitle icon={ArrowUpwardRounded}>
{m.dashboard_widget_traffic_upload()}
</SparklineCardTitle>
<SparklineCardContent>
{filesize(clashTraffic?.at(-1)?.up ?? 0)}/s
</SparklineCardContent>
<SparklineCardBottom>
{total !== undefined &&
m.dashboard_widget_traffic_total({
value: filesize(total),
})}
</SparklineCardBottom>
</SparklineCard>
)
}
export function ConnectionsWidget({ id, onCloseClick }: WidgetComponentProps) {
const {
query: { data: clashConnections },
} = useClashConnections()
return (
<SparklineCard
id={id}
data={padData(
clashConnections?.map((item) => item.connections?.length ?? 0),
MAX_CONNECTIONS_HISTORY,
)}
onCloseClick={onCloseClick}
>
<SparklineCardTitle icon={SettingsEthernetRounded}>
{m.dashboard_widget_connections()}
</SparklineCardTitle>
<SparklineCardContent>
{clashConnections?.at(-1)?.connections?.length ?? 0}
</SparklineCardContent>
<SparklineCardBottom />
</SparklineCard>
)
}
export function MemoryWidget({ id, onCloseClick }: WidgetComponentProps) {
const { data: clashMemory } = useClashMemory()
return (
<SparklineCard
id={id}
data={padData(
clashMemory?.map((item) => item.inuse),
MAX_MEMORY_HISTORY,
)}
onCloseClick={onCloseClick}
>
<SparklineCardTitle icon={MemoryOutlineRounded}>
{m.dashboard_widget_memory()}
</SparklineCardTitle>
<SparklineCardContent>
{filesize(clashMemory?.at(-1)?.inuse ?? 0)}
</SparklineCardContent>
<SparklineCardBottom />
</SparklineCard>
)
}
@@ -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<string>[]): 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 (
<DragOverlay dropAnimation={null}>
{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 (
<div
className="cursor-grabbing rounded-2xl opacity-90"
style={{
width: activeDrag.dims.width,
height: activeDrag.dims.height,
}}
>
<DndGridProvider
value={{
displayItems: [],
getItemRect: () => ({
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: () => {},
}}
>
<WidgetComponent id={activeDrag.itemId} />
</DndGridProvider>
</div>
)
})()}
</DragOverlay>
)
}
const WidgetRender = () => {
const { isEditing, setOpenSheet } = useDashboardContext()
const [layoutStorage, setLayoutStorage] = useKvStorage<LayoutStorage>(
'dashboard-widgets',
DEFAULT_LAYOUTS,
)
const [displayItems, setDisplayItems] =
useState<DashboardItem[]>(DEFAULT_ITEMS)
const layoutStorageRef = useRef(layoutStorage)
layoutStorageRef.current = layoutStorage
const displayItemsRef = useRef(displayItems)
displayItemsRef.current = displayItems
const gridSizeRef = useRef<GridSize>({ cols: 1, rows: 1 })
const handleSizeChange = useCallback(
(
newSize: GridSize,
constraintsMap: Record<string, GridItemConstraints>,
) => {
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 (
<DndGridRoot>
<div className="size-full p-4" data-slot="dashboard-widget-container">
<DndGrid
gridId="main"
className="size-full"
items={displayItems}
onLayoutChange={(newItems) =>
handleLayoutChange(normalizeItems(newItems))
}
minCellSize={64}
onSizeChange={handleSizeChange}
gap={16}
disabled={!isEditing}
>
{(item) => {
const WidgetComponent = RENDER_MAP[(item as DashboardItem).type]
return (
<WidgetComponent
id={item.id}
onCloseClick={() =>
handleLayoutChange(
displayItemsRef.current.filter((i) => i.id !== item.id),
)
}
/>
)
}}
</DndGrid>
</div>
<DashboardDragOverlay displayItems={displayItemsRef.current} />
<WidgetSheet
onSourceDrop={(id) => addWidgetFromSheet(id)}
onSourceDragStart={() => setOpenSheet(false)}
/>
</DndGridRoot>
)
}
function RouteComponent() {
const { setIsEditing, setOpenSheet } = useDashboardContext()
return (
<RegisterContextMenu>
<RegisterContextMenuTrigger asChild>
<div
data-slot="dashboard-container"
className="relative size-full overflow-hidden"
>
<WidgetRender />
<EditAction />
</div>
</RegisterContextMenuTrigger>
<RegisterContextMenuContent>
<ContextMenuItem onSelect={() => setIsEditing(true)}>
<EditRounded className="size-4" />
<span>{m.dashboard_context_menu_edit_widgets()}</span>
</ContextMenuItem>
<ContextMenuItem
onSelect={() => {
setIsEditing(true)
setOpenSheet(true)
}}
>
<AddRounded className="size-4" />
<span>{m.dashboard_context_menu_add_widgets()}</span>
</ContextMenuItem>
</RegisterContextMenuContent>
</RegisterContextMenu>
)
}
@@ -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 (
<AppContentScrollArea>
<div className="h-dvh">
<p>Hello "/(main)/main/dashboard"!</p>
<Button>Click me</Button>
</div>
</AppContentScrollArea>
<DashboardProvider>
<Outlet />
</DashboardProvider>
)
}
@@ -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 (
<SettingsCard asChild>
<SettingsCardAnimatedItem>
@@ -47,37 +52,61 @@ export default function KVStorage() {
</div>
{query.data &&
query.data.map((storage) => (
<div key={storage.key} className="flex items-center gap-2">
<div className="flex-1">Key: {storage.key}</div>
query.data.map((storage) => {
const tryFmt = () => {
try {
const parsed = JSON.parse(storage.value)
<Modal>
<ModalTrigger asChild>
<Button variant="stroked" className="h-8 min-w-0 px-3">
Detail
</Button>
</ModalTrigger>
return JSON.stringify(
typeof parsed === 'string' ? JSON.parse(parsed) : parsed,
null,
2,
)
} catch {
return storage.value
}
}
<ModalContent>
<Card className="min-w-96">
<CardHeader>
<ModalTitle>Storage Detail</ModalTitle>
</CardHeader>
return (
<div key={storage.key} className="flex items-center gap-2">
<div className="flex-1">Key: {storage.key}</div>
<CardContent>
<pre className="overflow-auto font-mono select-text">
{JSON.stringify(storage, null, 2)}
</pre>
</CardContent>
<Button
variant="stroked"
className="h-8 min-w-0 px-3"
onClick={() => handleRemoveItem(storage.key)}
>
Delete
</Button>
<CardFooter className="gap-2">
<ModalClose>{m.common_close()}</ModalClose>
</CardFooter>
</Card>
</ModalContent>
</Modal>
</div>
))}
<Modal>
<ModalTrigger asChild>
<Button variant="stroked" className="h-8 min-w-0 px-3">
Detail
</Button>
</ModalTrigger>
<ModalContent>
<Card className="min-w-96">
<CardHeader>
<ModalTitle>Storage Detail</ModalTitle>
</CardHeader>
<CardContent>
<pre className="max-h-[70vh] max-w-[80vw] overflow-auto font-mono text-wrap select-text">
{tryFmt()}
</pre>
</CardContent>
<CardFooter className="gap-2">
<ModalClose>{m.common_close()}</ModalClose>
</CardFooter>
</Card>
</ModalContent>
</Modal>
</div>
)
})}
</SettingsCardContent>
<SettingsCardFooter>
@@ -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 (
<SettingsCard data-slot="switch-legacy-card">
<SettingsCardContent
className="flex items-center justify-between px-2"
data-slot="switch-legacy-card-content"
>
<Card className="w-full space-y-4">
<CardHeader>Switch to Legacy UI</CardHeader>
<Modal>
<SettingsCardContent asChild>
<ModalTrigger asChild>
<Button className="text-on-surface! h-auto w-full rounded-none px-5 text-left text-base">
<ItemContainer>
<ItemLabel>
<ItemLabelText>Switch to Legacy UI</ItemLabelText>
</ItemLabel>
<CardFooter>
<Modal>
<ModalTrigger asChild>
<Button variant="stroked">Open</Button>
</ModalTrigger>
<div>
<ArrowForwardIosRounded />
</div>
</ItemContainer>
</Button>
</ModalTrigger>
</SettingsCardContent>
<ModalContent>
<Card className="w-96">
<CardHeader>
<ModalTitle>
Are you sure you want to switch to Legacy UI?
</ModalTitle>
</CardHeader>
<ModalContent>
<Card className="w-96">
<CardHeader>
<ModalTitle>
Are you sure you want to switch to Legacy UI?
</ModalTitle>
</CardHeader>
<CardContent>
<p>
Switching to Legacy UI will revert the UI to the original
design.
</p>
</CardContent>
<CardContent>
<p>
Switching to Legacy UI will revert the UI to the original
design.
</p>
</CardContent>
<CardFooter className="gap-2">
<Button variant="flat" onClick={handleClick}>
Continue
</Button>
<CardFooter className="gap-2">
<Button variant="flat" onClick={handleClick}>
Continue
</Button>
<ModalClose asChild>
<Button>Cancel</Button>
</ModalClose>
</CardFooter>
</Card>
</ModalContent>
</Modal>
</CardFooter>
</Card>
</SettingsCardContent>
<ModalClose asChild>
<Button>Cancel</Button>
</ModalClose>
</CardFooter>
</Card>
</ModalContent>
</Modal>
</SettingsCard>
)
}
@@ -70,9 +70,15 @@ function RouteComponent() {
<LanguageSettings />
<ThemeModeSettings />
</div>
{/* <SwitchLegacy /> */}
<div data-slot="switch-legacy-settings-container">
<SettingsLabel>Switch to Legacy UI</SettingsLabel>
<SettingsGroup>
<SwitchLegacy />
</SettingsGroup>
</div>
</div>
</>
)
}
@@ -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,
+3 -3
View File
@@ -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"
}
+45 -18
View File
@@ -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)
-182
View File
@@ -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)
-645
View File
@@ -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)
}
-643
View File
@@ -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)
}
-657
View File
@@ -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)
}
-884
View File
@@ -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 {
-883
View File
@@ -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 {
+20 -94
View File
@@ -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
+9 -9
View File
@@ -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: |
+93 -6
View File
@@ -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 {
@@ -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)
}
@@ -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}
}
@@ -0,0 +1,9 @@
//go:build go1.21
package contextutils
import "context"
func WithoutCancel(parent context.Context) context.Context {
return context.WithoutCancel(parent)
}
+7
View File
@@ -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)
+7 -7
View File
@@ -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,
}
+7 -2
View File
@@ -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)
}
})
}
})
+16
View File
@@ -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,
+51
View File
@@ -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)
}
+23 -1
View File
@@ -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
+293
View File
@@ -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[:])
}
+209
View File
@@ -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
}
+64
View File
@@ -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
}
+306
View File
@@ -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
}
+78
View File
@@ -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
}
@@ -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)))
@@ -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;
}
@@ -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;
}
@@ -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;
}
@@ -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))
+13 -4
View File
@@ -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" \
@@ -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))

Some files were not shown because too many files have changed in this diff Show More