mirror of
https://github.com/bolucat/Archive.git
synced 2026-04-22 16:07:49 +08:00
Update On Fri Mar 27 20:08:13 CET 2026
This commit is contained in:
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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: |
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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[:])
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
Vendored
+5
-1
@@ -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"
|
||||
|
||||
Generated
+3
-3
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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: 11↔12 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,
|
||||
},
|
||||
],
|
||||
}
|
||||
+67
@@ -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>
|
||||
)
|
||||
}
|
||||
+176
@@ -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
|
||||
}
|
||||
+39
@@ -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>
|
||||
)
|
||||
}
|
||||
+61
@@ -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>
|
||||
)
|
||||
}
|
||||
+111
@@ -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>
|
||||
)
|
||||
}
|
||||
+296
@@ -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>
|
||||
)
|
||||
}
|
||||
+237
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
+56
-27
@@ -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>
|
||||
|
||||
+50
-41
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
+8
-2
@@ -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,
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
Generated
+45
-18
@@ -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)
|
||||
|
||||
Vendored
-182
@@ -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)
|
||||
Vendored
-645
@@ -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)
|
||||
}
|
||||
Vendored
-643
@@ -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)
|
||||
}
|
||||
Vendored
-657
@@ -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)
|
||||
}
|
||||
Vendored
-884
@@ -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 {
|
||||
Vendored
-883
@@ -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 {
|
||||
Vendored
+20
-94
@@ -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
|
||||
|
||||
Vendored
+9
-9
@@ -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: |
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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[:])
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)))
|
||||
|
||||
+22
@@ -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;
|
||||
}
|
||||
|
||||
+22
@@ -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;
|
||||
}
|
||||
|
||||
+38
@@ -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))
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user