mirror of
https://github.com/MetaCubeX/mihomo.git
synced 2026-03-04 21:07:30 +00:00
chore: add loopback detect for direct outbound
This commit is contained in:
@@ -3,6 +3,7 @@ package outbound
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/netip"
|
||||
|
||||
N "github.com/metacubex/mihomo/common/net"
|
||||
@@ -13,6 +14,7 @@ import (
|
||||
|
||||
type Direct struct {
|
||||
*Base
|
||||
loopBack *loopBackDetector
|
||||
}
|
||||
|
||||
type DirectOption struct {
|
||||
@@ -22,17 +24,23 @@ type DirectOption struct {
|
||||
|
||||
// DialContext implements C.ProxyAdapter
|
||||
func (d *Direct) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) {
|
||||
if d.loopBack.CheckConn(metadata.SourceAddrPort()) {
|
||||
return nil, fmt.Errorf("reject loopback connection to: %s", metadata.RemoteAddress())
|
||||
}
|
||||
opts = append(opts, dialer.WithResolver(resolver.DefaultResolver))
|
||||
c, err := dialer.DialContext(ctx, "tcp", metadata.RemoteAddress(), d.Base.DialOptions(opts...)...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
N.TCPKeepAlive(c)
|
||||
return NewConn(c, d), nil
|
||||
return d.loopBack.NewConn(NewConn(c, d)), nil
|
||||
}
|
||||
|
||||
// ListenPacketContext implements C.ProxyAdapter
|
||||
func (d *Direct) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) {
|
||||
if d.loopBack.CheckPacketConn(metadata.SourceAddrPort()) {
|
||||
return nil, fmt.Errorf("reject loopback connection to: %s", metadata.RemoteAddress())
|
||||
}
|
||||
// net.UDPConn.WriteTo only working with *net.UDPAddr, so we need a net.UDPAddr
|
||||
if !metadata.Resolved() {
|
||||
ip, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, resolver.DefaultResolver)
|
||||
@@ -45,7 +53,7 @@ func (d *Direct) ListenPacketContext(ctx context.Context, metadata *C.Metadata,
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newPacketConn(pc, d), nil
|
||||
return d.loopBack.NewPacketConn(newPacketConn(pc, d)), nil
|
||||
}
|
||||
|
||||
func NewDirectWithOption(option DirectOption) *Direct {
|
||||
@@ -60,6 +68,7 @@ func NewDirectWithOption(option DirectOption) *Direct {
|
||||
rmark: option.RoutingMark,
|
||||
prefer: C.NewDNSPrefer(option.IPVersion),
|
||||
},
|
||||
loopBack: newLoopBackDetector(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,6 +80,7 @@ func NewDirect() *Direct {
|
||||
udp: true,
|
||||
prefer: C.DualStack,
|
||||
},
|
||||
loopBack: newLoopBackDetector(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,5 +92,6 @@ func NewCompatible() *Direct {
|
||||
udp: true,
|
||||
prefer: C.DualStack,
|
||||
},
|
||||
loopBack: newLoopBackDetector(),
|
||||
}
|
||||
}
|
||||
|
||||
68
adapter/outbound/direct_loopback_detect.go
Normal file
68
adapter/outbound/direct_loopback_detect.go
Normal file
@@ -0,0 +1,68 @@
|
||||
package outbound
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
|
||||
"github.com/metacubex/mihomo/common/callback"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
|
||||
"github.com/puzpuzpuz/xsync/v3"
|
||||
)
|
||||
|
||||
type loopBackDetector struct {
|
||||
connMap *xsync.MapOf[netip.AddrPort, struct{}]
|
||||
packetConnMap *xsync.MapOf[netip.AddrPort, struct{}]
|
||||
}
|
||||
|
||||
func newLoopBackDetector() *loopBackDetector {
|
||||
return &loopBackDetector{
|
||||
connMap: xsync.NewMapOf[netip.AddrPort, struct{}](),
|
||||
packetConnMap: xsync.NewMapOf[netip.AddrPort, struct{}](),
|
||||
}
|
||||
}
|
||||
|
||||
func (l *loopBackDetector) NewConn(conn C.Conn) C.Conn {
|
||||
metadata := C.Metadata{}
|
||||
if metadata.SetRemoteAddr(conn.LocalAddr()) != nil {
|
||||
return conn
|
||||
}
|
||||
connAddr := metadata.AddrPort()
|
||||
if !connAddr.IsValid() {
|
||||
return conn
|
||||
}
|
||||
l.connMap.Store(connAddr, struct{}{})
|
||||
return callback.NewCloseCallbackConn(conn, func() {
|
||||
l.connMap.Delete(connAddr)
|
||||
})
|
||||
}
|
||||
|
||||
func (l *loopBackDetector) NewPacketConn(conn C.PacketConn) C.PacketConn {
|
||||
metadata := C.Metadata{}
|
||||
if metadata.SetRemoteAddr(conn.LocalAddr()) != nil {
|
||||
return conn
|
||||
}
|
||||
connAddr := metadata.AddrPort()
|
||||
if !connAddr.IsValid() {
|
||||
return conn
|
||||
}
|
||||
l.packetConnMap.Store(connAddr, struct{}{})
|
||||
return callback.NewCloseCallbackPacketConn(conn, func() {
|
||||
l.packetConnMap.Delete(connAddr)
|
||||
})
|
||||
}
|
||||
|
||||
func (l *loopBackDetector) CheckConn(connAddr netip.AddrPort) bool {
|
||||
if !connAddr.IsValid() {
|
||||
return false
|
||||
}
|
||||
_, ok := l.connMap.Load(connAddr)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (l *loopBackDetector) CheckPacketConn(connAddr netip.AddrPort) bool {
|
||||
if !connAddr.IsValid() {
|
||||
return false
|
||||
}
|
||||
_, ok := l.packetConnMap.Load(connAddr)
|
||||
return ok
|
||||
}
|
||||
Reference in New Issue
Block a user