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