1
0
世界 3 жил өмнө
parent
commit
cbab86ae38

+ 13 - 80
common/redir/tproxy_linux.go

@@ -2,14 +2,11 @@ package redir
 
 import (
 	"encoding/binary"
-	"net"
 	"net/netip"
-	"os"
-	"strconv"
 	"syscall"
 
+	"github.com/sagernet/sing/common/control"
 	E "github.com/sagernet/sing/common/exceptions"
-	F "github.com/sagernet/sing/common/format"
 	M "github.com/sagernet/sing/common/metadata"
 
 	"golang.org/x/sys/unix"
@@ -32,6 +29,18 @@ func TProxy(fd uintptr, isIPv6 bool) error {
 	return err
 }
 
+func TProxyWriteBack() control.Func {
+	return func(network, address string, conn syscall.RawConn) error {
+		return control.Raw(conn, func(fd uintptr) error {
+			if M.ParseSocksaddr(address).Addr.Is6() {
+				return syscall.SetsockoptInt(int(fd), syscall.SOL_IPV6, unix.IPV6_TRANSPARENT, 1)
+			} else {
+				return syscall.SetsockoptInt(int(fd), syscall.SOL_IP, syscall.IP_TRANSPARENT, 1)
+			}
+		})
+	}
+}
+
 func GetOriginalDestinationFromOOB(oob []byte) (netip.AddrPort, error) {
 	controlMessages, err := unix.ParseSocketControlMessage(oob)
 	if err != nil {
@@ -46,79 +55,3 @@ func GetOriginalDestinationFromOOB(oob []byte) (netip.AddrPort, error) {
 	}
 	return netip.AddrPort{}, E.New("not found")
 }
-
-func DialUDP(lAddr *net.UDPAddr, rAddr *net.UDPAddr) (*net.UDPConn, error) {
-	rSockAddr, err := udpAddrToSockAddr(rAddr)
-	if err != nil {
-		return nil, err
-	}
-
-	lSockAddr, err := udpAddrToSockAddr(lAddr)
-	if err != nil {
-		return nil, err
-	}
-
-	fd, err := syscall.Socket(udpAddrFamily(lAddr, rAddr), syscall.SOCK_DGRAM, 0)
-	if err != nil {
-		return nil, err
-	}
-
-	if err = syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil {
-		syscall.Close(fd)
-		return nil, err
-	}
-
-	if err = syscall.SetsockoptInt(fd, syscall.SOL_IP, syscall.IP_TRANSPARENT, 1); err != nil {
-		syscall.Close(fd)
-		return nil, err
-	}
-
-	if err = syscall.Bind(fd, lSockAddr); err != nil {
-		syscall.Close(fd)
-		return nil, err
-	}
-
-	if err = syscall.Connect(fd, rSockAddr); err != nil {
-		syscall.Close(fd)
-		return nil, err
-	}
-
-	fdFile := os.NewFile(uintptr(fd), F.ToString("net-udp-dial-", rAddr))
-	defer fdFile.Close()
-
-	c, err := net.FileConn(fdFile)
-	if err != nil {
-		syscall.Close(fd)
-		return nil, err
-	}
-
-	return c.(*net.UDPConn), nil
-}
-
-func udpAddrToSockAddr(addr *net.UDPAddr) (syscall.Sockaddr, error) {
-	switch {
-	case addr.IP.To4() != nil:
-		ip := [4]byte{}
-		copy(ip[:], addr.IP.To4())
-
-		return &syscall.SockaddrInet4{Addr: ip, Port: addr.Port}, nil
-
-	default:
-		ip := [16]byte{}
-		copy(ip[:], addr.IP.To16())
-
-		zoneID, err := strconv.ParseUint(addr.Zone, 10, 32)
-		if err != nil {
-			zoneID = 0
-		}
-
-		return &syscall.SockaddrInet6{Addr: ip, Port: addr.Port, ZoneId: uint32(zoneID)}, nil
-	}
-}
-
-func udpAddrFamily(lAddr, rAddr *net.UDPAddr) int {
-	if (lAddr == nil || lAddr.IP.To4() != nil) && (rAddr == nil || lAddr.IP.To4() != nil) {
-		return syscall.AF_INET
-	}
-	return syscall.AF_INET6
-}

+ 6 - 5
common/redir/tproxy_other.go

@@ -3,19 +3,20 @@
 package redir
 
 import (
-	"net"
 	"net/netip"
 	"os"
+
+	"github.com/sagernet/sing/common/control"
 )
 
 func TProxy(fd uintptr, isIPv6 bool) error {
 	return os.ErrInvalid
 }
 
-func GetOriginalDestinationFromOOB(oob []byte) (netip.AddrPort, error) {
-	return netip.AddrPort{}, os.ErrInvalid
+func TProxyWriteBack() control.Func {
+	return nil
 }
 
-func DialUDP(lAddr *net.UDPAddr, rAddr *net.UDPAddr) (*net.UDPConn, error) {
-	return nil, os.ErrInvalid
+func GetOriginalDestinationFromOOB(oob []byte) (netip.AddrPort, error) {
+	return netip.AddrPort{}, os.ErrInvalid
 }

+ 23 - 17
inbound/tproxy.go

@@ -86,12 +86,13 @@ func (t *TProxy) NewPacket(ctx context.Context, conn N.PacketConn, buffer *buf.B
 	}
 	metadata.Destination = M.SocksaddrFromNetIP(destination)
 	t.udpNat.NewContextPacket(ctx, metadata.Source.AddrPort(), buffer, adapter.UpstreamMetadata(metadata), func(natConn N.PacketConn) (context.Context, N.PacketWriter) {
-		return adapter.WithContext(log.ContextWithNewID(ctx), &metadata), &tproxyPacketWriter{source: natConn}
+		return adapter.WithContext(log.ContextWithNewID(ctx), &metadata), &tproxyPacketWriter{ctx: ctx, source: natConn}
 	})
 	return nil
 }
 
 type tproxyPacketWriter struct {
+	ctx         context.Context
 	source      N.PacketConn
 	destination M.Socksaddr
 	conn        *net.UDPConn
@@ -99,25 +100,30 @@ type tproxyPacketWriter struct {
 
 func (w *tproxyPacketWriter) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
 	defer buffer.Release()
-	var udpConn *net.UDPConn
-	if w.destination == destination {
-		if w.conn != nil {
-			udpConn = w.conn
+	destination = destination.Unwrap()
+	if !w.destination.Addr.IsValid() {
+		w.destination = destination
+	} else if w.destination == destination && w.conn != nil {
+		_, err := w.conn.WriteToUDPAddrPort(buffer.Bytes(), M.AddrPortFromNet(w.source.LocalAddr()))
+		if err == nil {
+			w.conn = nil
 		}
+		return err
 	}
-	if udpConn == nil {
-		var err error
-		udpConn, err = redir.DialUDP(destination.UDPAddr(), M.SocksaddrFromNet(w.source.LocalAddr()).UDPAddr())
-		if err != nil {
-			return E.Cause(err, "tproxy udp write back")
-		}
-		if w.destination == destination {
-			w.conn = udpConn
-		} else {
-			defer udpConn.Close()
-		}
+	var listener net.ListenConfig
+	listener.Control = control.Append(listener.Control, control.ReuseAddr())
+	listener.Control = control.Append(listener.Control, redir.TProxyWriteBack())
+	packetConn, err := listener.ListenPacket(w.ctx, "udp", destination.String())
+	if err != nil {
+		return err
+	}
+	udpConn := packetConn.(*net.UDPConn)
+	if w.destination == destination {
+		w.conn = udpConn
+	} else {
+		defer udpConn.Close()
 	}
-	return common.Error(udpConn.Write(buffer.Bytes()))
+	return common.Error(udpConn.WriteToUDPAddrPort(buffer.Bytes(), M.AddrPortFromNet(w.source.LocalAddr())))
 }
 
 func (w *tproxyPacketWriter) Close() error {