From d382e630455ef5ba33b533eb45c3466878116502 Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Sat, 9 Mar 2024 18:40:19 +0800 Subject: [PATCH] feat: Experimental supports dialer IP4P address convert form https://github.com/heiher/natmap/wiki/faq --- component/dialer/dialer.go | 29 ++++++++++++++++++++++++++++- config/config.go | 1 + hub/executor/executor.go | 1 + 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index 070d98b816..335d24c3c7 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -7,12 +7,14 @@ import ( "net" "net/netip" "os" + "strconv" "strings" "sync" "time" "github.com/metacubex/mihomo/component/resolver" "github.com/metacubex/mihomo/constant/features" + "github.com/metacubex/mihomo/log" ) const ( @@ -24,6 +26,7 @@ type dialFunc func(ctx context.Context, network string, ips []netip.Addr, port s var ( dialMux sync.Mutex + IP4PEnable bool actualSingleStackDialContext = serialSingleStackDialContext actualDualStackDialContext = serialDualStackDialContext tcpConcurrent = false @@ -128,7 +131,13 @@ func dialContext(ctx context.Context, network string, destination netip.Addr, po return dialContextHooked(ctx, network, destination, port) } - address := net.JoinHostPort(destination.String(), port) + var address string + if IP4PEnable { + NewDestination, NewPort := lookupIP4P(destination.String(), port) + address = net.JoinHostPort(NewDestination, NewPort) + } else { + address = net.JoinHostPort(destination.String(), port) + } netDialer := opt.netDialer switch netDialer.(type) { @@ -383,3 +392,21 @@ func NewDialer(options ...Option) Dialer { opt := applyOptions(options...) return Dialer{Opt: *opt} } + +func DialWithIP4PTranslation(enableIP4PTranslation bool) { + IP4PEnable = enableIP4PTranslation +} + +// kanged from https://github.com/heiher/frp/blob/ip4p/client/ip4p.go + +func lookupIP4P(addr string, port string) (string, string) { + ip := net.ParseIP(addr) + if ip[0] == 0x20 && ip[1] == 0x01 && + ip[2] == 0x00 && ip[3] == 0x00 { + addr = net.IPv4(ip[12], ip[13], ip[14], ip[15]).String() + port = strconv.Itoa(int(ip[10])<<8 + int(ip[11])) + log.Debugln("Convert IP4P address %s to %s", ip, net.JoinHostPort(addr, port)) + return addr, port + } + return addr, port +} diff --git a/config/config.go b/config/config.go index d4b9ad89c9..8fec0bab55 100644 --- a/config/config.go +++ b/config/config.go @@ -169,6 +169,7 @@ type Experimental struct { Fingerprints []string `yaml:"fingerprints"` QUICGoDisableGSO bool `yaml:"quic-go-disable-gso"` QUICGoDisableECN bool `yaml:"quic-go-disable-ecn"` + IP4PEnable bool `yaml:"dialer-ip4p-convert"` } // Config is mihomo config manager diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 14e826d7a9..9ae5e421e5 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -197,6 +197,7 @@ func updateExperimental(c *config.Config) { if c.Experimental.QUICGoDisableECN { _ = os.Setenv("QUIC_GO_DISABLE_ECN", strconv.FormatBool(true)) } + dialer.DialWithIP4PTranslation(c.Experimental.IP4PEnable) } func updateNTP(c *config.NTP) {