mirror of
https://github.com/MetaCubeX/Clash.Meta.git
synced 2025-04-18 08:20:53 +00:00
Merge branch 'Alpha' into Meta
This commit is contained in:
commit
48e481d0a2
54 changed files with 1256 additions and 311 deletions
54
.github/patch_go122/48042aa09c2f878c4faa576948b07fe625c4707a.diff
vendored
Normal file
54
.github/patch_go122/48042aa09c2f878c4faa576948b07fe625c4707a.diff
vendored
Normal file
|
@ -0,0 +1,54 @@
|
|||
diff --git a/src/syscall/exec_windows.go b/src/syscall/exec_windows.go
|
||||
index 06e684c7116b4..b311a5c74684b 100644
|
||||
--- a/src/syscall/exec_windows.go
|
||||
+++ b/src/syscall/exec_windows.go
|
||||
@@ -319,17 +319,6 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle
|
||||
}
|
||||
}
|
||||
|
||||
- var maj, min, build uint32
|
||||
- rtlGetNtVersionNumbers(&maj, &min, &build)
|
||||
- isWin7 := maj < 6 || (maj == 6 && min <= 1)
|
||||
- // NT kernel handles are divisible by 4, with the bottom 3 bits left as
|
||||
- // a tag. The fully set tag correlates with the types of handles we're
|
||||
- // concerned about here. Except, the kernel will interpret some
|
||||
- // special handle values, like -1, -2, and so forth, so kernelbase.dll
|
||||
- // checks to see that those bottom three bits are checked, but that top
|
||||
- // bit is not checked.
|
||||
- isLegacyWin7ConsoleHandle := func(handle Handle) bool { return isWin7 && handle&0x10000003 == 3 }
|
||||
-
|
||||
p, _ := GetCurrentProcess()
|
||||
parentProcess := p
|
||||
if sys.ParentProcess != 0 {
|
||||
@@ -338,15 +327,7 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle
|
||||
fd := make([]Handle, len(attr.Files))
|
||||
for i := range attr.Files {
|
||||
if attr.Files[i] > 0 {
|
||||
- destinationProcessHandle := parentProcess
|
||||
-
|
||||
- // On Windows 7, console handles aren't real handles, and can only be duplicated
|
||||
- // into the current process, not a parent one, which amounts to the same thing.
|
||||
- if parentProcess != p && isLegacyWin7ConsoleHandle(Handle(attr.Files[i])) {
|
||||
- destinationProcessHandle = p
|
||||
- }
|
||||
-
|
||||
- err := DuplicateHandle(p, Handle(attr.Files[i]), destinationProcessHandle, &fd[i], 0, true, DUPLICATE_SAME_ACCESS)
|
||||
+ err := DuplicateHandle(p, Handle(attr.Files[i]), parentProcess, &fd[i], 0, true, DUPLICATE_SAME_ACCESS)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
@@ -377,14 +358,6 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle
|
||||
|
||||
fd = append(fd, sys.AdditionalInheritedHandles...)
|
||||
|
||||
- // On Windows 7, console handles aren't real handles, so don't pass them
|
||||
- // through to PROC_THREAD_ATTRIBUTE_HANDLE_LIST.
|
||||
- for i := range fd {
|
||||
- if isLegacyWin7ConsoleHandle(fd[i]) {
|
||||
- fd[i] = 0
|
||||
- }
|
||||
- }
|
||||
-
|
||||
// The presence of a NULL handle in the list is enough to cause PROC_THREAD_ATTRIBUTE_HANDLE_LIST
|
||||
// to treat the entire list as empty, so remove NULL handles.
|
||||
j := 0
|
158
.github/patch_go122/693def151adff1af707d82d28f55dba81ceb08e1.diff
vendored
Normal file
158
.github/patch_go122/693def151adff1af707d82d28f55dba81ceb08e1.diff
vendored
Normal file
|
@ -0,0 +1,158 @@
|
|||
diff --git a/src/crypto/rand/rand.go b/src/crypto/rand/rand.go
|
||||
index 62738e2cb1a7d..d0dcc7cc71fc0 100644
|
||||
--- a/src/crypto/rand/rand.go
|
||||
+++ b/src/crypto/rand/rand.go
|
||||
@@ -15,7 +15,7 @@ import "io"
|
||||
// available, /dev/urandom otherwise.
|
||||
// On OpenBSD and macOS, Reader uses getentropy(2).
|
||||
// On other Unix-like systems, Reader reads from /dev/urandom.
|
||||
-// On Windows systems, Reader uses the RtlGenRandom API.
|
||||
+// On Windows systems, Reader uses the ProcessPrng API.
|
||||
// On JS/Wasm, Reader uses the Web Crypto API.
|
||||
// On WASIP1/Wasm, Reader uses random_get from wasi_snapshot_preview1.
|
||||
var Reader io.Reader
|
||||
diff --git a/src/crypto/rand/rand_windows.go b/src/crypto/rand/rand_windows.go
|
||||
index 6c0655c72b692..7380f1f0f1e6e 100644
|
||||
--- a/src/crypto/rand/rand_windows.go
|
||||
+++ b/src/crypto/rand/rand_windows.go
|
||||
@@ -15,11 +15,8 @@ func init() { Reader = &rngReader{} }
|
||||
|
||||
type rngReader struct{}
|
||||
|
||||
-func (r *rngReader) Read(b []byte) (n int, err error) {
|
||||
- // RtlGenRandom only returns 1<<32-1 bytes at a time. We only read at
|
||||
- // most 1<<31-1 bytes at a time so that this works the same on 32-bit
|
||||
- // and 64-bit systems.
|
||||
- if err := batched(windows.RtlGenRandom, 1<<31-1)(b); err != nil {
|
||||
+func (r *rngReader) Read(b []byte) (int, error) {
|
||||
+ if err := windows.ProcessPrng(b); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return len(b), nil
|
||||
diff --git a/src/internal/syscall/windows/syscall_windows.go b/src/internal/syscall/windows/syscall_windows.go
|
||||
index ab4ad2ec64108..5854ca60b5cef 100644
|
||||
--- a/src/internal/syscall/windows/syscall_windows.go
|
||||
+++ b/src/internal/syscall/windows/syscall_windows.go
|
||||
@@ -373,7 +373,7 @@ func ErrorLoadingGetTempPath2() error {
|
||||
//sys DestroyEnvironmentBlock(block *uint16) (err error) = userenv.DestroyEnvironmentBlock
|
||||
//sys CreateEvent(eventAttrs *SecurityAttributes, manualReset uint32, initialState uint32, name *uint16) (handle syscall.Handle, err error) = kernel32.CreateEventW
|
||||
|
||||
-//sys RtlGenRandom(buf []byte) (err error) = advapi32.SystemFunction036
|
||||
+//sys ProcessPrng(buf []byte) (err error) = bcryptprimitives.ProcessPrng
|
||||
|
||||
type FILE_ID_BOTH_DIR_INFO struct {
|
||||
NextEntryOffset uint32
|
||||
diff --git a/src/internal/syscall/windows/zsyscall_windows.go b/src/internal/syscall/windows/zsyscall_windows.go
|
||||
index e3f6d8d2a2208..5a587ad4f146c 100644
|
||||
--- a/src/internal/syscall/windows/zsyscall_windows.go
|
||||
+++ b/src/internal/syscall/windows/zsyscall_windows.go
|
||||
@@ -37,13 +37,14 @@ func errnoErr(e syscall.Errno) error {
|
||||
}
|
||||
|
||||
var (
|
||||
- modadvapi32 = syscall.NewLazyDLL(sysdll.Add("advapi32.dll"))
|
||||
- modiphlpapi = syscall.NewLazyDLL(sysdll.Add("iphlpapi.dll"))
|
||||
- modkernel32 = syscall.NewLazyDLL(sysdll.Add("kernel32.dll"))
|
||||
- modnetapi32 = syscall.NewLazyDLL(sysdll.Add("netapi32.dll"))
|
||||
- modpsapi = syscall.NewLazyDLL(sysdll.Add("psapi.dll"))
|
||||
- moduserenv = syscall.NewLazyDLL(sysdll.Add("userenv.dll"))
|
||||
- modws2_32 = syscall.NewLazyDLL(sysdll.Add("ws2_32.dll"))
|
||||
+ modadvapi32 = syscall.NewLazyDLL(sysdll.Add("advapi32.dll"))
|
||||
+ modbcryptprimitives = syscall.NewLazyDLL(sysdll.Add("bcryptprimitives.dll"))
|
||||
+ modiphlpapi = syscall.NewLazyDLL(sysdll.Add("iphlpapi.dll"))
|
||||
+ modkernel32 = syscall.NewLazyDLL(sysdll.Add("kernel32.dll"))
|
||||
+ modnetapi32 = syscall.NewLazyDLL(sysdll.Add("netapi32.dll"))
|
||||
+ modpsapi = syscall.NewLazyDLL(sysdll.Add("psapi.dll"))
|
||||
+ moduserenv = syscall.NewLazyDLL(sysdll.Add("userenv.dll"))
|
||||
+ modws2_32 = syscall.NewLazyDLL(sysdll.Add("ws2_32.dll"))
|
||||
|
||||
procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges")
|
||||
procDuplicateTokenEx = modadvapi32.NewProc("DuplicateTokenEx")
|
||||
@@ -55,7 +56,7 @@ var (
|
||||
procQueryServiceStatus = modadvapi32.NewProc("QueryServiceStatus")
|
||||
procRevertToSelf = modadvapi32.NewProc("RevertToSelf")
|
||||
procSetTokenInformation = modadvapi32.NewProc("SetTokenInformation")
|
||||
- procSystemFunction036 = modadvapi32.NewProc("SystemFunction036")
|
||||
+ procProcessPrng = modbcryptprimitives.NewProc("ProcessPrng")
|
||||
procGetAdaptersAddresses = modiphlpapi.NewProc("GetAdaptersAddresses")
|
||||
procCreateEventW = modkernel32.NewProc("CreateEventW")
|
||||
procGetACP = modkernel32.NewProc("GetACP")
|
||||
@@ -179,12 +180,12 @@ func SetTokenInformation(tokenHandle syscall.Token, tokenInformationClass uint32
|
||||
return
|
||||
}
|
||||
|
||||
-func RtlGenRandom(buf []byte) (err error) {
|
||||
+func ProcessPrng(buf []byte) (err error) {
|
||||
var _p0 *byte
|
||||
if len(buf) > 0 {
|
||||
_p0 = &buf[0]
|
||||
}
|
||||
- r1, _, e1 := syscall.Syscall(procSystemFunction036.Addr(), 2, uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), 0)
|
||||
+ r1, _, e1 := syscall.Syscall(procProcessPrng.Addr(), 2, uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go
|
||||
index 8ca8d7790909e..3772a864b2ff4 100644
|
||||
--- a/src/runtime/os_windows.go
|
||||
+++ b/src/runtime/os_windows.go
|
||||
@@ -127,15 +127,8 @@ var (
|
||||
_WriteFile,
|
||||
_ stdFunction
|
||||
|
||||
- // Use RtlGenRandom to generate cryptographically random data.
|
||||
- // This approach has been recommended by Microsoft (see issue
|
||||
- // 15589 for details).
|
||||
- // The RtlGenRandom is not listed in advapi32.dll, instead
|
||||
- // RtlGenRandom function can be found by searching for SystemFunction036.
|
||||
- // Also some versions of Mingw cannot link to SystemFunction036
|
||||
- // when building executable as Cgo. So load SystemFunction036
|
||||
- // manually during runtime startup.
|
||||
- _RtlGenRandom stdFunction
|
||||
+ // Use ProcessPrng to generate cryptographically random data.
|
||||
+ _ProcessPrng stdFunction
|
||||
|
||||
// Load ntdll.dll manually during startup, otherwise Mingw
|
||||
// links wrong printf function to cgo executable (see issue
|
||||
@@ -151,11 +144,11 @@ var (
|
||||
)
|
||||
|
||||
var (
|
||||
- advapi32dll = [...]uint16{'a', 'd', 'v', 'a', 'p', 'i', '3', '2', '.', 'd', 'l', 'l', 0}
|
||||
- ntdlldll = [...]uint16{'n', 't', 'd', 'l', 'l', '.', 'd', 'l', 'l', 0}
|
||||
- powrprofdll = [...]uint16{'p', 'o', 'w', 'r', 'p', 'r', 'o', 'f', '.', 'd', 'l', 'l', 0}
|
||||
- winmmdll = [...]uint16{'w', 'i', 'n', 'm', 'm', '.', 'd', 'l', 'l', 0}
|
||||
- ws2_32dll = [...]uint16{'w', 's', '2', '_', '3', '2', '.', 'd', 'l', 'l', 0}
|
||||
+ bcryptprimitivesdll = [...]uint16{'b', 'c', 'r', 'y', 'p', 't', 'p', 'r', 'i', 'm', 'i', 't', 'i', 'v', 'e', 's', '.', 'd', 'l', 'l', 0}
|
||||
+ ntdlldll = [...]uint16{'n', 't', 'd', 'l', 'l', '.', 'd', 'l', 'l', 0}
|
||||
+ powrprofdll = [...]uint16{'p', 'o', 'w', 'r', 'p', 'r', 'o', 'f', '.', 'd', 'l', 'l', 0}
|
||||
+ winmmdll = [...]uint16{'w', 'i', 'n', 'm', 'm', '.', 'd', 'l', 'l', 0}
|
||||
+ ws2_32dll = [...]uint16{'w', 's', '2', '_', '3', '2', '.', 'd', 'l', 'l', 0}
|
||||
)
|
||||
|
||||
// Function to be called by windows CreateThread
|
||||
@@ -251,11 +244,11 @@ func windowsLoadSystemLib(name []uint16) uintptr {
|
||||
}
|
||||
|
||||
func loadOptionalSyscalls() {
|
||||
- a32 := windowsLoadSystemLib(advapi32dll[:])
|
||||
- if a32 == 0 {
|
||||
- throw("advapi32.dll not found")
|
||||
+ bcryptPrimitives := windowsLoadSystemLib(bcryptprimitivesdll[:])
|
||||
+ if bcryptPrimitives == 0 {
|
||||
+ throw("bcryptprimitives.dll not found")
|
||||
}
|
||||
- _RtlGenRandom = windowsFindfunc(a32, []byte("SystemFunction036\000"))
|
||||
+ _ProcessPrng = windowsFindfunc(bcryptPrimitives, []byte("ProcessPrng\000"))
|
||||
|
||||
n32 := windowsLoadSystemLib(ntdlldll[:])
|
||||
if n32 == 0 {
|
||||
@@ -531,7 +524,7 @@ func osinit() {
|
||||
//go:nosplit
|
||||
func readRandom(r []byte) int {
|
||||
n := 0
|
||||
- if stdcall2(_RtlGenRandom, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 {
|
||||
+ if stdcall2(_ProcessPrng, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 {
|
||||
n = len(r)
|
||||
}
|
||||
return n
|
162
.github/patch_go122/7c1157f9544922e96945196b47b95664b1e39108.diff
vendored
Normal file
162
.github/patch_go122/7c1157f9544922e96945196b47b95664b1e39108.diff
vendored
Normal file
|
@ -0,0 +1,162 @@
|
|||
diff --git a/src/net/hook_windows.go b/src/net/hook_windows.go
|
||||
index ab8656cbbf343..28c49cc6de7e7 100644
|
||||
--- a/src/net/hook_windows.go
|
||||
+++ b/src/net/hook_windows.go
|
||||
@@ -14,7 +14,6 @@ var (
|
||||
testHookDialChannel = func() { time.Sleep(time.Millisecond) } // see golang.org/issue/5349
|
||||
|
||||
// Placeholders for socket system calls.
|
||||
- socketFunc func(int, int, int) (syscall.Handle, error) = syscall.Socket
|
||||
wsaSocketFunc func(int32, int32, int32, *syscall.WSAProtocolInfo, uint32, uint32) (syscall.Handle, error) = windows.WSASocket
|
||||
connectFunc func(syscall.Handle, syscall.Sockaddr) error = syscall.Connect
|
||||
listenFunc func(syscall.Handle, int) error = syscall.Listen
|
||||
diff --git a/src/net/internal/socktest/main_test.go b/src/net/internal/socktest/main_test.go
|
||||
index 0197feb3f199a..967ce6795aedb 100644
|
||||
--- a/src/net/internal/socktest/main_test.go
|
||||
+++ b/src/net/internal/socktest/main_test.go
|
||||
@@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
-//go:build !js && !plan9 && !wasip1
|
||||
+//go:build !js && !plan9 && !wasip1 && !windows
|
||||
|
||||
package socktest_test
|
||||
|
||||
diff --git a/src/net/internal/socktest/main_windows_test.go b/src/net/internal/socktest/main_windows_test.go
|
||||
deleted file mode 100644
|
||||
index df1cb97784b51..0000000000000
|
||||
--- a/src/net/internal/socktest/main_windows_test.go
|
||||
+++ /dev/null
|
||||
@@ -1,22 +0,0 @@
|
||||
-// Copyright 2015 The Go Authors. All rights reserved.
|
||||
-// Use of this source code is governed by a BSD-style
|
||||
-// license that can be found in the LICENSE file.
|
||||
-
|
||||
-package socktest_test
|
||||
-
|
||||
-import "syscall"
|
||||
-
|
||||
-var (
|
||||
- socketFunc func(int, int, int) (syscall.Handle, error)
|
||||
- closeFunc func(syscall.Handle) error
|
||||
-)
|
||||
-
|
||||
-func installTestHooks() {
|
||||
- socketFunc = sw.Socket
|
||||
- closeFunc = sw.Closesocket
|
||||
-}
|
||||
-
|
||||
-func uninstallTestHooks() {
|
||||
- socketFunc = syscall.Socket
|
||||
- closeFunc = syscall.Closesocket
|
||||
-}
|
||||
diff --git a/src/net/internal/socktest/sys_windows.go b/src/net/internal/socktest/sys_windows.go
|
||||
index 8c1c862f33c9b..1c42e5c7f34b7 100644
|
||||
--- a/src/net/internal/socktest/sys_windows.go
|
||||
+++ b/src/net/internal/socktest/sys_windows.go
|
||||
@@ -9,38 +9,6 @@ import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
-// Socket wraps syscall.Socket.
|
||||
-func (sw *Switch) Socket(family, sotype, proto int) (s syscall.Handle, err error) {
|
||||
- sw.once.Do(sw.init)
|
||||
-
|
||||
- so := &Status{Cookie: cookie(family, sotype, proto)}
|
||||
- sw.fmu.RLock()
|
||||
- f, _ := sw.fltab[FilterSocket]
|
||||
- sw.fmu.RUnlock()
|
||||
-
|
||||
- af, err := f.apply(so)
|
||||
- if err != nil {
|
||||
- return syscall.InvalidHandle, err
|
||||
- }
|
||||
- s, so.Err = syscall.Socket(family, sotype, proto)
|
||||
- if err = af.apply(so); err != nil {
|
||||
- if so.Err == nil {
|
||||
- syscall.Closesocket(s)
|
||||
- }
|
||||
- return syscall.InvalidHandle, err
|
||||
- }
|
||||
-
|
||||
- sw.smu.Lock()
|
||||
- defer sw.smu.Unlock()
|
||||
- if so.Err != nil {
|
||||
- sw.stats.getLocked(so.Cookie).OpenFailed++
|
||||
- return syscall.InvalidHandle, so.Err
|
||||
- }
|
||||
- nso := sw.addLocked(s, family, sotype, proto)
|
||||
- sw.stats.getLocked(nso.Cookie).Opened++
|
||||
- return s, nil
|
||||
-}
|
||||
-
|
||||
// WSASocket wraps [syscall.WSASocket].
|
||||
func (sw *Switch) WSASocket(family, sotype, proto int32, protinfo *syscall.WSAProtocolInfo, group uint32, flags uint32) (s syscall.Handle, err error) {
|
||||
sw.once.Do(sw.init)
|
||||
diff --git a/src/net/main_windows_test.go b/src/net/main_windows_test.go
|
||||
index 07f21b72eb1fc..bc024c0bbd82d 100644
|
||||
--- a/src/net/main_windows_test.go
|
||||
+++ b/src/net/main_windows_test.go
|
||||
@@ -8,7 +8,6 @@ import "internal/poll"
|
||||
|
||||
var (
|
||||
// Placeholders for saving original socket system calls.
|
||||
- origSocket = socketFunc
|
||||
origWSASocket = wsaSocketFunc
|
||||
origClosesocket = poll.CloseFunc
|
||||
origConnect = connectFunc
|
||||
@@ -18,7 +17,6 @@ var (
|
||||
)
|
||||
|
||||
func installTestHooks() {
|
||||
- socketFunc = sw.Socket
|
||||
wsaSocketFunc = sw.WSASocket
|
||||
poll.CloseFunc = sw.Closesocket
|
||||
connectFunc = sw.Connect
|
||||
@@ -28,7 +26,6 @@ func installTestHooks() {
|
||||
}
|
||||
|
||||
func uninstallTestHooks() {
|
||||
- socketFunc = origSocket
|
||||
wsaSocketFunc = origWSASocket
|
||||
poll.CloseFunc = origClosesocket
|
||||
connectFunc = origConnect
|
||||
diff --git a/src/net/sock_windows.go b/src/net/sock_windows.go
|
||||
index fa11c7af2e727..5540135a2c43e 100644
|
||||
--- a/src/net/sock_windows.go
|
||||
+++ b/src/net/sock_windows.go
|
||||
@@ -19,21 +19,6 @@ func maxListenerBacklog() int {
|
||||
func sysSocket(family, sotype, proto int) (syscall.Handle, error) {
|
||||
s, err := wsaSocketFunc(int32(family), int32(sotype), int32(proto),
|
||||
nil, 0, windows.WSA_FLAG_OVERLAPPED|windows.WSA_FLAG_NO_HANDLE_INHERIT)
|
||||
- if err == nil {
|
||||
- return s, nil
|
||||
- }
|
||||
- // WSA_FLAG_NO_HANDLE_INHERIT flag is not supported on some
|
||||
- // old versions of Windows, see
|
||||
- // https://msdn.microsoft.com/en-us/library/windows/desktop/ms742212(v=vs.85).aspx
|
||||
- // for details. Just use syscall.Socket, if windows.WSASocket failed.
|
||||
-
|
||||
- // See ../syscall/exec_unix.go for description of ForkLock.
|
||||
- syscall.ForkLock.RLock()
|
||||
- s, err = socketFunc(family, sotype, proto)
|
||||
- if err == nil {
|
||||
- syscall.CloseOnExec(s)
|
||||
- }
|
||||
- syscall.ForkLock.RUnlock()
|
||||
if err != nil {
|
||||
return syscall.InvalidHandle, os.NewSyscallError("socket", err)
|
||||
}
|
||||
diff --git a/src/syscall/exec_windows.go b/src/syscall/exec_windows.go
|
||||
index 0a93bc0a80d4e..06e684c7116b4 100644
|
||||
--- a/src/syscall/exec_windows.go
|
||||
+++ b/src/syscall/exec_windows.go
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
+// ForkLock is not used on Windows.
|
||||
var ForkLock sync.RWMutex
|
||||
|
||||
// EscapeArg rewrites command line argument s as prescribed
|
153
.github/workflows/build.yml
vendored
153
.github/workflows/build.yml
vendored
|
@ -1,6 +1,10 @@
|
|||
name: Build
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: "Tag version to release"
|
||||
required: true
|
||||
push:
|
||||
paths-ignore:
|
||||
- "docs/**"
|
||||
|
@ -13,7 +17,6 @@ on:
|
|||
pull_request_target:
|
||||
branches:
|
||||
- Alpha
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
@ -64,6 +67,13 @@ jobs:
|
|||
- { goos: android, goarch: arm, ndk: armv7a-linux-androideabi34, output: armv7 }
|
||||
- { goos: android, goarch: arm64, ndk: aarch64-linux-android34, output: arm64-v8 }
|
||||
|
||||
# Go 1.21 can revert commit `9e4385` to work on Windows 7
|
||||
# https://github.com/golang/go/issues/64622#issuecomment-1847475161
|
||||
# (OR we can just use golang1.21.4 which unneeded any patch)
|
||||
- { goos: windows, goarch: '386', output: '386-go121', goversion: '1.21' }
|
||||
- { goos: windows, goarch: amd64, goamd64: v1, output: amd64-compatible-go121, goversion: '1.21' }
|
||||
- { goos: windows, goarch: amd64, goamd64: v3, output: amd64-go121, goversion: '1.21' }
|
||||
|
||||
# Go 1.20 is the last release that will run on any release of Windows 7, 8, Server 2008 and Server 2012. Go 1.21 will require at least Windows 10 or Server 2016.
|
||||
- { goos: windows, goarch: '386', output: '386-go120', goversion: '1.20' }
|
||||
- { goos: windows, goarch: amd64, goamd64: v1, output: amd64-compatible-go120, goversion: '1.20' }
|
||||
|
@ -94,28 +104,50 @@ jobs:
|
|||
with:
|
||||
go-version: ${{ matrix.jobs.goversion }}
|
||||
|
||||
- name: Set up Go1.21 loongarch abi1
|
||||
- name: Set up Go1.22 loongarch abi1
|
||||
if: ${{ matrix.jobs.goarch == 'loong64' && matrix.jobs.abi == '1' }}
|
||||
run: |
|
||||
wget -q https://github.com/xishang0128/loongarch64-golang/releases/download/1.21.5/go1.21.5.linux-amd64-abi1.tar.gz
|
||||
sudo tar zxf go1.21.5.linux-amd64-abi1.tar.gz -C /usr/local
|
||||
wget -q https://github.com/xishang0128/loongarch64-golang/releases/download/1.22.0/go1.22.0.linux-amd64-abi1.tar.gz
|
||||
sudo tar zxf go1.22.0.linux-amd64-abi1.tar.gz -C /usr/local
|
||||
echo "/usr/local/go/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Set up Go1.21 loongarch abi2
|
||||
- name: Set up Go1.22 loongarch abi2
|
||||
if: ${{ matrix.jobs.goarch == 'loong64' && matrix.jobs.abi == '2' }}
|
||||
run: |
|
||||
wget -q https://github.com/xishang0128/loongarch64-golang/releases/download/1.21.5/go1.21.5.linux-amd64-abi2.tar.gz
|
||||
sudo tar zxf go1.21.5.linux-amd64-abi2.tar.gz -C /usr/local
|
||||
wget -q https://github.com/xishang0128/loongarch64-golang/releases/download/1.22.0/go1.22.0.linux-amd64-abi2.tar.gz
|
||||
sudo tar zxf go1.22.0.linux-amd64-abi2.tar.gz -C /usr/local
|
||||
echo "/usr/local/go/bin" >> $GITHUB_PATH
|
||||
|
||||
# modify from https://github.com/restic/restic/issues/4636#issuecomment-1896455557
|
||||
# this patch file only works on golang1.22.x
|
||||
# that means after golang1.23 release it must be changed
|
||||
# revert:
|
||||
# 693def151adff1af707d82d28f55dba81ceb08e1: "crypto/rand,runtime: switch RtlGenRandom for ProcessPrng"
|
||||
# 7c1157f9544922e96945196b47b95664b1e39108: "net: remove sysSocket fallback for Windows 7"
|
||||
# 48042aa09c2f878c4faa576948b07fe625c4707a: "syscall: remove Windows 7 console handle workaround"
|
||||
- name: Revert Golang1.22 commit for Windows7/8
|
||||
if: ${{ matrix.jobs.goos == 'windows' && matrix.jobs.goversion == '' }}
|
||||
run: |
|
||||
cd $(go env GOROOT)
|
||||
patch --verbose -R -p 1 < $GITHUB_WORKSPACE/.github/patch_go122/693def151adff1af707d82d28f55dba81ceb08e1.diff
|
||||
patch --verbose -R -p 1 < $GITHUB_WORKSPACE/.github/patch_go122/7c1157f9544922e96945196b47b95664b1e39108.diff
|
||||
patch --verbose -R -p 1 < $GITHUB_WORKSPACE/.github/patch_go122/48042aa09c2f878c4faa576948b07fe625c4707a.diff
|
||||
|
||||
# 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)
|
||||
curl https://github.com/golang/go/commit/9e43850a3298a9b8b1162ba0033d4c53f8637571.diff | patch --verbose -R -p 1
|
||||
|
||||
- name: Set variables
|
||||
if: ${{github.ref_name=='Alpha'}}
|
||||
run: echo "VERSION=alpha-$(git rev-parse --short HEAD)" >> $GITHUB_ENV
|
||||
if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.version != '' }}
|
||||
run: echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV
|
||||
shell: bash
|
||||
|
||||
- name: Set variables
|
||||
if: ${{github.ref_name=='' || github.ref_type=='tag'}}
|
||||
run: echo "VERSION=$(git describe --tags)" >> $GITHUB_ENV
|
||||
if: ${{ github.event_name != 'workflow_dispatch' && github.ref_name == 'Alpha' }}
|
||||
run: echo "VERSION=alpha-$(git rev-parse --short HEAD)" >> $GITHUB_ENV
|
||||
shell: bash
|
||||
|
||||
- name: Set Time Variable
|
||||
|
@ -248,7 +280,7 @@ jobs:
|
|||
|
||||
Upload-Prerelease:
|
||||
permissions: write-all
|
||||
if: ${{ github.ref_type == 'branch' && !startsWith(github.event_name, 'pull_request') }}
|
||||
if: ${{ github.event_name != 'workflow_dispatch' && github.ref_type == 'branch' && !startsWith(github.event_name, 'pull_request') }}
|
||||
needs: [build]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
@ -299,44 +331,62 @@ jobs:
|
|||
|
||||
Upload-Release:
|
||||
permissions: write-all
|
||||
if: ${{ github.ref_type=='tag' }}
|
||||
if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.version != '' }}
|
||||
needs: [build]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: Meta
|
||||
fetch-depth: '0'
|
||||
fetch-tags: 'true'
|
||||
|
||||
- name: Get tags
|
||||
run: |
|
||||
echo "CURRENTVERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
|
||||
git fetch --tags
|
||||
echo "PREVERSION=$(git describe --tags --abbrev=0 HEAD^)" >> $GITHUB_ENV
|
||||
- name: Get tags
|
||||
run: |
|
||||
echo "CURRENTVERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV
|
||||
git fetch --tags
|
||||
echo "PREVERSION=$(git describe --tags --abbrev=0 HEAD)" >> $GITHUB_ENV
|
||||
|
||||
- name: Generate release notes
|
||||
run: |
|
||||
cp ./.github/genReleaseNote.sh ./
|
||||
bash ./genReleaseNote.sh -v ${PREVERSION}...${CURRENTVERSION}
|
||||
rm ./genReleaseNote.sh
|
||||
- name: Merge Alpha branch into Meta
|
||||
run: |
|
||||
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git config --global user.name "github-actions[bot]"
|
||||
git fetch origin Alpha:Alpha
|
||||
git merge Alpha
|
||||
git push origin Meta
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: bin/
|
||||
merge-multiple: true
|
||||
- name: Tag the commit
|
||||
run: |
|
||||
git tag ${{ github.event.inputs.version }}
|
||||
git push origin ${{ github.event.inputs.version }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Display structure of downloaded files
|
||||
run: ls -R
|
||||
working-directory: bin
|
||||
|
||||
- name: Upload Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
if: ${{ success() }}
|
||||
with:
|
||||
tag_name: ${{ github.ref_name }}
|
||||
files: bin/*
|
||||
generate_release_notes: true
|
||||
body_path: release.md
|
||||
- name: Generate release notes
|
||||
run: |
|
||||
cp ./.github/genReleaseNote.sh ./
|
||||
bash ./genReleaseNote.sh -v ${PREVERSION}...${CURRENTVERSION}
|
||||
rm ./genReleaseNote.sh
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: bin/
|
||||
merge-multiple: true
|
||||
|
||||
- name: Display structure of downloaded files
|
||||
run: ls -R
|
||||
working-directory: bin
|
||||
|
||||
- name: Upload Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
if: ${{ success() }}
|
||||
with:
|
||||
tag_name: ${{ github.event.inputs.version }}
|
||||
files: bin/*
|
||||
body_path: release.md
|
||||
|
||||
Docker:
|
||||
if: ${{ !startsWith(github.event_name, 'pull_request') }}
|
||||
|
@ -366,6 +416,18 @@ jobs:
|
|||
with:
|
||||
version: latest
|
||||
|
||||
- name: Set Docker tags and labels based on trigger
|
||||
id: set-meta
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||
echo "tags=${{ github.event.inputs.version }}" >> $GITHUB_ENV
|
||||
echo "labels=org.opencontainers.image.version=${{ github.event.inputs.version }}" >> $GITHUB_ENV
|
||||
else
|
||||
echo "tags=${{ steps.meta.outputs.tags }}" >> $GITHUB_ENV
|
||||
echo "labels=${{ steps.meta.outputs.labels }}" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
|
||||
# Extract metadata (tags, labels) for Docker
|
||||
# https://github.com/docker/metadata-action
|
||||
- name: Extract Docker metadata
|
||||
|
@ -373,6 +435,9 @@ jobs:
|
|||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ github.repository }}
|
||||
tags: ${{ env.tags }}
|
||||
labels: ${{ env.labels }}
|
||||
|
||||
|
||||
- name: Show files
|
||||
run: |
|
||||
|
@ -401,4 +466,4 @@ jobs:
|
|||
linux/arm64
|
||||
linux/arm/v7
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
|
@ -98,4 +98,3 @@ API.
|
|||
|
||||
This software is released under the GPL-3.0 license.
|
||||
|
||||
[](https://app.fossa.io/projects/git%2Bgithub.com%2FMetaCubeX%2Fmihomo?ref=badge_large)
|
||||
|
|
|
@ -2,6 +2,7 @@ package adapter
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
|
@ -14,6 +15,7 @@ import (
|
|||
"github.com/metacubex/mihomo/common/atomic"
|
||||
"github.com/metacubex/mihomo/common/queue"
|
||||
"github.com/metacubex/mihomo/common/utils"
|
||||
"github.com/metacubex/mihomo/component/ca"
|
||||
"github.com/metacubex/mihomo/component/dialer"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
"github.com/puzpuzpuz/xsync/v3"
|
||||
|
@ -230,6 +232,7 @@ func (p *Proxy) URLTest(ctx context.Context, url string, expectedStatus utils.In
|
|||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
TLSClientConfig: ca.GetGlobalTLSConfig(&tls.Config{}),
|
||||
}
|
||||
|
||||
client := http.Client{
|
||||
|
|
|
@ -166,12 +166,6 @@ func (ss *ShadowSocks) ListenPacketContext(ctx context.Context, metadata *C.Meta
|
|||
|
||||
// ListenPacketWithDialer implements C.ProxyAdapter
|
||||
func (ss *ShadowSocks) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.PacketConn, err error) {
|
||||
if len(ss.option.DialerProxy) > 0 {
|
||||
dialer, err = proxydialer.NewByName(ss.option.DialerProxy, dialer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if ss.option.UDPOverTCP {
|
||||
tcpConn, err := ss.DialContextWithDialer(ctx, dialer, metadata)
|
||||
if err != nil {
|
||||
|
@ -179,6 +173,12 @@ func (ss *ShadowSocks) ListenPacketWithDialer(ctx context.Context, dialer C.Dial
|
|||
}
|
||||
return ss.ListenPacketOnStreamConn(ctx, tcpConn, metadata)
|
||||
}
|
||||
if len(ss.option.DialerProxy) > 0 {
|
||||
dialer, err = proxydialer.NewByName(ss.option.DialerProxy, dialer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
addr, err := resolveUDPAddrWithPrefer(ctx, "udp", ss.addr, ss.prefer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -273,6 +273,7 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) {
|
|||
if opts.TLS {
|
||||
v2rayOption.TLS = true
|
||||
v2rayOption.SkipCertVerify = opts.SkipCertVerify
|
||||
v2rayOption.Fingerprint = opts.Fingerprint
|
||||
}
|
||||
} else if option.Plugin == shadowtls.Mode {
|
||||
obfsMode = shadowtls.Mode
|
||||
|
|
|
@ -179,6 +179,7 @@ func (v *Vmess) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.M
|
|||
tlsOpts := mihomoVMess.TLSConfig{
|
||||
Host: host,
|
||||
SkipCertVerify: v.option.SkipCertVerify,
|
||||
FingerPrint: v.option.Fingerprint,
|
||||
NextProtos: []string{"h2"},
|
||||
ClientFingerprint: v.option.ClientFingerprint,
|
||||
Reality: v.realityConfig,
|
||||
|
@ -208,6 +209,7 @@ func (v *Vmess) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.M
|
|||
tlsOpts := &mihomoVMess.TLSConfig{
|
||||
Host: host,
|
||||
SkipCertVerify: v.option.SkipCertVerify,
|
||||
FingerPrint: v.option.Fingerprint,
|
||||
ClientFingerprint: v.option.ClientFingerprint,
|
||||
Reality: v.realityConfig,
|
||||
NextProtos: v.option.ALPN,
|
||||
|
|
|
@ -48,7 +48,7 @@ type GroupBaseOption struct {
|
|||
func NewGroupBase(opt GroupBaseOption) *GroupBase {
|
||||
var excludeFilterReg *regexp2.Regexp
|
||||
if opt.excludeFilter != "" {
|
||||
excludeFilterReg = regexp2.MustCompile(opt.excludeFilter, 0)
|
||||
excludeFilterReg = regexp2.MustCompile(opt.excludeFilter, regexp2.None)
|
||||
}
|
||||
var excludeTypeArray []string
|
||||
if opt.excludeType != "" {
|
||||
|
@ -58,7 +58,7 @@ func NewGroupBase(opt GroupBaseOption) *GroupBase {
|
|||
var filterRegs []*regexp2.Regexp
|
||||
if opt.filter != "" {
|
||||
for _, filter := range strings.Split(opt.filter, "`") {
|
||||
filterReg := regexp2.MustCompile(filter, 0)
|
||||
filterReg := regexp2.MustCompile(filter, regexp2.None)
|
||||
filterRegs = append(filterRegs, filterReg)
|
||||
}
|
||||
}
|
||||
|
@ -126,7 +126,7 @@ func (gb *GroupBase) GetProxies(touch bool) []C.Proxy {
|
|||
for _, filterReg := range gb.filterRegs {
|
||||
for _, p := range proxies {
|
||||
name := p.Name()
|
||||
if mat, _ := filterReg.FindStringMatch(name); mat != nil {
|
||||
if mat, _ := filterReg.MatchString(name); mat {
|
||||
if _, ok := proxiesSet[name]; !ok {
|
||||
proxiesSet[name] = struct{}{}
|
||||
newProxies = append(newProxies, p)
|
||||
|
@ -150,7 +150,7 @@ func (gb *GroupBase) GetProxies(touch bool) []C.Proxy {
|
|||
for _, filterReg := range gb.filterRegs {
|
||||
for _, p := range proxies {
|
||||
name := p.Name()
|
||||
if mat, _ := filterReg.FindStringMatch(name); mat != nil {
|
||||
if mat, _ := filterReg.MatchString(name); mat {
|
||||
if _, ok := proxiesSet[name]; !ok {
|
||||
proxiesSet[name] = struct{}{}
|
||||
newProxies = append(newProxies, p)
|
||||
|
@ -191,7 +191,7 @@ func (gb *GroupBase) GetProxies(touch bool) []C.Proxy {
|
|||
var newProxies []C.Proxy
|
||||
for _, p := range proxies {
|
||||
name := p.Name()
|
||||
if mat, _ := gb.excludeFilterReg.FindStringMatch(name); mat != nil {
|
||||
if mat, _ := gb.excludeFilterReg.MatchString(name); mat {
|
||||
continue
|
||||
}
|
||||
newProxies = append(newProxies, p)
|
||||
|
|
|
@ -5,6 +5,8 @@ import (
|
|||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/dlclark/regexp2"
|
||||
|
||||
"github.com/metacubex/mihomo/adapter/outbound"
|
||||
"github.com/metacubex/mihomo/adapter/provider"
|
||||
"github.com/metacubex/mihomo/common/structure"
|
||||
|
@ -70,7 +72,22 @@ func ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, provide
|
|||
groupOption.Use = append(groupOption.Use, AllProviders...)
|
||||
}
|
||||
if groupOption.IncludeAllProxies {
|
||||
groupOption.Proxies = append(groupOption.Proxies, AllProxies...)
|
||||
if groupOption.Filter != "" {
|
||||
var filterRegs []*regexp2.Regexp
|
||||
for _, filter := range strings.Split(groupOption.Filter, "`") {
|
||||
filterReg := regexp2.MustCompile(filter, regexp2.None)
|
||||
filterRegs = append(filterRegs, filterReg)
|
||||
}
|
||||
for _, p := range AllProxies {
|
||||
for _, filterReg := range filterRegs {
|
||||
if mat, _ := filterReg.MatchString(p); mat {
|
||||
groupOption.Proxies = append(groupOption.Proxies, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
groupOption.Proxies = append(groupOption.Proxies, AllProxies...)
|
||||
}
|
||||
}
|
||||
|
||||
if len(groupOption.Proxies) == 0 && len(groupOption.Use) == 0 {
|
||||
|
|
|
@ -181,14 +181,14 @@ func (hc *HealthCheck) execute(b *batch.Batch[bool], url, uid string, option *ex
|
|||
filters = append(filters, filter)
|
||||
}
|
||||
|
||||
filterReg = regexp2.MustCompile(strings.Join(filters, "|"), 0)
|
||||
filterReg = regexp2.MustCompile(strings.Join(filters, "|"), regexp2.None)
|
||||
}
|
||||
}
|
||||
|
||||
for _, proxy := range hc.proxies {
|
||||
// skip proxies that do not require health check
|
||||
if filterReg != nil {
|
||||
if match, _ := filterReg.FindStringMatch(proxy.Name()); match == nil {
|
||||
if match, _ := filterReg.MatchString(proxy.Name()); !match {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
|
|
@ -169,7 +169,7 @@ func stopProxyProvider(pd *ProxySetProvider) {
|
|||
}
|
||||
|
||||
func NewProxySetProvider(name string, interval time.Duration, filter string, excludeFilter string, excludeType string, dialerProxy string, override OverrideSchema, vehicle types.Vehicle, hc *HealthCheck) (*ProxySetProvider, error) {
|
||||
excludeFilterReg, err := regexp2.Compile(excludeFilter, 0)
|
||||
excludeFilterReg, err := regexp2.Compile(excludeFilter, regexp2.None)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid excludeFilter regex: %w", err)
|
||||
}
|
||||
|
@ -180,7 +180,7 @@ func NewProxySetProvider(name string, interval time.Duration, filter string, exc
|
|||
|
||||
var filterRegs []*regexp2.Regexp
|
||||
for _, filter := range strings.Split(filter, "`") {
|
||||
filterReg, err := regexp2.Compile(filter, 0)
|
||||
filterReg, err := regexp2.Compile(filter, regexp2.None)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid filter regex: %w", err)
|
||||
}
|
||||
|
@ -356,12 +356,12 @@ func proxiesParseAndFilter(filter string, excludeFilter string, excludeTypeArray
|
|||
continue
|
||||
}
|
||||
if len(excludeFilter) > 0 {
|
||||
if mat, _ := excludeFilterReg.FindStringMatch(name); mat != nil {
|
||||
if mat, _ := excludeFilterReg.MatchString(name); mat {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if len(filter) > 0 {
|
||||
if mat, _ := filterReg.FindStringMatch(name); mat == nil {
|
||||
if mat, _ := filterReg.MatchString(name); !mat {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,9 +67,6 @@ func ResetCertificate() {
|
|||
}
|
||||
|
||||
func getCertPool() *x509.CertPool {
|
||||
if len(trustCerts) == 0 {
|
||||
return nil
|
||||
}
|
||||
if globalCertPool == nil {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
|
14
component/ca/fix_windows.go
Normal file
14
component/ca/fix_windows.go
Normal file
|
@ -0,0 +1,14 @@
|
|||
package ca
|
||||
|
||||
import (
|
||||
"github.com/metacubex/mihomo/constant/features"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// crypto/x509: certificate validation in Windows fails to validate IP in SAN
|
||||
// https://github.com/golang/go/issues/37176
|
||||
// As far as I can tell this is still the case on most older versions of Windows (but seems to be fixed in 10)
|
||||
if features.WindowsMajorVersion < 10 && len(_CaCertificates) > 0 {
|
||||
DisableSystemCa = true
|
||||
}
|
||||
}
|
|
@ -164,7 +164,7 @@ func dialContext(ctx context.Context, network string, destination netip.Addr, po
|
|||
if opt.mpTcp {
|
||||
setMultiPathTCP(dialer)
|
||||
}
|
||||
if opt.tfo {
|
||||
if opt.tfo && !DisableTFO {
|
||||
return dialTFO(ctx, *dialer, network, address)
|
||||
}
|
||||
return dialer.DialContext(ctx, network, address)
|
||||
|
|
|
@ -9,6 +9,8 @@ import (
|
|||
"github.com/metacubex/tfo-go"
|
||||
)
|
||||
|
||||
var DisableTFO = false
|
||||
|
||||
type tfoConn struct {
|
||||
net.Conn
|
||||
closed bool
|
||||
|
|
11
component/dialer/tfo_windows.go
Normal file
11
component/dialer/tfo_windows.go
Normal file
|
@ -0,0 +1,11 @@
|
|||
package dialer
|
||||
|
||||
import "github.com/metacubex/mihomo/constant/features"
|
||||
|
||||
func init() {
|
||||
// According to MSDN, this option is available since Windows 10, 1607
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms738596(v=vs.85).aspx
|
||||
if features.WindowsMajorVersion < 10 || (features.WindowsMajorVersion == 10 && features.WindowsBuildNumber < 14393) {
|
||||
DisableTFO = true
|
||||
}
|
||||
}
|
|
@ -9,11 +9,15 @@ import (
|
|||
_ "unsafe"
|
||||
|
||||
"github.com/metacubex/mihomo/common/utils"
|
||||
"github.com/metacubex/mihomo/component/resolver/hosts"
|
||||
"github.com/metacubex/mihomo/component/trie"
|
||||
"github.com/zhangyunhao116/fastrand"
|
||||
)
|
||||
|
||||
var DisableSystemHosts, _ = strconv.ParseBool(os.Getenv("DISABLE_SYSTEM_HOSTS"))
|
||||
var (
|
||||
DisableSystemHosts, _ = strconv.ParseBool(os.Getenv("DISABLE_SYSTEM_HOSTS"))
|
||||
UseSystemHosts bool
|
||||
)
|
||||
|
||||
type Hosts struct {
|
||||
*trie.DomainTrie[HostValue]
|
||||
|
@ -25,11 +29,6 @@ func NewHosts(hosts *trie.DomainTrie[HostValue]) Hosts {
|
|||
}
|
||||
}
|
||||
|
||||
// lookupStaticHost looks up the addresses and the canonical name for the given host from /etc/hosts.
|
||||
//
|
||||
//go:linkname lookupStaticHost net.lookupStaticHost
|
||||
func lookupStaticHost(host string) ([]string, string)
|
||||
|
||||
// Return the search result and whether to match the parameter `isDomain`
|
||||
func (h *Hosts) Search(domain string, isDomain bool) (*HostValue, bool) {
|
||||
if value := h.DomainTrie.Search(domain); value != nil {
|
||||
|
@ -51,8 +50,9 @@ func (h *Hosts) Search(domain string, isDomain bool) (*HostValue, bool) {
|
|||
|
||||
return &hostValue, false
|
||||
}
|
||||
if !isDomain && !DisableSystemHosts {
|
||||
addr, _ := lookupStaticHost(domain)
|
||||
|
||||
if !isDomain && !DisableSystemHosts && UseSystemHosts {
|
||||
addr, _ := hosts.LookupStaticHost(domain)
|
||||
if hostValue, err := NewHostValue(addr); err == nil {
|
||||
return &hostValue, true
|
||||
}
|
||||
|
|
309
component/resolver/hosts/hosts.go
Normal file
309
component/resolver/hosts/hosts.go
Normal file
|
@ -0,0 +1,309 @@
|
|||
package hosts
|
||||
|
||||
// this file copy and modify from golang's std net/hosts.go
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"io/fs"
|
||||
"net/netip"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var hostsFilePath = "/etc/hosts"
|
||||
|
||||
const cacheMaxAge = 5 * time.Second
|
||||
|
||||
func parseLiteralIP(addr string) string {
|
||||
ip, err := netip.ParseAddr(addr)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return ip.String()
|
||||
}
|
||||
|
||||
type byName struct {
|
||||
addrs []string
|
||||
canonicalName string
|
||||
}
|
||||
|
||||
// hosts contains known host entries.
|
||||
var hosts struct {
|
||||
sync.Mutex
|
||||
|
||||
// Key for the list of literal IP addresses must be a host
|
||||
// name. It would be part of DNS labels, a FQDN or an absolute
|
||||
// FQDN.
|
||||
// For now the key is converted to lower case for convenience.
|
||||
byName map[string]byName
|
||||
|
||||
// Key for the list of host names must be a literal IP address
|
||||
// including IPv6 address with zone identifier.
|
||||
// We don't support old-classful IP address notation.
|
||||
byAddr map[string][]string
|
||||
|
||||
expire time.Time
|
||||
path string
|
||||
mtime time.Time
|
||||
size int64
|
||||
}
|
||||
|
||||
func readHosts() {
|
||||
now := time.Now()
|
||||
hp := hostsFilePath
|
||||
|
||||
if now.Before(hosts.expire) && hosts.path == hp && len(hosts.byName) > 0 {
|
||||
return
|
||||
}
|
||||
mtime, size, err := stat(hp)
|
||||
if err == nil && hosts.path == hp && hosts.mtime.Equal(mtime) && hosts.size == size {
|
||||
hosts.expire = now.Add(cacheMaxAge)
|
||||
return
|
||||
}
|
||||
|
||||
hs := make(map[string]byName)
|
||||
is := make(map[string][]string)
|
||||
|
||||
file, err := open(hp)
|
||||
if err != nil {
|
||||
if !errors.Is(err, fs.ErrNotExist) && !errors.Is(err, fs.ErrPermission) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if file != nil {
|
||||
defer file.close()
|
||||
for line, ok := file.readLine(); ok; line, ok = file.readLine() {
|
||||
if i := strings.IndexByte(line, '#'); i >= 0 {
|
||||
// Discard comments.
|
||||
line = line[0:i]
|
||||
}
|
||||
f := getFields(line)
|
||||
if len(f) < 2 {
|
||||
continue
|
||||
}
|
||||
addr := parseLiteralIP(f[0])
|
||||
if addr == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
var canonical string
|
||||
for i := 1; i < len(f); i++ {
|
||||
name := absDomainName(f[i])
|
||||
h := []byte(f[i])
|
||||
lowerASCIIBytes(h)
|
||||
key := absDomainName(string(h))
|
||||
|
||||
if i == 1 {
|
||||
canonical = key
|
||||
}
|
||||
|
||||
is[addr] = append(is[addr], name)
|
||||
|
||||
if v, ok := hs[key]; ok {
|
||||
hs[key] = byName{
|
||||
addrs: append(v.addrs, addr),
|
||||
canonicalName: v.canonicalName,
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
hs[key] = byName{
|
||||
addrs: []string{addr},
|
||||
canonicalName: canonical,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Update the data cache.
|
||||
hosts.expire = now.Add(cacheMaxAge)
|
||||
hosts.path = hp
|
||||
hosts.byName = hs
|
||||
hosts.byAddr = is
|
||||
hosts.mtime = mtime
|
||||
hosts.size = size
|
||||
}
|
||||
|
||||
// LookupStaticHost looks up the addresses and the canonical name for the given host from /etc/hosts.
|
||||
func LookupStaticHost(host string) ([]string, string) {
|
||||
hosts.Lock()
|
||||
defer hosts.Unlock()
|
||||
readHosts()
|
||||
if len(hosts.byName) != 0 {
|
||||
if hasUpperCase(host) {
|
||||
lowerHost := []byte(host)
|
||||
lowerASCIIBytes(lowerHost)
|
||||
host = string(lowerHost)
|
||||
}
|
||||
if byName, ok := hosts.byName[absDomainName(host)]; ok {
|
||||
ipsCp := make([]string, len(byName.addrs))
|
||||
copy(ipsCp, byName.addrs)
|
||||
return ipsCp, byName.canonicalName
|
||||
}
|
||||
}
|
||||
return nil, ""
|
||||
}
|
||||
|
||||
// LookupStaticAddr looks up the hosts for the given address from /etc/hosts.
|
||||
func LookupStaticAddr(addr string) []string {
|
||||
hosts.Lock()
|
||||
defer hosts.Unlock()
|
||||
readHosts()
|
||||
addr = parseLiteralIP(addr)
|
||||
if addr == "" {
|
||||
return nil
|
||||
}
|
||||
if len(hosts.byAddr) != 0 {
|
||||
if hosts, ok := hosts.byAddr[addr]; ok {
|
||||
hostsCp := make([]string, len(hosts))
|
||||
copy(hostsCp, hosts)
|
||||
return hostsCp
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func stat(name string) (mtime time.Time, size int64, err error) {
|
||||
st, err := os.Stat(name)
|
||||
if err != nil {
|
||||
return time.Time{}, 0, err
|
||||
}
|
||||
return st.ModTime(), st.Size(), nil
|
||||
}
|
||||
|
||||
type file struct {
|
||||
file *os.File
|
||||
data []byte
|
||||
atEOF bool
|
||||
}
|
||||
|
||||
func (f *file) close() { f.file.Close() }
|
||||
|
||||
func (f *file) getLineFromData() (s string, ok bool) {
|
||||
data := f.data
|
||||
i := 0
|
||||
for i = 0; i < len(data); i++ {
|
||||
if data[i] == '\n' {
|
||||
s = string(data[0:i])
|
||||
ok = true
|
||||
// move data
|
||||
i++
|
||||
n := len(data) - i
|
||||
copy(data[0:], data[i:])
|
||||
f.data = data[0:n]
|
||||
return
|
||||
}
|
||||
}
|
||||
if f.atEOF && len(f.data) > 0 {
|
||||
// EOF, return all we have
|
||||
s = string(data)
|
||||
f.data = f.data[0:0]
|
||||
ok = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (f *file) readLine() (s string, ok bool) {
|
||||
if s, ok = f.getLineFromData(); ok {
|
||||
return
|
||||
}
|
||||
if len(f.data) < cap(f.data) {
|
||||
ln := len(f.data)
|
||||
n, err := io.ReadFull(f.file, f.data[ln:cap(f.data)])
|
||||
if n >= 0 {
|
||||
f.data = f.data[0 : ln+n]
|
||||
}
|
||||
if err == io.EOF || err == io.ErrUnexpectedEOF {
|
||||
f.atEOF = true
|
||||
}
|
||||
}
|
||||
s, ok = f.getLineFromData()
|
||||
return
|
||||
}
|
||||
|
||||
func (f *file) stat() (mtime time.Time, size int64, err error) {
|
||||
st, err := f.file.Stat()
|
||||
if err != nil {
|
||||
return time.Time{}, 0, err
|
||||
}
|
||||
return st.ModTime(), st.Size(), nil
|
||||
}
|
||||
|
||||
func open(name string) (*file, error) {
|
||||
fd, err := os.Open(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &file{fd, make([]byte, 0, 64*1024), false}, nil
|
||||
}
|
||||
|
||||
func getFields(s string) []string { return splitAtBytes(s, " \r\t\n") }
|
||||
|
||||
// Count occurrences in s of any bytes in t.
|
||||
func countAnyByte(s string, t string) int {
|
||||
n := 0
|
||||
for i := 0; i < len(s); i++ {
|
||||
if strings.IndexByte(t, s[i]) >= 0 {
|
||||
n++
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Split s at any bytes in t.
|
||||
func splitAtBytes(s string, t string) []string {
|
||||
a := make([]string, 1+countAnyByte(s, t))
|
||||
n := 0
|
||||
last := 0
|
||||
for i := 0; i < len(s); i++ {
|
||||
if strings.IndexByte(t, s[i]) >= 0 {
|
||||
if last < i {
|
||||
a[n] = s[last:i]
|
||||
n++
|
||||
}
|
||||
last = i + 1
|
||||
}
|
||||
}
|
||||
if last < len(s) {
|
||||
a[n] = s[last:]
|
||||
n++
|
||||
}
|
||||
return a[0:n]
|
||||
}
|
||||
|
||||
// lowerASCIIBytes makes x ASCII lowercase in-place.
|
||||
func lowerASCIIBytes(x []byte) {
|
||||
for i, b := range x {
|
||||
if 'A' <= b && b <= 'Z' {
|
||||
x[i] += 'a' - 'A'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// hasUpperCase tells whether the given string contains at least one upper-case.
|
||||
func hasUpperCase(s string) bool {
|
||||
for i := range s {
|
||||
if 'A' <= s[i] && s[i] <= 'Z' {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// absDomainName returns an absolute domain name which ends with a
|
||||
// trailing dot to match pure Go reverse resolver and all other lookup
|
||||
// routines.
|
||||
// See golang.org/issue/12189.
|
||||
// But we don't want to add dots for local names from /etc/hosts.
|
||||
// It's hard to tell so we settle on the heuristic that names without dots
|
||||
// (like "localhost" or "myhost") do not get trailing dots, but any other
|
||||
// names do.
|
||||
func absDomainName(s string) string {
|
||||
if strings.IndexByte(s, '.') != -1 && s[len(s)-1] != '.' {
|
||||
s += "."
|
||||
}
|
||||
return s
|
||||
}
|
13
component/resolver/hosts/hosts_windows.go
Normal file
13
component/resolver/hosts/hosts_windows.go
Normal file
|
@ -0,0 +1,13 @@
|
|||
package hosts
|
||||
|
||||
// this file copy and modify from golang's std net/hook_windows.go
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
func init() {
|
||||
if dir, err := windows.GetSystemDirectory(); err == nil {
|
||||
hostsFilePath = dir + "/Drivers/etc/hosts"
|
||||
}
|
||||
}
|
|
@ -16,16 +16,13 @@ import (
|
|||
"errors"
|
||||
"net"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/metacubex/mihomo/common/utils"
|
||||
"github.com/metacubex/mihomo/log"
|
||||
"github.com/metacubex/mihomo/ntp"
|
||||
|
||||
utls "github.com/sagernet/utls"
|
||||
utls "github.com/metacubex/utls"
|
||||
"github.com/zhangyunhao116/fastrand"
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
"golang.org/x/crypto/hkdf"
|
||||
|
@ -39,9 +36,6 @@ type RealityConfig struct {
|
|||
ShortID [RealityMaxShortIDLen]byte
|
||||
}
|
||||
|
||||
//go:linkname aesgcmPreferred crypto/tls.aesgcmPreferred
|
||||
func aesgcmPreferred(ciphers []uint16) bool
|
||||
|
||||
func GetRealityConn(ctx context.Context, conn net.Conn, ClientFingerprint string, tlsConfig *tls.Config, realityConfig *RealityConfig) (net.Conn, error) {
|
||||
retry := 0
|
||||
for fingerprint, exists := GetFingerprint(ClientFingerprint); exists; retry++ {
|
||||
|
@ -102,7 +96,7 @@ func GetRealityConn(ctx context.Context, conn net.Conn, ClientFingerprint string
|
|||
return nil, err
|
||||
}
|
||||
var aeadCipher cipher.AEAD
|
||||
if aesgcmPreferred(hello.CipherSuites) {
|
||||
if utls.AesgcmPreferred(hello.CipherSuites) {
|
||||
aesBlock, _ := aes.NewCipher(authKey)
|
||||
aeadCipher, _ = cipher.NewGCM(aesBlock)
|
||||
} else {
|
||||
|
@ -139,7 +133,10 @@ func realityClientFallback(uConn net.Conn, serverName string, fingerprint utls.C
|
|||
},
|
||||
},
|
||||
}
|
||||
request, _ := http.NewRequest("GET", "https://"+serverName, nil)
|
||||
request, err := http.NewRequest("GET", "https://"+serverName, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
request.Header.Set("User-Agent", fingerprint.Client)
|
||||
request.AddCookie(&http.Cookie{Name: "padding", Value: strings.Repeat("0", fastrand.Intn(32)+30)})
|
||||
response, err := client.Do(request)
|
||||
|
@ -159,11 +156,12 @@ type realityVerifier struct {
|
|||
verified bool
|
||||
}
|
||||
|
||||
var pOffset = utils.MustOK(reflect.TypeOf((*utls.Conn)(nil)).Elem().FieldByName("peerCertificates")).Offset
|
||||
//var pOffset = utils.MustOK(reflect.TypeOf((*utls.Conn)(nil)).Elem().FieldByName("peerCertificates")).Offset
|
||||
|
||||
func (c *realityVerifier) VerifyPeerCertificate(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
|
||||
//p, _ := reflect.TypeOf(c.Conn).Elem().FieldByName("peerCertificates")
|
||||
certs := *(*[]*x509.Certificate)(unsafe.Add(unsafe.Pointer(c.Conn), pOffset))
|
||||
//certs := *(*[]*x509.Certificate)(unsafe.Add(unsafe.Pointer(c.Conn), pOffset))
|
||||
certs := c.Conn.PeerCertificates()
|
||||
if pub, ok := certs[0].PublicKey.(ed25519.PublicKey); ok {
|
||||
h := hmac.New(sha512.New, c.authKey)
|
||||
h.Write(pub)
|
||||
|
|
|
@ -6,8 +6,8 @@ import (
|
|||
|
||||
"github.com/metacubex/mihomo/log"
|
||||
|
||||
utls "github.com/metacubex/utls"
|
||||
"github.com/mroth/weightedrand/v2"
|
||||
utls "github.com/sagernet/utls"
|
||||
)
|
||||
|
||||
type UConn struct {
|
||||
|
|
|
@ -16,7 +16,6 @@ import (
|
|||
"time"
|
||||
|
||||
mihomoHttp "github.com/metacubex/mihomo/component/http"
|
||||
"github.com/metacubex/mihomo/constant"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
"github.com/metacubex/mihomo/log"
|
||||
|
||||
|
@ -52,6 +51,10 @@ func init() {
|
|||
if runtime.GOARCH == "amd64" && cpuid.CPU.X64Level() < 3 {
|
||||
amd64Compatible = "-compatible"
|
||||
}
|
||||
if !strings.HasPrefix(C.Version, "alpha") {
|
||||
baseURL = "https://github.com/MetaCubeX/mihomo/releases/latest/download/mihomo"
|
||||
versionURL = "https://github.com/MetaCubeX/mihomo/releases/latest/download/version.txt"
|
||||
}
|
||||
}
|
||||
|
||||
type updateError struct {
|
||||
|
@ -64,7 +67,7 @@ func (e *updateError) Error() string {
|
|||
|
||||
// Update performs the auto-updater. It returns an error if the updater failed.
|
||||
// If firstRun is true, it assumes the configuration file doesn't exist.
|
||||
func Update(execPath string) (err error) {
|
||||
func UpdateCore(execPath string) (err error) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
|
@ -73,9 +76,9 @@ func Update(execPath string) (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
log.Infoln("current version %s, latest version %s", constant.Version, latestVersion)
|
||||
log.Infoln("current version %s, latest version %s", C.Version, latestVersion)
|
||||
|
||||
if latestVersion == constant.Version {
|
||||
if latestVersion == C.Version {
|
||||
err := &updateError{Message: "already using latest version"}
|
||||
return err
|
||||
}
|
|
@ -1,18 +1,27 @@
|
|||
package config
|
||||
package updater
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/metacubex/mihomo/common/atomic"
|
||||
"github.com/metacubex/mihomo/component/geodata"
|
||||
_ "github.com/metacubex/mihomo/component/geodata/standard"
|
||||
"github.com/metacubex/mihomo/component/mmdb"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
"github.com/metacubex/mihomo/log"
|
||||
|
||||
"github.com/oschwald/maxminddb-golang"
|
||||
)
|
||||
|
||||
func UpdateGeoDatabases() error {
|
||||
var (
|
||||
UpdatingGeo atomic.Bool
|
||||
)
|
||||
|
||||
func updateGeoDatabases() error {
|
||||
defer runtime.GC()
|
||||
geoLoader, err := geodata.GetGeoDataLoader("standard")
|
||||
if err != nil {
|
||||
|
@ -88,3 +97,75 @@ func UpdateGeoDatabases() error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func UpdateGeoDatabases(updateNotification chan struct{}) error {
|
||||
log.Infoln("[GEO] Start updating GEO database")
|
||||
|
||||
if UpdatingGeo.Load() {
|
||||
return errors.New("GEO database is updating, skip")
|
||||
}
|
||||
|
||||
UpdatingGeo.Store(true)
|
||||
defer UpdatingGeo.Store(false)
|
||||
|
||||
log.Infoln("[GEO] Updating GEO database")
|
||||
|
||||
if err := updateGeoDatabases(); err != nil {
|
||||
log.Errorln("[GEO] update GEO database error: %s", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
updateNotification <- struct{}{}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getUpdateTime() (err error, time time.Time) {
|
||||
var fileInfo os.FileInfo
|
||||
if C.GeodataMode {
|
||||
fileInfo, err = os.Stat(C.Path.GeoIP())
|
||||
if err != nil {
|
||||
return err, time
|
||||
}
|
||||
} else {
|
||||
fileInfo, err = os.Stat(C.Path.MMDB())
|
||||
if err != nil {
|
||||
return err, time
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fileInfo.ModTime()
|
||||
}
|
||||
|
||||
func RegisterGeoUpdater(updateNotification chan struct{}) {
|
||||
if C.GeoUpdateInterval <= 0 {
|
||||
log.Errorln("[GEO] Invalid update interval: %d", C.GeoUpdateInterval)
|
||||
return
|
||||
}
|
||||
|
||||
ticker := time.NewTicker(time.Duration(C.GeoUpdateInterval) * time.Hour)
|
||||
defer ticker.Stop()
|
||||
|
||||
log.Infoln("[GEO] update GEO database every %d hours", C.GeoUpdateInterval)
|
||||
go func() {
|
||||
err, lastUpdate := getUpdateTime()
|
||||
if err != nil {
|
||||
log.Errorln("[GEO] Get GEO database update time error: %s", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
log.Infoln("[GEO] last update time %s", lastUpdate)
|
||||
if lastUpdate.Add(time.Duration(C.GeoUpdateInterval) * time.Hour).Before(time.Now()) {
|
||||
log.Infoln("[GEO] Database has not been updated for %v, update now", time.Duration(C.GeoUpdateInterval)*time.Hour)
|
||||
if err := UpdateGeoDatabases(updateNotification); err != nil {
|
||||
log.Errorln("[GEO] Failed to update GEO database: %s", err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for range ticker.C {
|
||||
if err := UpdateGeoDatabases(updateNotification); err != nil {
|
||||
log.Errorln("[GEO] Failed to update GEO database: %s", err.Error())
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package config
|
||||
package updater
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
|
@ -29,7 +29,7 @@ func UpdateUI() error {
|
|||
xdMutex.Lock()
|
||||
defer xdMutex.Unlock()
|
||||
|
||||
err := prepare()
|
||||
err := prepare_ui()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ func UpdateUI() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func prepare() error {
|
||||
func prepare_ui() error {
|
||||
if ExternalUIPath == "" || ExternalUIURL == "" {
|
||||
return ErrIncompleteConf
|
||||
}
|
|
@ -1,12 +1,35 @@
|
|||
package updater
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
mihomoHttp "github.com/metacubex/mihomo/component/http"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
|
||||
"golang.org/x/exp/constraints"
|
||||
)
|
||||
|
||||
func downloadForBytes(url string) ([]byte, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*90)
|
||||
defer cancel()
|
||||
resp, err := mihomoHttp.HttpRequest(ctx, url, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return io.ReadAll(resp.Body)
|
||||
}
|
||||
|
||||
func saveFile(bytes []byte, path string) error {
|
||||
return os.WriteFile(path, bytes, 0o644)
|
||||
}
|
||||
|
||||
// LimitReachedError records the limit and the operation that caused it.
|
||||
type LimitReachedError struct {
|
||||
Limit int64
|
|
@ -28,6 +28,7 @@ import (
|
|||
SNIFF "github.com/metacubex/mihomo/component/sniffer"
|
||||
tlsC "github.com/metacubex/mihomo/component/tls"
|
||||
"github.com/metacubex/mihomo/component/trie"
|
||||
"github.com/metacubex/mihomo/component/updater"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
"github.com/metacubex/mihomo/constant/features"
|
||||
providerTypes "github.com/metacubex/mihomo/constant/provider"
|
||||
|
@ -114,6 +115,7 @@ type DNS struct {
|
|||
PreferH3 bool `yaml:"prefer-h3"`
|
||||
IPv6 bool `yaml:"ipv6"`
|
||||
IPv6Timeout uint `yaml:"ipv6-timeout"`
|
||||
UseSystemHosts bool `yaml:"use-system-hosts"`
|
||||
NameServer []dns.NameServer `yaml:"nameserver"`
|
||||
Fallback []dns.NameServer `yaml:"fallback"`
|
||||
FallbackFilter FallbackFilter `yaml:"fallback-filter"`
|
||||
|
@ -209,6 +211,7 @@ type RawDNS struct {
|
|||
IPv6 bool `yaml:"ipv6" json:"ipv6"`
|
||||
IPv6Timeout uint `yaml:"ipv6-timeout" json:"ipv6-timeout"`
|
||||
UseHosts bool `yaml:"use-hosts" json:"use-hosts"`
|
||||
UseSystemHosts bool `yaml:"use-system-hosts" json:"use-system-hosts"`
|
||||
NameServer []string `yaml:"nameserver" json:"nameserver"`
|
||||
Fallback []string `yaml:"fallback" json:"fallback"`
|
||||
FallbackFilter RawFallbackFilter `yaml:"fallback-filter" json:"fallback-filter"`
|
||||
|
@ -456,12 +459,13 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) {
|
|||
Interval: 30,
|
||||
},
|
||||
DNS: RawDNS{
|
||||
Enable: false,
|
||||
IPv6: false,
|
||||
UseHosts: true,
|
||||
IPv6Timeout: 100,
|
||||
EnhancedMode: C.DNSMapping,
|
||||
FakeIPRange: "198.18.0.1/16",
|
||||
Enable: false,
|
||||
IPv6: false,
|
||||
UseHosts: true,
|
||||
UseSystemHosts: true,
|
||||
IPv6Timeout: 100,
|
||||
EnhancedMode: C.DNSMapping,
|
||||
FakeIPRange: "198.18.0.1/16",
|
||||
FallbackFilter: RawFallbackFilter{
|
||||
GeoIP: true,
|
||||
GeoIPCode: "CN",
|
||||
|
@ -637,28 +641,28 @@ func parseGeneral(cfg *RawConfig) (*General, error) {
|
|||
N.KeepAliveInterval = time.Duration(cfg.KeepAliveInterval) * time.Second
|
||||
}
|
||||
|
||||
ExternalUIPath = cfg.ExternalUI
|
||||
updater.ExternalUIPath = cfg.ExternalUI
|
||||
// checkout externalUI exist
|
||||
if ExternalUIPath != "" {
|
||||
ExternalUIPath = C.Path.Resolve(ExternalUIPath)
|
||||
if _, err := os.Stat(ExternalUIPath); os.IsNotExist(err) {
|
||||
if updater.ExternalUIPath != "" {
|
||||
updater.ExternalUIPath = C.Path.Resolve(updater.ExternalUIPath)
|
||||
if _, err := os.Stat(updater.ExternalUIPath); os.IsNotExist(err) {
|
||||
defaultUIpath := path.Join(C.Path.HomeDir(), "ui")
|
||||
log.Warnln("external-ui: %s does not exist, creating folder in %s", ExternalUIPath, defaultUIpath)
|
||||
log.Warnln("external-ui: %s does not exist, creating folder in %s", updater.ExternalUIPath, defaultUIpath)
|
||||
if err := os.MkdirAll(defaultUIpath, os.ModePerm); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ExternalUIPath = defaultUIpath
|
||||
updater.ExternalUIPath = defaultUIpath
|
||||
cfg.ExternalUI = defaultUIpath
|
||||
}
|
||||
}
|
||||
// checkout UIpath/name exist
|
||||
if cfg.ExternalUIName != "" {
|
||||
ExternalUIName = cfg.ExternalUIName
|
||||
updater.ExternalUIName = cfg.ExternalUIName
|
||||
} else {
|
||||
ExternalUIFolder = ExternalUIPath
|
||||
updater.ExternalUIFolder = updater.ExternalUIPath
|
||||
}
|
||||
if cfg.ExternalUIURL != "" {
|
||||
ExternalUIURL = cfg.ExternalUIURL
|
||||
updater.ExternalUIURL = cfg.ExternalUIURL
|
||||
}
|
||||
|
||||
cfg.Tun.RedirectToTun = cfg.EBpf.RedirectToTun
|
||||
|
@ -924,7 +928,7 @@ func parseRules(rulesConfig []string, proxies map[string]C.Proxy, subRules map[s
|
|||
|
||||
l := len(rule)
|
||||
|
||||
if ruleName == "NOT" || ruleName == "OR" || ruleName == "AND" || ruleName == "SUB-RULE" || ruleName == "DOMAIN-REGEX" {
|
||||
if ruleName == "NOT" || ruleName == "OR" || ruleName == "AND" || ruleName == "SUB-RULE" || ruleName == "DOMAIN-REGEX" || ruleName == "PROCESS-NAME-REGEX" || ruleName == "PROCESS-PATH-REGEX" {
|
||||
target = rule[l-1]
|
||||
payload = strings.Join(rule[1:l-1], ",")
|
||||
} else {
|
||||
|
@ -1285,12 +1289,13 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rul
|
|||
}
|
||||
|
||||
dnsCfg := &DNS{
|
||||
Enable: cfg.Enable,
|
||||
Listen: cfg.Listen,
|
||||
PreferH3: cfg.PreferH3,
|
||||
IPv6Timeout: cfg.IPv6Timeout,
|
||||
IPv6: cfg.IPv6,
|
||||
EnhancedMode: cfg.EnhancedMode,
|
||||
Enable: cfg.Enable,
|
||||
Listen: cfg.Listen,
|
||||
PreferH3: cfg.PreferH3,
|
||||
IPv6Timeout: cfg.IPv6Timeout,
|
||||
IPv6: cfg.IPv6,
|
||||
UseSystemHosts: cfg.UseSystemHosts,
|
||||
EnhancedMode: cfg.EnhancedMode,
|
||||
FallbackFilter: FallbackFilter{
|
||||
IPCIDR: []netip.Prefix{},
|
||||
GeoSite: []router.DomainMatcher{},
|
||||
|
|
|
@ -1,38 +1,15 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/metacubex/mihomo/adapter/outboundgroup"
|
||||
"github.com/metacubex/mihomo/common/structure"
|
||||
mihomoHttp "github.com/metacubex/mihomo/component/http"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
)
|
||||
|
||||
func downloadForBytes(url string) ([]byte, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*90)
|
||||
defer cancel()
|
||||
resp, err := mihomoHttp.HttpRequest(ctx, url, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return io.ReadAll(resp.Body)
|
||||
}
|
||||
|
||||
func saveFile(bytes []byte, path string) error {
|
||||
return os.WriteFile(path, bytes, 0o644)
|
||||
}
|
||||
|
||||
func trimArr(arr []string) (r []string) {
|
||||
for _, e := range arr {
|
||||
r = append(r, strings.Trim(e, " "))
|
||||
|
|
5
constant/features/version.go
Normal file
5
constant/features/version.go
Normal file
|
@ -0,0 +1,5 @@
|
|||
package features
|
||||
|
||||
var WindowsMajorVersion uint32
|
||||
var WindowsMinorVersion uint32
|
||||
var WindowsBuildNumber uint32
|
10
constant/features/version_windows.go
Normal file
10
constant/features/version_windows.go
Normal file
|
@ -0,0 +1,10 @@
|
|||
package features
|
||||
|
||||
import "golang.org/x/sys/windows"
|
||||
|
||||
func init() {
|
||||
version := windows.RtlGetVersion()
|
||||
WindowsMajorVersion = version.MajorVersion
|
||||
WindowsMinorVersion = version.MinorVersion
|
||||
WindowsBuildNumber = version.MinorVersion
|
||||
}
|
|
@ -22,8 +22,10 @@ const (
|
|||
InUser
|
||||
InName
|
||||
InType
|
||||
Process
|
||||
ProcessName
|
||||
ProcessPath
|
||||
ProcessNameRegex
|
||||
ProcessPathRegex
|
||||
RuleSet
|
||||
Network
|
||||
Uid
|
||||
|
@ -76,10 +78,14 @@ func (rt RuleType) String() string {
|
|||
return "InName"
|
||||
case InType:
|
||||
return "InType"
|
||||
case Process:
|
||||
return "Process"
|
||||
case ProcessName:
|
||||
return "ProcessName"
|
||||
case ProcessPath:
|
||||
return "ProcessPath"
|
||||
case ProcessNameRegex:
|
||||
return "ProcessNameRegex"
|
||||
case ProcessPathRegex:
|
||||
return "ProcessPathRegex"
|
||||
case MATCH:
|
||||
return "Match"
|
||||
case RuleSet:
|
||||
|
|
|
@ -515,7 +515,7 @@ func (doh *dnsOverHTTPS) createTransportH3(
|
|||
},
|
||||
DisableCompression: true,
|
||||
TLSClientConfig: tlsConfig,
|
||||
QuicConfig: doh.getQUICConfig(),
|
||||
QUICConfig: doh.getQUICConfig(),
|
||||
}
|
||||
|
||||
return &http3Transport{baseTransport: rt}, nil
|
||||
|
|
34
go.mod
34
go.mod
|
@ -12,21 +12,22 @@ require (
|
|||
github.com/go-chi/chi/v5 v5.0.12
|
||||
github.com/go-chi/cors v1.2.1
|
||||
github.com/go-chi/render v1.0.3
|
||||
github.com/gobwas/ws v1.3.2
|
||||
github.com/gofrs/uuid/v5 v5.1.0
|
||||
github.com/gobwas/ws v1.4.0
|
||||
github.com/gofrs/uuid/v5 v5.2.0
|
||||
github.com/insomniacslk/dhcp v0.0.0-20240419123447-f1cffa2c0c49
|
||||
github.com/klauspost/cpuid/v2 v2.2.7
|
||||
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40
|
||||
github.com/mdlayher/netlink v1.7.2
|
||||
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759
|
||||
github.com/metacubex/quic-go v0.42.1-0.20240418003344-f006b5735d98
|
||||
github.com/metacubex/sing-quic v0.0.0-20240418004036-814c531c378d
|
||||
github.com/metacubex/quic-go v0.43.2-0.20240518033621-2c3d14c6b38e
|
||||
github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72
|
||||
github.com/metacubex/sing-shadowsocks v0.2.6
|
||||
github.com/metacubex/sing-shadowsocks2 v0.2.0
|
||||
github.com/metacubex/sing-tun v0.2.6
|
||||
github.com/metacubex/sing-tun v0.2.7-0.20240512075008-89e7c6208eec
|
||||
github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f
|
||||
github.com/metacubex/sing-wireguard v0.0.0-20240321042214-224f96122a63
|
||||
github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66
|
||||
github.com/metacubex/utls v1.6.6
|
||||
github.com/miekg/dns v1.1.59
|
||||
github.com/mroth/weightedrand/v2 v2.1.0
|
||||
github.com/openacid/low v0.1.21
|
||||
|
@ -37,24 +38,23 @@ require (
|
|||
github.com/sagernet/sing v0.3.8
|
||||
github.com/sagernet/sing-mux v0.2.1-0.20240124034317-9bfb33698bb6
|
||||
github.com/sagernet/sing-shadowtls v0.1.4
|
||||
github.com/sagernet/utls v1.5.4
|
||||
github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e
|
||||
github.com/samber/lo v1.39.0
|
||||
github.com/shirou/gopsutil/v3 v3.24.3
|
||||
github.com/shirou/gopsutil/v3 v3.24.4
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8
|
||||
github.com/zhangyunhao116/fastrand v0.4.0
|
||||
go.uber.org/automaxprocs v1.5.3
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
||||
golang.org/x/crypto v0.22.0
|
||||
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f
|
||||
golang.org/x/net v0.24.0
|
||||
golang.org/x/crypto v0.23.0
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842
|
||||
golang.org/x/net v0.25.0
|
||||
golang.org/x/sync v0.7.0
|
||||
golang.org/x/sys v0.19.0
|
||||
google.golang.org/protobuf v1.33.0
|
||||
golang.org/x/sys v0.20.0
|
||||
google.golang.org/protobuf v1.34.1
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
lukechampine.com/blake3 v1.2.2
|
||||
lukechampine.com/blake3 v1.3.0
|
||||
)
|
||||
|
||||
require (
|
||||
|
@ -63,7 +63,7 @@ require (
|
|||
github.com/ajg/form v1.5.1 // indirect
|
||||
github.com/andybalholm/brotli v1.0.6 // indirect
|
||||
github.com/buger/jsonparser v1.1.1 // indirect
|
||||
github.com/cloudflare/circl v1.3.6 // indirect
|
||||
github.com/cloudflare/circl v1.3.7 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9 // indirect
|
||||
github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 // indirect
|
||||
|
@ -105,9 +105,9 @@ require (
|
|||
gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect
|
||||
go.uber.org/mock v0.4.0 // indirect
|
||||
golang.org/x/mod v0.17.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/text v0.15.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
golang.org/x/tools v0.20.0 // indirect
|
||||
golang.org/x/tools v0.21.0 // indirect
|
||||
)
|
||||
|
||||
replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20240408015159-aa61c96df764
|
||||
replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20240518125217-e63d65a914d1
|
||||
|
|
71
go.sum
71
go.sum
|
@ -21,8 +21,8 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P
|
|||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/cilium/ebpf v0.12.3 h1:8ht6F9MquybnY97at+VDZb3eQQr8ev79RueWeVaEcG4=
|
||||
github.com/cilium/ebpf v0.12.3/go.mod h1:TctK1ivibvI3znr66ljgi4hqOT8EYQjz1KWBfb1UVgM=
|
||||
github.com/cloudflare/circl v1.3.6 h1:/xbKIqSHbZXHwkhbrhrt2YOHIwYJlXH94E3tI/gDlUg=
|
||||
github.com/cloudflare/circl v1.3.6/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
|
||||
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
|
||||
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
|
||||
github.com/coreos/go-iptables v0.7.0 h1:XWM3V+MPRr5/q51NuWSgU0fqMad64Zyxs8ZUoMsamr8=
|
||||
github.com/coreos/go-iptables v0.7.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
@ -60,10 +60,10 @@ github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU
|
|||
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
|
||||
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
|
||||
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
||||
github.com/gobwas/ws v1.3.2 h1:zlnbNHxumkRvfPWgfXu8RBwyNR1x8wh9cf5PTOCqs9Q=
|
||||
github.com/gobwas/ws v1.3.2/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=
|
||||
github.com/gofrs/uuid/v5 v5.1.0 h1:S5rqVKIigghZTCBKPCw0Y+bXkn26K3TB5mvQq2Ix8dk=
|
||||
github.com/gofrs/uuid/v5 v5.1.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
|
||||
github.com/gobwas/ws v1.4.0 h1:CTaoG1tojrh4ucGPcoJFiAQUAsEWekEWvLy7GsVNqGs=
|
||||
github.com/gobwas/ws v1.4.0/go.mod h1:G3gNqMNtPppf5XUz7O4shetPpcZ1VJ7zt18dlUeakrc=
|
||||
github.com/gofrs/uuid/v5 v5.2.0 h1:qw1GMx6/y8vhVsx626ImfKMuS5CvJmhIKKtuyvfajMM=
|
||||
github.com/gofrs/uuid/v5 v5.2.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
|
||||
|
@ -78,8 +78,6 @@ github.com/google/tink/go v1.6.1 h1:t7JHqO8Ath2w2ig5vjwQYJzhGEZymedQc90lQXUBa4I=
|
|||
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
|
||||
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20240227161007-c728f5dd21c8 h1:V3plQrMHRWOB5zMm3yNqvBxDQVW1+/wHBSok5uPdmVs=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20240227161007-c728f5dd21c8/go.mod h1:izxuNQZeFrbx2nK2fAyN5iNUB34Fe9j0nK4PwLzAkKw=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20240419123447-f1cffa2c0c49 h1:/OuvSMGT9+xnyZ+7MZQ1zdngaCCAdPoSw8B/uurZ7pg=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20240419123447-f1cffa2c0c49/go.mod h1:KclMyHxX06VrVr0DJmeFSUb1ankt7xTfoOA35pCkoic=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
|
@ -106,24 +104,26 @@ github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 h1:cjd4biTvO
|
|||
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759/go.mod h1:UHOv2xu+RIgLwpXca7TLrXleEd4oR3sPatW6IF8wU88=
|
||||
github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec h1:HxreOiFTUrJXJautEo8rnE1uKTVGY8wtZepY1Tii/Nc=
|
||||
github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec/go.mod h1:8BVmQ+3cxjqzWElafm24rb2Ae4jRI6vAXNXWqWjfrXw=
|
||||
github.com/metacubex/quic-go v0.42.1-0.20240418003344-f006b5735d98 h1:oMLlJV4a9AylNo8ZLBNUiqZ02Vme6GLLHjuWJz8amSk=
|
||||
github.com/metacubex/quic-go v0.42.1-0.20240418003344-f006b5735d98/go.mod h1:iGx3Y1zynls/FjFgykLSqDcM81U0IKePRTXEz5g3iiQ=
|
||||
github.com/metacubex/sing v0.0.0-20240408015159-aa61c96df764 h1:+czGKoynxYA90YaL3NlCAIJHnlqwoUlLWgmOhdm5ZU8=
|
||||
github.com/metacubex/sing v0.0.0-20240408015159-aa61c96df764/go.mod h1:+60H3Cm91RnL9dpVGWDPHt0zTQImO9Vfqt9a4rSambI=
|
||||
github.com/metacubex/sing-quic v0.0.0-20240418004036-814c531c378d h1:RAe0ND8J5SOPGI623oEXfaHKaaUrrzHx+U1DN9Awcco=
|
||||
github.com/metacubex/sing-quic v0.0.0-20240418004036-814c531c378d/go.mod h1:WyY0zYxv+o+18R/Ece+QFontlgXoobKbNqbtYn2zjz8=
|
||||
github.com/metacubex/quic-go v0.43.2-0.20240518033621-2c3d14c6b38e h1:Nzwe08FNIJpExWpy9iXkG336dN/8nJqn69yijB7vJ8g=
|
||||
github.com/metacubex/quic-go v0.43.2-0.20240518033621-2c3d14c6b38e/go.mod h1:uXHODgJFUfUnkkCMWLd5Er6L5QY/LFRZb9LD5jyyhsk=
|
||||
github.com/metacubex/sing v0.0.0-20240518125217-e63d65a914d1 h1:7hDHLTmjgtRoAp59STwPQpe5Pinwi4cWex+FB3Ohvco=
|
||||
github.com/metacubex/sing v0.0.0-20240518125217-e63d65a914d1/go.mod h1:+60H3Cm91RnL9dpVGWDPHt0zTQImO9Vfqt9a4rSambI=
|
||||
github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72 h1:Wr4g1HCb5Z/QIFwFiVNjO2qL+dRu25+Mdn9xtAZZ+ew=
|
||||
github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72/go.mod h1:g7Mxj7b7zm7YVqD975mk/hSmrb0A0G4bVvIMr2MMzn8=
|
||||
github.com/metacubex/sing-shadowsocks v0.2.6 h1:6oEB3QcsFYnNiFeoevcXrCwJ3sAablwVSgtE9R3QeFQ=
|
||||
github.com/metacubex/sing-shadowsocks v0.2.6/go.mod h1:zIkMeSnb8Mbf4hdqhw0pjzkn1d99YJ3JQm/VBg5WMTg=
|
||||
github.com/metacubex/sing-shadowsocks2 v0.2.0 h1:hqwT/AfI5d5UdPefIzR6onGHJfDXs5zgOM5QSgaM/9A=
|
||||
github.com/metacubex/sing-shadowsocks2 v0.2.0/go.mod h1:LCKF6j1P94zN8ZS+LXRK1gmYTVGB3squivBSXAFnOg8=
|
||||
github.com/metacubex/sing-tun v0.2.6 h1:frc58BqnIClqcC9KcYBfVAn5bgO6WW1ANKvZW3/HYAQ=
|
||||
github.com/metacubex/sing-tun v0.2.6/go.mod h1:4VsMwZH1IlgPGFK1ZbBomZ/B2MYkTgs2+gnBAr5GOIo=
|
||||
github.com/metacubex/sing-tun v0.2.7-0.20240512075008-89e7c6208eec h1:K4Wq3GOdLZ/xcqwyzAt4kmYQrjokyKQ3u/Xh5Yft14U=
|
||||
github.com/metacubex/sing-tun v0.2.7-0.20240512075008-89e7c6208eec/go.mod h1:4VsMwZH1IlgPGFK1ZbBomZ/B2MYkTgs2+gnBAr5GOIo=
|
||||
github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f h1:QjXrHKbTMBip/C+R79bvbfr42xH1gZl3uFb0RELdZiQ=
|
||||
github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f/go.mod h1:olVkD4FChQ5gKMHG4ZzuD7+fMkJY1G8vwOKpRehjrmY=
|
||||
github.com/metacubex/sing-wireguard v0.0.0-20240321042214-224f96122a63 h1:AGyIB55UfQm/0ZH0HtQO9u3l//yjtHUpjeRjjPGfGRI=
|
||||
github.com/metacubex/sing-wireguard v0.0.0-20240321042214-224f96122a63/go.mod h1:uY+BYb0UEknLrqvbGcwi9i++KgrKxsurysgI6G1Pveo=
|
||||
github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 h1:as/aO/fM8nv4W4pOr9EETP6kV/Oaujk3fUNyQSJK61c=
|
||||
github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66/go.mod h1:c7bVFM9f5+VzeZ/6Kg77T/jrg1Xp8QpqlSHvG/aXVts=
|
||||
github.com/metacubex/utls v1.6.6 h1:3D12YKHTf2Z41UPhQU2dWerNWJ5TVQD9gKoQ+H+iLC8=
|
||||
github.com/metacubex/utls v1.6.6/go.mod h1:+WLFUnXjcpdxXCnyX25nggw8C6YonZ8zOK2Zm/oRvdo=
|
||||
github.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs=
|
||||
github.com/miekg/dns v1.1.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk=
|
||||
github.com/mroth/weightedrand/v2 v2.1.0 h1:o1ascnB1CIVzsqlfArQQjeMy1U0NcIbBO5rfd5E/OeU=
|
||||
|
@ -165,14 +165,12 @@ github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnV
|
|||
github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4=
|
||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ=
|
||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7/go.mod h1:FP9X2xjT/Az1EsG/orYYoC+5MojWnuI7hrffz8fGwwo=
|
||||
github.com/sagernet/utls v1.5.4 h1:KmsEGbB2dKUtCNC+44NwAdNAqnqQ6GA4pTO0Yik56co=
|
||||
github.com/sagernet/utls v1.5.4/go.mod h1:CTGxPWExIloRipK3XFpYv0OVyhO8kk3XCGW/ieyTh1s=
|
||||
github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e h1:iGH0RMv2FzELOFNFQtvsxH7NPmlo7X5JizEK51UCojo=
|
||||
github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e/go.mod h1:YbL4TKHRR6APYQv3U2RGfwLDpPYSyWz6oUlpISBEzBE=
|
||||
github.com/samber/lo v1.39.0 h1:4gTz1wUhNYLhFSKl6O+8peW0v2F4BCY034GRpU9WnuA=
|
||||
github.com/samber/lo v1.39.0/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA=
|
||||
github.com/shirou/gopsutil/v3 v3.24.3 h1:eoUGJSmdfLzJ3mxIhmOAhgKEKgQkeOwKpz1NbhVnuPE=
|
||||
github.com/shirou/gopsutil/v3 v3.24.3/go.mod h1:JpND7O217xa72ewWz9zN2eIIkPWsDN/3pl0H8Qt0uwg=
|
||||
github.com/shirou/gopsutil/v3 v3.24.4 h1:dEHgzZXt4LMNm+oYELpzl9YCqV65Yr/6SfrvgRBtXeU=
|
||||
github.com/shirou/gopsutil/v3 v3.24.4/go.mod h1:lTd2mdiOspcqLgAnr9/nGi71NkeMpWKdmhuxm9GusH8=
|
||||
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
|
||||
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
|
||||
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
|
||||
|
@ -224,18 +222,18 @@ go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBs
|
|||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
|
||||
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
||||
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f h1:99ci1mjWVBWwJiEKYY6jWa4d2nTQVIEhZIptnrVb1XY=
|
||||
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI=
|
||||
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
||||
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
||||
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
|
@ -255,25 +253,26 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
|
||||
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY=
|
||||
golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg=
|
||||
golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw=
|
||||
golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
|
||||
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
lukechampine.com/blake3 v1.2.2 h1:wEAbSg0IVU4ih44CVlpMqMZMpzr5hf/6aqodLlevd/w=
|
||||
lukechampine.com/blake3 v1.2.2/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=
|
||||
lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE=
|
||||
lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=
|
||||
|
|
|
@ -253,6 +253,7 @@ func updateDNS(c *config.DNS, ruleProvider map[string]provider.RuleProvider, gen
|
|||
resolver.DefaultResolver = r
|
||||
resolver.DefaultHostMapper = m
|
||||
resolver.DefaultLocalServer = dns.NewLocalServer(r, m)
|
||||
resolver.UseSystemHosts = c.UseSystemHosts
|
||||
|
||||
if pr.Invalid() {
|
||||
resolver.ProxyServerHostResolver = pr
|
||||
|
|
|
@ -4,11 +4,11 @@ import (
|
|||
"net/http"
|
||||
"net/netip"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"github.com/metacubex/mihomo/adapter/inbound"
|
||||
"github.com/metacubex/mihomo/component/dialer"
|
||||
"github.com/metacubex/mihomo/component/resolver"
|
||||
"github.com/metacubex/mihomo/component/updater"
|
||||
"github.com/metacubex/mihomo/config"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
"github.com/metacubex/mihomo/hub/executor"
|
||||
|
@ -21,11 +21,6 @@ import (
|
|||
"github.com/go-chi/render"
|
||||
)
|
||||
|
||||
var (
|
||||
updateGeoMux sync.Mutex
|
||||
updatingGeo = false
|
||||
)
|
||||
|
||||
func configRouter() http.Handler {
|
||||
r := chi.NewRouter()
|
||||
r.Get("/", getConfigs)
|
||||
|
@ -369,40 +364,47 @@ func updateConfigs(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func updateGeoDatabases(w http.ResponseWriter, r *http.Request) {
|
||||
updateGeoMux.Lock()
|
||||
|
||||
if updatingGeo {
|
||||
updateGeoMux.Unlock()
|
||||
render.Status(r, http.StatusBadRequest)
|
||||
render.JSON(w, r, newError("updating..."))
|
||||
return
|
||||
}
|
||||
|
||||
updatingGeo = true
|
||||
updateGeoMux.Unlock()
|
||||
updateNotification := make(chan struct{})
|
||||
errorChannel := make(chan error, 1)
|
||||
done := make(chan struct{})
|
||||
defer func() {
|
||||
close(updateNotification)
|
||||
close(errorChannel)
|
||||
}()
|
||||
|
||||
go func() {
|
||||
defer func() {
|
||||
updatingGeo = false
|
||||
}()
|
||||
defer close(done)
|
||||
for {
|
||||
select {
|
||||
case <-updateNotification:
|
||||
cfg, err := executor.ParseWithPath(C.Path.Config())
|
||||
if err != nil {
|
||||
log.Errorln("[REST-API] update GEO databases failed: %v", err)
|
||||
render.Status(r, http.StatusInternalServerError)
|
||||
render.JSON(w, r, newError("Error parsing configuration"))
|
||||
return
|
||||
}
|
||||
|
||||
log.Warnln("[REST-API] updating GEO databases...")
|
||||
|
||||
if err := config.UpdateGeoDatabases(); err != nil {
|
||||
log.Errorln("[REST-API] update GEO databases failed: %v", err)
|
||||
return
|
||||
log.Warnln("[REST-API] update GEO databases success, applying config")
|
||||
executor.ApplyConfig(cfg, false)
|
||||
return
|
||||
case err := <-errorChannel:
|
||||
log.Errorln("[REST-API] update GEO databases failed: %v", err)
|
||||
render.Status(r, http.StatusInternalServerError)
|
||||
render.JSON(w, r, err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
cfg, err := executor.ParseWithPath(C.Path.Config())
|
||||
if err != nil {
|
||||
log.Errorln("[REST-API] update GEO databases failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Warnln("[REST-API] update GEO databases successful, apply config...")
|
||||
|
||||
executor.ApplyConfig(cfg, false)
|
||||
}()
|
||||
|
||||
go func() {
|
||||
err := updater.UpdateGeoDatabases(updateNotification)
|
||||
if err != nil {
|
||||
errorChannel <- err
|
||||
}
|
||||
}()
|
||||
|
||||
<-done
|
||||
|
||||
render.NoContent(w, r)
|
||||
}
|
||||
|
|
|
@ -6,8 +6,7 @@ import (
|
|||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/metacubex/mihomo/config"
|
||||
"github.com/metacubex/mihomo/hub/updater"
|
||||
"github.com/metacubex/mihomo/component/updater"
|
||||
"github.com/metacubex/mihomo/log"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
@ -18,6 +17,7 @@ func upgradeRouter() http.Handler {
|
|||
r := chi.NewRouter()
|
||||
r.Post("/", upgradeCore)
|
||||
r.Post("/ui", updateUI)
|
||||
r.Post("/geo", updateGeoDatabases)
|
||||
return r
|
||||
}
|
||||
|
||||
|
@ -31,7 +31,7 @@ func upgradeCore(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
err = updater.Update(execPath)
|
||||
err = updater.UpdateCore(execPath)
|
||||
if err != nil {
|
||||
log.Warnln("%s", err)
|
||||
render.Status(r, http.StatusInternalServerError)
|
||||
|
@ -48,9 +48,9 @@ func upgradeCore(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func updateUI(w http.ResponseWriter, r *http.Request) {
|
||||
err := config.UpdateUI()
|
||||
err := updater.UpdateUI()
|
||||
if err != nil {
|
||||
if errors.Is(err, config.ErrIncompleteConf) {
|
||||
if errors.Is(err, updater.ErrIncompleteConf) {
|
||||
log.Warnln("%s", err)
|
||||
render.Status(r, http.StatusNotImplemented)
|
||||
render.JSON(w, r, newError(fmt.Sprintf("%s", err)))
|
||||
|
|
|
@ -8,7 +8,6 @@ import (
|
|||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
_ "unsafe"
|
||||
|
||||
"github.com/metacubex/mihomo/adapter/inbound"
|
||||
"github.com/metacubex/mihomo/common/lru"
|
||||
|
@ -18,11 +17,19 @@ import (
|
|||
"github.com/metacubex/mihomo/log"
|
||||
)
|
||||
|
||||
//go:linkname registerOnHitEOF net/http.registerOnHitEOF
|
||||
func registerOnHitEOF(rc io.ReadCloser, fn func())
|
||||
type bodyWrapper struct {
|
||||
io.ReadCloser
|
||||
once sync.Once
|
||||
onHitEOF func()
|
||||
}
|
||||
|
||||
//go:linkname requestBodyRemains net/http.requestBodyRemains
|
||||
func requestBodyRemains(rc io.ReadCloser) bool
|
||||
func (b *bodyWrapper) Read(p []byte) (n int, err error) {
|
||||
n, err = b.ReadCloser.Read(p)
|
||||
if err == io.EOF && b.onHitEOF != nil {
|
||||
b.once.Do(b.onHitEOF)
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func HandleConn(c net.Conn, tunnel C.Tunnel, cache *lru.LruCache[string, bool], additions ...inbound.Addition) {
|
||||
client := newClient(c, tunnel, additions...)
|
||||
|
@ -100,10 +107,10 @@ func HandleConn(c net.Conn, tunnel C.Tunnel, cache *lru.LruCache[string, bool],
|
|||
}
|
||||
}()
|
||||
}
|
||||
if requestBodyRemains(request.Body) {
|
||||
registerOnHitEOF(request.Body, startBackgroundRead)
|
||||
} else {
|
||||
if request.Body == nil || request.Body == http.NoBody {
|
||||
startBackgroundRead()
|
||||
} else {
|
||||
request.Body = &bodyWrapper{ReadCloser: request.Body, onHitEOF: startBackgroundRead}
|
||||
}
|
||||
resp, err = client.Do(request)
|
||||
if err != nil {
|
||||
|
|
|
@ -20,12 +20,14 @@ import (
|
|||
|
||||
tun "github.com/metacubex/sing-tun"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/control"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
"github.com/sagernet/sing/common/ranges"
|
||||
)
|
||||
|
||||
var InterfaceName = "Meta"
|
||||
var EnforceBindInterface = false
|
||||
|
||||
type Listener struct {
|
||||
closed bool
|
||||
|
@ -263,6 +265,8 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis
|
|||
UDPTimeout: udpTimeout,
|
||||
Handler: handler,
|
||||
Logger: log.SingLogger,
|
||||
InterfaceFinder: control.DefaultInterfaceFinder(),
|
||||
EnforceBindInterface: EnforceBindInterface,
|
||||
}
|
||||
|
||||
if options.FileDescriptor > 0 {
|
||||
|
|
|
@ -3,6 +3,7 @@ package sing_tun
|
|||
import (
|
||||
"time"
|
||||
|
||||
"github.com/metacubex/mihomo/constant/features"
|
||||
"github.com/metacubex/mihomo/log"
|
||||
|
||||
tun "github.com/metacubex/sing-tun"
|
||||
|
@ -27,4 +28,9 @@ func tunNew(options tun.Options) (tunIf tun.Tun, err error) {
|
|||
|
||||
func init() {
|
||||
tun.TunnelType = InterfaceName
|
||||
|
||||
if features.WindowsMajorVersion < 10 {
|
||||
// to resolve "bind: The requested address is not valid in its context"
|
||||
EnforceBindInterface = true
|
||||
}
|
||||
}
|
||||
|
|
58
main.go
58
main.go
|
@ -8,10 +8,9 @@ import (
|
|||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/metacubex/mihomo/component/updater"
|
||||
"github.com/metacubex/mihomo/config"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
"github.com/metacubex/mihomo/constant/features"
|
||||
|
@ -32,8 +31,6 @@ var (
|
|||
externalController string
|
||||
externalControllerUnix string
|
||||
secret string
|
||||
updateGeoMux sync.Mutex
|
||||
updatingGeo = false
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -116,16 +113,23 @@ func main() {
|
|||
}
|
||||
|
||||
if C.GeoAutoUpdate {
|
||||
ticker := time.NewTicker(time.Duration(C.GeoUpdateInterval) * time.Hour)
|
||||
updateNotification := make(chan struct{})
|
||||
go updater.RegisterGeoUpdater(updateNotification)
|
||||
|
||||
log.Infoln("[GEO] Start update GEO database every %d hours", C.GeoUpdateInterval)
|
||||
go func() {
|
||||
for range ticker.C {
|
||||
updateGeoDatabases()
|
||||
for range updateNotification {
|
||||
cfg, err := executor.ParseWithPath(C.Path.Config())
|
||||
if err != nil {
|
||||
log.Errorln("[GEO] update GEO databases failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Warnln("[GEO] update GEO databases success, applying config")
|
||||
|
||||
executor.ApplyConfig(cfg, false)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
defer executor.Shutdown()
|
||||
|
||||
termSign := make(chan os.Signal, 1)
|
||||
|
@ -145,39 +149,3 @@ func main() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func updateGeoDatabases() {
|
||||
log.Infoln("[GEO] Start updating GEO database")
|
||||
updateGeoMux.Lock()
|
||||
|
||||
if updatingGeo {
|
||||
updateGeoMux.Unlock()
|
||||
log.Infoln("[GEO] GEO database is updating, skip")
|
||||
return
|
||||
}
|
||||
|
||||
updatingGeo = true
|
||||
updateGeoMux.Unlock()
|
||||
|
||||
go func() {
|
||||
defer func() {
|
||||
updatingGeo = false
|
||||
}()
|
||||
|
||||
log.Infoln("[GEO] Updating GEO database")
|
||||
|
||||
if err := config.UpdateGeoDatabases(); err != nil {
|
||||
log.Errorln("[GEO] update GEO database error: %s", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
cfg, err := executor.ParseWithPath(C.Path.Config())
|
||||
if err != nil {
|
||||
log.Errorln("[GEO] update GEO database failed: %s", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
log.Infoln("[GEO] Update GEO database success, apply new config")
|
||||
executor.ApplyConfig(cfg, false)
|
||||
}()
|
||||
}
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
|
||||
"github.com/dlclark/regexp2"
|
||||
)
|
||||
|
||||
type DomainRegex struct {
|
||||
*Base
|
||||
regex *regexp.Regexp
|
||||
regex *regexp2.Regexp
|
||||
adapter string
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,8 @@ func (dr *DomainRegex) RuleType() C.RuleType {
|
|||
|
||||
func (dr *DomainRegex) Match(metadata *C.Metadata) (bool, string) {
|
||||
domain := metadata.RuleHost()
|
||||
return dr.regex.MatchString(domain), dr.adapter
|
||||
match, _ := dr.regex.MatchString(domain)
|
||||
return match, dr.adapter
|
||||
}
|
||||
|
||||
func (dr *DomainRegex) Adapter() string {
|
||||
|
@ -30,7 +31,7 @@ func (dr *DomainRegex) Payload() string {
|
|||
}
|
||||
|
||||
func NewDomainRegex(regex string, adapter string) (*DomainRegex, error) {
|
||||
r, err := regexp.Compile(regex)
|
||||
r, err := regexp2.Compile(regex, regexp2.IgnoreCase)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@ import (
|
|||
"strings"
|
||||
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
|
||||
"github.com/dlclark/regexp2"
|
||||
)
|
||||
|
||||
type Process struct {
|
||||
|
@ -11,21 +13,36 @@ type Process struct {
|
|||
adapter string
|
||||
process string
|
||||
nameOnly bool
|
||||
regexp *regexp2.Regexp
|
||||
}
|
||||
|
||||
func (ps *Process) RuleType() C.RuleType {
|
||||
if ps.nameOnly {
|
||||
return C.Process
|
||||
if ps.regexp != nil {
|
||||
return C.ProcessNameRegex
|
||||
}
|
||||
return C.ProcessName
|
||||
}
|
||||
|
||||
if ps.regexp != nil {
|
||||
return C.ProcessPathRegex
|
||||
}
|
||||
return C.ProcessPath
|
||||
}
|
||||
|
||||
func (ps *Process) Match(metadata *C.Metadata) (bool, string) {
|
||||
if ps.nameOnly {
|
||||
if ps.regexp != nil {
|
||||
match, _ := ps.regexp.MatchString(metadata.Process)
|
||||
return match, ps.adapter
|
||||
}
|
||||
return strings.EqualFold(metadata.Process, ps.process), ps.adapter
|
||||
}
|
||||
|
||||
if ps.regexp != nil {
|
||||
match, _ := ps.regexp.MatchString(metadata.ProcessPath)
|
||||
return match, ps.adapter
|
||||
}
|
||||
return strings.EqualFold(metadata.ProcessPath, ps.process), ps.adapter
|
||||
}
|
||||
|
||||
|
@ -41,11 +58,20 @@ func (ps *Process) ShouldFindProcess() bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func NewProcess(process string, adapter string, nameOnly bool) (*Process, error) {
|
||||
func NewProcess(process string, adapter string, nameOnly bool, regex bool) (*Process, error) {
|
||||
var r *regexp2.Regexp
|
||||
var err error
|
||||
if regex {
|
||||
r, err = regexp2.Compile(process, regexp2.IgnoreCase)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return &Process{
|
||||
Base: &Base{},
|
||||
adapter: adapter,
|
||||
process: process,
|
||||
nameOnly: nameOnly,
|
||||
regexp: r,
|
||||
}, nil
|
||||
}
|
||||
|
|
|
@ -50,9 +50,13 @@ func ParseRule(tp, payload, target string, params []string, subRules map[string]
|
|||
case "DSCP":
|
||||
parsed, parseErr = RC.NewDSCP(payload, target)
|
||||
case "PROCESS-NAME":
|
||||
parsed, parseErr = RC.NewProcess(payload, target, true)
|
||||
parsed, parseErr = RC.NewProcess(payload, target, true, false)
|
||||
case "PROCESS-PATH":
|
||||
parsed, parseErr = RC.NewProcess(payload, target, false)
|
||||
parsed, parseErr = RC.NewProcess(payload, target, false, false)
|
||||
case "PROCESS-NAME-REGEX":
|
||||
parsed, parseErr = RC.NewProcess(payload, target, true, true)
|
||||
case "PROCESS-PATH-REGEX":
|
||||
parsed, parseErr = RC.NewProcess(payload, target, false, true)
|
||||
case "NETWORK":
|
||||
parsed, parseErr = RC.NewNetworkType(payload, target)
|
||||
case "UID":
|
||||
|
|
|
@ -77,7 +77,7 @@ func ruleParse(ruleRaw string) (string, string, []string) {
|
|||
} else if len(item) == 2 {
|
||||
return item[0], item[1], nil
|
||||
} else if len(item) > 2 {
|
||||
if item[0] == "NOT" || item[0] == "OR" || item[0] == "AND" || item[0] == "SUB-RULE" || item[0] == "DOMAIN-REGEX" {
|
||||
if item[0] == "NOT" || item[0] == "OR" || item[0] == "AND" || item[0] == "SUB-RULE" || item[0] == "DOMAIN-REGEX" || item[0] == "PROCESS-NAME-REGEX" || item[0] == "PROCESS-PATH-REGEX" {
|
||||
return item[0], strings.Join(item[1:len(item)], ","), nil
|
||||
} else {
|
||||
return item[0], item[1], item[2:]
|
||||
|
|
|
@ -406,7 +406,7 @@ func (c *quicPktConn) WriteTo(p []byte, addr string) error {
|
|||
if errors.As(err, &errSize) {
|
||||
// need to frag
|
||||
msg.MsgID = uint16(fastrand.Intn(0xFFFF)) + 1 // msgID must be > 0 when fragCount > 1
|
||||
fragMsgs := fragUDPMessage(msg, int(errSize.PeerMaxDatagramFrameSize))
|
||||
fragMsgs := fragUDPMessage(msg, int(errSize.MaxDatagramPayloadSize))
|
||||
for _, fragMsg := range fragMsgs {
|
||||
msgBuf.Reset()
|
||||
_ = struc.Pack(&msgBuf, &fragMsg)
|
||||
|
|
|
@ -65,7 +65,10 @@ func (ho *HTTPObfs) Write(b []byte) (int, error) {
|
|||
if ho.firstRequest {
|
||||
randBytes := make([]byte, 16)
|
||||
fastrand.Read(randBytes)
|
||||
req, _ := http.NewRequest("GET", fmt.Sprintf("http://%s/", ho.host), bytes.NewBuffer(b[:]))
|
||||
req, err := http.NewRequest("GET", fmt.Sprintf("http://%s/", ho.host), bytes.NewBuffer(b[:]))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
req.Header.Set("User-Agent", fmt.Sprintf("curl/7.%d.%d", fastrand.Int()%54, fastrand.Int()%2))
|
||||
req.Header.Set("Upgrade", "websocket")
|
||||
req.Header.Set("Connection", "Upgrade")
|
||||
|
@ -75,7 +78,7 @@ func (ho *HTTPObfs) Write(b []byte) (int, error) {
|
|||
}
|
||||
req.Header.Set("Sec-WebSocket-Key", base64.URLEncoding.EncodeToString(randBytes))
|
||||
req.ContentLength = int64(len(b))
|
||||
err := req.Write(ho.Conn)
|
||||
err = req.Write(ho.Conn)
|
||||
ho.firstRequest = false
|
||||
return len(b), err
|
||||
}
|
||||
|
|
|
@ -9,9 +9,9 @@ import (
|
|||
tlsC "github.com/metacubex/mihomo/component/tls"
|
||||
"github.com/metacubex/mihomo/log"
|
||||
|
||||
utls "github.com/metacubex/utls"
|
||||
"github.com/sagernet/sing-shadowtls"
|
||||
sing_common "github.com/sagernet/sing/common"
|
||||
utls "github.com/sagernet/utls"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
|
@ -129,6 +129,12 @@ func (t *Trojan) StreamWebsocketConn(ctx context.Context, conn net.Conn, wsOptio
|
|||
ServerName: t.option.ServerName,
|
||||
}
|
||||
|
||||
var err error
|
||||
tlsConfig, err = ca.GetSpecifiedFingerprintTLSConfig(tlsConfig, t.option.Fingerprint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return vmess.StreamWebsocketConn(ctx, conn, &vmess.WebsocketConfig{
|
||||
Host: wsOptions.Host,
|
||||
Port: wsOptions.Port,
|
||||
|
|
|
@ -123,7 +123,7 @@ func (q *quicStreamPacketConn) WaitReadFrom() (data []byte, put func(), addr net
|
|||
|
||||
func (q *quicStreamPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
if q.udpRelayMode != common.QUIC && len(p) > q.maxUdpRelayPacketSize {
|
||||
return 0, &quic.DatagramTooLargeError{PeerMaxDatagramFrameSize: int64(q.maxUdpRelayPacketSize)}
|
||||
return 0, &quic.DatagramTooLargeError{MaxDatagramPayloadSize: int64(q.maxUdpRelayPacketSize)}
|
||||
}
|
||||
if q.closed {
|
||||
return 0, net.ErrClosed
|
||||
|
|
|
@ -137,7 +137,7 @@ func (q *quicStreamPacketConn) WaitReadFrom() (data []byte, put func(), addr net
|
|||
|
||||
func (q *quicStreamPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
if len(p) > 0xffff { // uint16 max
|
||||
return 0, &quic.DatagramTooLargeError{PeerMaxDatagramFrameSize: 0xffff}
|
||||
return 0, &quic.DatagramTooLargeError{MaxDatagramPayloadSize: 0xffff}
|
||||
}
|
||||
if q.closed {
|
||||
return 0, net.ErrClosed
|
||||
|
@ -189,7 +189,7 @@ func (q *quicStreamPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err erro
|
|||
|
||||
var tooLarge *quic.DatagramTooLargeError
|
||||
if errors.As(err, &tooLarge) {
|
||||
err = fragWriteNative(q.quicConn, packet, buf, int(tooLarge.PeerMaxDatagramFrameSize)-PacketOverHead)
|
||||
err = fragWriteNative(q.quicConn, packet, buf, int(tooLarge.MaxDatagramPayloadSize)-PacketOverHead)
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
|
|
|
@ -14,7 +14,7 @@ import (
|
|||
"github.com/metacubex/mihomo/log"
|
||||
|
||||
"github.com/gofrs/uuid/v5"
|
||||
utls "github.com/sagernet/utls"
|
||||
utls "github.com/metacubex/utls"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
|
@ -14,8 +14,8 @@ import (
|
|||
tlsC "github.com/metacubex/mihomo/component/tls"
|
||||
|
||||
"github.com/gofrs/uuid/v5"
|
||||
utls "github.com/metacubex/utls"
|
||||
"github.com/sagernet/sing/common"
|
||||
utls "github.com/sagernet/utls"
|
||||
)
|
||||
|
||||
var ErrNotTLS13 = errors.New("XTLS Vision based on TLS 1.3 outer connection")
|
||||
|
|
|
@ -66,7 +66,10 @@ func (hc *httpConn) Write(b []byte) (int, error) {
|
|||
}
|
||||
|
||||
u := fmt.Sprintf("http://%s%s", net.JoinHostPort(host, "80"), path)
|
||||
req, _ := http.NewRequest(utils.EmptyOr(hc.cfg.Method, http.MethodGet), u, bytes.NewBuffer(b))
|
||||
req, err := http.NewRequest(utils.EmptyOr(hc.cfg.Method, http.MethodGet), u, bytes.NewBuffer(b))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
for key, list := range hc.cfg.Headers {
|
||||
req.Header.Set(key, list[fastrand.Intn(len(list))])
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue