mirror of
https://gitclone.com/github.com/MetaCubeX/Clash.Meta
synced 2025-02-23 01:43:15 +08:00
chore: better keepalive handle
This commit is contained in:
parent
24c6e7d819
commit
f20f371a61
23
common/net/tcp_keepalive.go
Normal file
23
common/net/tcp_keepalive.go
Normal file
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,9 +4,7 @@ package net
|
|||||||
|
|
||||||
import "net"
|
import "net"
|
||||||
|
|
||||||
func TCPKeepAlive(c net.Conn) {
|
func tcpKeepAlive(tcp *net.TCPConn) {
|
||||||
if tcp, ok := c.(*net.TCPConn); ok {
|
_ = tcp.SetKeepAlive(true)
|
||||||
_ = tcp.SetKeepAlive(true)
|
_ = tcp.SetKeepAlivePeriod(KeepAliveInterval)
|
||||||
_ = tcp.SetKeepAlivePeriod(KeepAliveInterval)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -4,12 +4,16 @@ package net
|
|||||||
|
|
||||||
import "net"
|
import "net"
|
||||||
|
|
||||||
func TCPKeepAlive(c net.Conn) {
|
func tcpKeepAlive(tcp *net.TCPConn) {
|
||||||
if tcp, ok := c.(*net.TCPConn); ok {
|
config := net.KeepAliveConfig{
|
||||||
_ = tcp.SetKeepAliveConfig(net.KeepAliveConfig{
|
Enable: true,
|
||||||
Enable: true,
|
Idle: KeepAliveIdle,
|
||||||
Idle: KeepAliveIdle,
|
Interval: KeepAliveInterval,
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
15
common/net/tcp_keepalive_go123_unix.go
Normal file
15
common/net/tcp_keepalive_go123_unix.go
Normal file
@ -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
|
||||||
|
}
|
63
common/net/tcp_keepalive_go123_windows.go
Normal file
63
common/net/tcp_keepalive_go123_windows.go
Normal file
@ -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,12 +4,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
KeepAliveIdle = 0 * time.Second
|
|
||||||
KeepAliveInterval = 0 * time.Second
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func SplitNetworkType(s string) (string, string, error) {
|
func SplitNetworkType(s string) (string, string, error) {
|
||||||
|
@ -13,7 +13,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/metacubex/mihomo/component/resolver"
|
"github.com/metacubex/mihomo/component/resolver"
|
||||||
"github.com/metacubex/mihomo/constant/features"
|
|
||||||
"github.com/metacubex/mihomo/log"
|
"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) {
|
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...)
|
cfg := applyOptions(options...)
|
||||||
|
|
||||||
lc := &net.ListenConfig{}
|
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 {
|
if cfg.addrReuse {
|
||||||
addrReuseToListenConfig(lc)
|
addrReuseToListenConfig(lc)
|
||||||
}
|
}
|
||||||
if cfg.routingMark != 0 {
|
if DefaultSocketHook != nil { // ignore interfaceName, routingMark when DefaultSocketHook not null (in CFMA)
|
||||||
bindMarkToListenConfig(cfg.routingMark, lc, network, address)
|
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)
|
return lc.ListenPacket(ctx, network, address)
|
||||||
@ -149,25 +148,26 @@ func dialContext(ctx context.Context, network string, destination netip.Addr, po
|
|||||||
setMultiPathTCP(dialer)
|
setMultiPathTCP(dialer)
|
||||||
}
|
}
|
||||||
|
|
||||||
if features.CMFA && DefaultSocketHook != nil { // ignore interfaceName, routingMark and tfo in CMFA
|
if DefaultSocketHook != nil { // ignore interfaceName, routingMark and tfo when DefaultSocketHook not null (in CFMA)
|
||||||
return dialContextHooked(ctx, dialer, network, address)
|
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)
|
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
|
|
||||||
}
|
|
27
component/dialer/socket_hook.go
Normal file
27
component/dialer/socket_hook.go
Normal file
@ -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)
|
||||||
|
})
|
||||||
|
}
|
@ -340,6 +340,7 @@ type RawConfig struct {
|
|||||||
GlobalUA string `yaml:"global-ua"`
|
GlobalUA string `yaml:"global-ua"`
|
||||||
KeepAliveIdle int `yaml:"keep-alive-idle"`
|
KeepAliveIdle int `yaml:"keep-alive-idle"`
|
||||||
KeepAliveInterval int `yaml:"keep-alive-interval"`
|
KeepAliveInterval int `yaml:"keep-alive-interval"`
|
||||||
|
DisableKeepAlive bool `yaml:"disable-keep-alive"`
|
||||||
|
|
||||||
Sniffer RawSniffer `yaml:"sniffer" json:"sniffer"`
|
Sniffer RawSniffer `yaml:"sniffer" json:"sniffer"`
|
||||||
ProxyProvider map[string]map[string]any `yaml:"proxy-providers"`
|
ProxyProvider map[string]map[string]any `yaml:"proxy-providers"`
|
||||||
@ -657,6 +658,7 @@ func parseGeneral(cfg *RawConfig) (*General, error) {
|
|||||||
if cfg.KeepAliveInterval != 0 {
|
if cfg.KeepAliveInterval != 0 {
|
||||||
N.KeepAliveInterval = time.Duration(cfg.KeepAliveInterval) * time.Second
|
N.KeepAliveInterval = time.Duration(cfg.KeepAliveInterval) * time.Second
|
||||||
}
|
}
|
||||||
|
N.DisableKeepAlive = cfg.DisableKeepAlive
|
||||||
|
|
||||||
updater.ExternalUIPath = cfg.ExternalUI
|
updater.ExternalUIPath = cfg.ExternalUI
|
||||||
// checkout externalUI exist
|
// checkout externalUI exist
|
||||||
|
@ -82,8 +82,9 @@ external-doh-server: /dns-query
|
|||||||
global-client-fingerprint: chrome
|
global-client-fingerprint: chrome
|
||||||
|
|
||||||
# TCP keep alive interval
|
# TCP keep alive interval
|
||||||
# keep-alive-idle: 7200
|
# disable-keep-alive: false #目前在android端强制为true
|
||||||
# keep-alive-interval: 75
|
# keep-alive-idle: 15
|
||||||
|
# keep-alive-interval: 15
|
||||||
|
|
||||||
# routing-mark:6666 # 配置 fwmark 仅用于 Linux
|
# routing-mark:6666 # 配置 fwmark 仅用于 Linux
|
||||||
experimental:
|
experimental:
|
||||||
|
@ -4,9 +4,9 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/metacubex/mihomo/adapter/inbound"
|
"github.com/metacubex/mihomo/adapter/inbound"
|
||||||
|
N "github.com/metacubex/mihomo/common/net"
|
||||||
"github.com/metacubex/mihomo/component/auth"
|
"github.com/metacubex/mihomo/component/auth"
|
||||||
C "github.com/metacubex/mihomo/constant"
|
C "github.com/metacubex/mihomo/constant"
|
||||||
"github.com/metacubex/mihomo/constant/features"
|
|
||||||
authStore "github.com/metacubex/mihomo/listener/auth"
|
authStore "github.com/metacubex/mihomo/listener/auth"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -74,11 +74,7 @@ func NewWithAuthenticator(addr string, tunnel C.Tunnel, authenticator auth.Authe
|
|||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if features.CMFA {
|
N.TCPKeepAlive(conn)
|
||||||
if t, ok := conn.(*net.TCPConn); ok {
|
|
||||||
t.SetKeepAlive(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if isDefault { // only apply on default listener
|
if isDefault { // only apply on default listener
|
||||||
if !inbound.IsRemoteAddrDisAllowed(conn.RemoteAddr()) {
|
if !inbound.IsRemoteAddrDisAllowed(conn.RemoteAddr()) {
|
||||||
_ = conn.Close()
|
_ = conn.Close()
|
||||||
|
Loading…
Reference in New Issue
Block a user