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 }