sing-box/outbound/direct_loopback_detect.go

187 lines
4.5 KiB
Go
Raw Permalink Normal View History

2023-12-20 02:52:11 +00:00
package outbound
import (
"net"
"net/netip"
"sync"
2024-04-12 01:24:49 +00:00
"github.com/sagernet/sing-box/adapter"
2023-12-20 02:52:11 +00:00
M "github.com/sagernet/sing/common/metadata"
2024-02-01 02:41:38 +00:00
N "github.com/sagernet/sing/common/network"
2023-12-20 02:52:11 +00:00
)
type loopBackDetector struct {
2024-04-12 01:24:49 +00:00
router adapter.Router
2023-12-20 02:52:11 +00:00
connAccess sync.RWMutex
packetConnAccess sync.RWMutex
2024-04-17 13:35:26 +00:00
connMap map[netip.AddrPort]netip.AddrPort
packetConnMap map[uint16]uint16
2023-12-20 02:52:11 +00:00
}
2024-04-12 01:24:49 +00:00
func newLoopBackDetector(router adapter.Router) *loopBackDetector {
2023-12-20 02:52:11 +00:00
return &loopBackDetector{
2024-04-12 01:24:49 +00:00
router: router,
2024-04-17 13:35:26 +00:00
connMap: make(map[netip.AddrPort]netip.AddrPort),
packetConnMap: make(map[uint16]uint16),
2023-12-20 02:52:11 +00:00
}
}
func (l *loopBackDetector) NewConn(conn net.Conn) net.Conn {
2024-04-17 13:35:26 +00:00
source := M.AddrPortFromNet(conn.LocalAddr())
if !source.IsValid() {
2023-12-20 02:52:11 +00:00
return conn
}
if udpConn, isUDPConn := conn.(abstractUDPConn); isUDPConn {
2024-04-12 01:24:49 +00:00
if !source.Addr().IsLoopback() {
2024-04-17 13:35:26 +00:00
_, err := l.router.InterfaceFinder().InterfaceByAddr(source.Addr())
if err != nil {
return conn
}
2024-04-12 01:24:49 +00:00
}
2024-04-17 13:35:26 +00:00
if !N.IsPublicAddr(source.Addr()) {
return conn
}
2023-12-20 02:52:11 +00:00
l.packetConnAccess.Lock()
2024-04-17 13:35:26 +00:00
l.packetConnMap[source.Port()] = M.AddrPortFromNet(conn.RemoteAddr()).Port()
2023-12-20 02:52:11 +00:00
l.packetConnAccess.Unlock()
2024-04-17 13:35:26 +00:00
return &loopBackDetectUDPWrapper{abstractUDPConn: udpConn, detector: l, connPort: source.Port()}
2023-12-20 02:52:11 +00:00
} else {
l.connAccess.Lock()
2024-04-17 13:35:26 +00:00
l.connMap[source] = M.AddrPortFromNet(conn.RemoteAddr())
2023-12-20 02:52:11 +00:00
l.connAccess.Unlock()
2024-04-17 13:35:26 +00:00
return &loopBackDetectWrapper{Conn: conn, detector: l, connAddr: source}
2023-12-20 02:52:11 +00:00
}
}
2024-04-17 13:35:26 +00:00
func (l *loopBackDetector) NewPacketConn(conn N.NetPacketConn, destination M.Socksaddr) N.NetPacketConn {
source := M.AddrPortFromNet(conn.LocalAddr())
if !source.IsValid() {
2023-12-20 02:52:11 +00:00
return conn
}
2024-04-12 01:24:49 +00:00
if !source.Addr().IsLoopback() {
_, err := l.router.InterfaceFinder().InterfaceByAddr(source.Addr())
if err != nil {
return conn
}
}
2023-12-20 02:52:11 +00:00
l.packetConnAccess.Lock()
2024-04-17 13:35:26 +00:00
l.packetConnMap[source.Port()] = destination.AddrPort().Port()
2023-12-20 02:52:11 +00:00
l.packetConnAccess.Unlock()
2024-04-17 13:35:26 +00:00
return &loopBackDetectPacketWrapper{NetPacketConn: conn, detector: l, connPort: source.Port()}
2023-12-20 02:52:11 +00:00
}
2024-04-17 13:35:26 +00:00
func (l *loopBackDetector) CheckConn(source netip.AddrPort, local netip.AddrPort) bool {
2023-12-20 02:52:11 +00:00
l.connAccess.RLock()
defer l.connAccess.RUnlock()
2024-04-17 13:35:26 +00:00
destination, loaded := l.connMap[source]
return loaded && destination != local
2023-12-20 02:52:11 +00:00
}
2024-04-17 13:35:26 +00:00
func (l *loopBackDetector) CheckPacketConn(source netip.AddrPort, local netip.AddrPort) bool {
if !source.IsValid() {
return false
}
2024-04-12 01:24:49 +00:00
if !source.Addr().IsLoopback() {
2024-04-17 13:35:26 +00:00
_, err := l.router.InterfaceFinder().InterfaceByAddr(source.Addr())
if err != nil {
return false
}
2024-04-12 01:24:49 +00:00
}
2024-04-17 13:35:26 +00:00
if N.IsPublicAddr(source.Addr()) {
return false
}
2023-12-20 02:52:11 +00:00
l.packetConnAccess.RLock()
defer l.packetConnAccess.RUnlock()
2024-04-17 13:35:26 +00:00
destinationPort, loaded := l.packetConnMap[source.Port()]
return loaded && destinationPort != local.Port()
2023-12-20 02:52:11 +00:00
}
type loopBackDetectWrapper struct {
net.Conn
detector *loopBackDetector
connAddr netip.AddrPort
closeOnce sync.Once
}
func (w *loopBackDetectWrapper) Close() error {
w.closeOnce.Do(func() {
w.detector.connAccess.Lock()
delete(w.detector.connMap, w.connAddr)
w.detector.connAccess.Unlock()
})
return w.Conn.Close()
}
func (w *loopBackDetectWrapper) ReaderReplaceable() bool {
return true
}
func (w *loopBackDetectWrapper) WriterReplaceable() bool {
return true
}
func (w *loopBackDetectWrapper) Upstream() any {
return w.Conn
}
type loopBackDetectPacketWrapper struct {
2024-02-01 02:41:38 +00:00
N.NetPacketConn
2023-12-20 02:52:11 +00:00
detector *loopBackDetector
2024-04-17 13:35:26 +00:00
connPort uint16
2023-12-20 02:52:11 +00:00
closeOnce sync.Once
}
func (w *loopBackDetectPacketWrapper) Close() error {
w.closeOnce.Do(func() {
w.detector.packetConnAccess.Lock()
2024-04-17 13:35:26 +00:00
delete(w.detector.packetConnMap, w.connPort)
2023-12-20 02:52:11 +00:00
w.detector.packetConnAccess.Unlock()
})
2024-02-01 02:41:38 +00:00
return w.NetPacketConn.Close()
2023-12-20 02:52:11 +00:00
}
func (w *loopBackDetectPacketWrapper) ReaderReplaceable() bool {
return true
}
func (w *loopBackDetectPacketWrapper) WriterReplaceable() bool {
return true
}
func (w *loopBackDetectPacketWrapper) Upstream() any {
2024-02-01 02:41:38 +00:00
return w.NetPacketConn
2023-12-20 02:52:11 +00:00
}
type abstractUDPConn interface {
net.Conn
net.PacketConn
}
type loopBackDetectUDPWrapper struct {
abstractUDPConn
detector *loopBackDetector
2024-04-17 13:35:26 +00:00
connPort uint16
2023-12-20 02:52:11 +00:00
closeOnce sync.Once
}
func (w *loopBackDetectUDPWrapper) Close() error {
w.closeOnce.Do(func() {
w.detector.packetConnAccess.Lock()
2024-04-17 13:35:26 +00:00
delete(w.detector.packetConnMap, w.connPort)
2023-12-20 02:52:11 +00:00
w.detector.packetConnAccess.Unlock()
})
return w.abstractUDPConn.Close()
}
func (w *loopBackDetectUDPWrapper) ReaderReplaceable() bool {
return true
}
func (w *loopBackDetectUDPWrapper) WriterReplaceable() bool {
return true
}
func (w *loopBackDetectUDPWrapper) Upstream() any {
return w.abstractUDPConn
}