github.com/sagernet/sing-box@v1.2.7/inbound/tproxy.go (about)

     1  package inbound
     2  
     3  import (
     4  	"context"
     5  	"net"
     6  	"net/netip"
     7  	"syscall"
     8  
     9  	"github.com/sagernet/sing-box/adapter"
    10  	"github.com/sagernet/sing-box/common/redir"
    11  	C "github.com/sagernet/sing-box/constant"
    12  	"github.com/sagernet/sing-box/log"
    13  	"github.com/sagernet/sing-box/option"
    14  	"github.com/sagernet/sing/common"
    15  	"github.com/sagernet/sing/common/buf"
    16  	"github.com/sagernet/sing/common/control"
    17  	E "github.com/sagernet/sing/common/exceptions"
    18  	M "github.com/sagernet/sing/common/metadata"
    19  	N "github.com/sagernet/sing/common/network"
    20  	"github.com/sagernet/sing/common/udpnat"
    21  )
    22  
    23  type TProxy struct {
    24  	myInboundAdapter
    25  	udpNat *udpnat.Service[netip.AddrPort]
    26  }
    27  
    28  func NewTProxy(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TProxyInboundOptions) *TProxy {
    29  	tproxy := &TProxy{
    30  		myInboundAdapter: myInboundAdapter{
    31  			protocol:      C.TypeTProxy,
    32  			network:       options.Network.Build(),
    33  			ctx:           ctx,
    34  			router:        router,
    35  			logger:        logger,
    36  			tag:           tag,
    37  			listenOptions: options.ListenOptions,
    38  		},
    39  	}
    40  	var udpTimeout int64
    41  	if options.UDPTimeout != 0 {
    42  		udpTimeout = options.UDPTimeout
    43  	} else {
    44  		udpTimeout = int64(C.UDPTimeout.Seconds())
    45  	}
    46  	tproxy.connHandler = tproxy
    47  	tproxy.oobPacketHandler = tproxy
    48  	tproxy.udpNat = udpnat.New[netip.AddrPort](udpTimeout, tproxy.upstreamContextHandler())
    49  	tproxy.packetUpstream = tproxy.udpNat
    50  	return tproxy
    51  }
    52  
    53  func (t *TProxy) Start() error {
    54  	err := t.myInboundAdapter.Start()
    55  	if err != nil {
    56  		return err
    57  	}
    58  	if t.tcpListener != nil {
    59  		err = control.Conn(common.MustCast[syscall.Conn](t.tcpListener), func(fd uintptr) error {
    60  			return redir.TProxy(fd, M.SocksaddrFromNet(t.tcpListener.Addr()).Addr.Is6())
    61  		})
    62  		if err != nil {
    63  			return E.Cause(err, "configure tproxy TCP listener")
    64  		}
    65  	}
    66  	if t.udpConn != nil {
    67  		err = control.Conn(t.udpConn, func(fd uintptr) error {
    68  			return redir.TProxy(fd, M.SocksaddrFromNet(t.udpConn.LocalAddr()).Addr.Is6())
    69  		})
    70  		if err != nil {
    71  			return E.Cause(err, "configure tproxy UDP listener")
    72  		}
    73  	}
    74  	return nil
    75  }
    76  
    77  func (t *TProxy) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
    78  	metadata.Destination = M.SocksaddrFromNet(conn.LocalAddr()).Unwrap()
    79  	return t.newConnection(ctx, conn, metadata)
    80  }
    81  
    82  func (t *TProxy) NewPacket(ctx context.Context, conn N.PacketConn, buffer *buf.Buffer, oob []byte, metadata adapter.InboundContext) error {
    83  	destination, err := redir.GetOriginalDestinationFromOOB(oob)
    84  	if err != nil {
    85  		return E.Cause(err, "get tproxy destination")
    86  	}
    87  	metadata.Destination = M.SocksaddrFromNetIP(destination).Unwrap()
    88  	t.udpNat.NewContextPacket(ctx, metadata.Source.AddrPort(), buffer, adapter.UpstreamMetadata(metadata), func(natConn N.PacketConn) (context.Context, N.PacketWriter) {
    89  		return adapter.WithContext(log.ContextWithNewID(ctx), &metadata), &tproxyPacketWriter{ctx: ctx, source: natConn, destination: metadata.Destination}
    90  	})
    91  	return nil
    92  }
    93  
    94  type tproxyPacketWriter struct {
    95  	ctx         context.Context
    96  	source      N.PacketConn
    97  	destination M.Socksaddr
    98  	conn        *net.UDPConn
    99  }
   100  
   101  func (w *tproxyPacketWriter) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
   102  	defer buffer.Release()
   103  	conn := w.conn
   104  	if w.destination == destination && conn != nil {
   105  		_, err := conn.WriteToUDPAddrPort(buffer.Bytes(), M.AddrPortFromNet(w.source.LocalAddr()))
   106  		if err != nil {
   107  			w.conn = nil
   108  		}
   109  		return err
   110  	}
   111  	var listener net.ListenConfig
   112  	listener.Control = control.Append(listener.Control, control.ReuseAddr())
   113  	listener.Control = control.Append(listener.Control, redir.TProxyWriteBack())
   114  	packetConn, err := listener.ListenPacket(w.ctx, "udp", destination.String())
   115  	if err != nil {
   116  		return err
   117  	}
   118  	udpConn := packetConn.(*net.UDPConn)
   119  	if w.destination == destination {
   120  		w.conn = udpConn
   121  	} else {
   122  		defer udpConn.Close()
   123  	}
   124  	return common.Error(udpConn.WriteToUDPAddrPort(buffer.Bytes(), M.AddrPortFromNet(w.source.LocalAddr())))
   125  }
   126  
   127  func (w *tproxyPacketWriter) Close() error {
   128  	return common.Close(common.PtrOrNil(w.conn))
   129  }