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

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