github.com/sagernet/sing-box@v1.9.0-rc.20/outbound/default.go (about) 1 package outbound 2 3 import ( 4 "context" 5 "net" 6 "net/netip" 7 "os" 8 "time" 9 10 "github.com/sagernet/sing-box/adapter" 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-dns" 15 "github.com/sagernet/sing/common" 16 "github.com/sagernet/sing/common/buf" 17 "github.com/sagernet/sing/common/bufio" 18 "github.com/sagernet/sing/common/canceler" 19 E "github.com/sagernet/sing/common/exceptions" 20 M "github.com/sagernet/sing/common/metadata" 21 N "github.com/sagernet/sing/common/network" 22 ) 23 24 type myOutboundAdapter struct { 25 protocol string 26 network []string 27 router adapter.Router 28 logger log.ContextLogger 29 tag string 30 dependencies []string 31 } 32 33 func (a *myOutboundAdapter) Type() string { 34 return a.protocol 35 } 36 37 func (a *myOutboundAdapter) Tag() string { 38 return a.tag 39 } 40 41 func (a *myOutboundAdapter) Network() []string { 42 return a.network 43 } 44 45 func (a *myOutboundAdapter) Dependencies() []string { 46 return a.dependencies 47 } 48 49 func (a *myOutboundAdapter) NewError(ctx context.Context, err error) { 50 NewError(a.logger, ctx, err) 51 } 52 53 func withDialerDependency(options option.DialerOptions) []string { 54 if options.Detour != "" { 55 return []string{options.Detour} 56 } 57 return nil 58 } 59 60 func NewConnection(ctx context.Context, this N.Dialer, conn net.Conn, metadata adapter.InboundContext) error { 61 ctx = adapter.WithContext(ctx, &metadata) 62 var outConn net.Conn 63 var err error 64 if len(metadata.DestinationAddresses) > 0 { 65 outConn, err = N.DialSerial(ctx, this, N.NetworkTCP, metadata.Destination, metadata.DestinationAddresses) 66 } else { 67 outConn, err = this.DialContext(ctx, N.NetworkTCP, metadata.Destination) 68 } 69 if err != nil { 70 return N.ReportHandshakeFailure(conn, err) 71 } 72 err = N.ReportHandshakeSuccess(conn) 73 if err != nil { 74 outConn.Close() 75 return err 76 } 77 return CopyEarlyConn(ctx, conn, outConn) 78 } 79 80 func NewDirectConnection(ctx context.Context, router adapter.Router, this N.Dialer, conn net.Conn, metadata adapter.InboundContext, domainStrategy dns.DomainStrategy) error { 81 ctx = adapter.WithContext(ctx, &metadata) 82 var outConn net.Conn 83 var err error 84 if len(metadata.DestinationAddresses) > 0 { 85 outConn, err = N.DialSerial(ctx, this, N.NetworkTCP, metadata.Destination, metadata.DestinationAddresses) 86 } else if metadata.Destination.IsFqdn() { 87 var destinationAddresses []netip.Addr 88 destinationAddresses, err = router.Lookup(ctx, metadata.Destination.Fqdn, domainStrategy) 89 if err != nil { 90 return N.ReportHandshakeFailure(conn, err) 91 } 92 outConn, err = N.DialSerial(ctx, this, N.NetworkTCP, metadata.Destination, destinationAddresses) 93 } else { 94 outConn, err = this.DialContext(ctx, N.NetworkTCP, metadata.Destination) 95 } 96 if err != nil { 97 return N.ReportHandshakeFailure(conn, err) 98 } 99 err = N.ReportHandshakeSuccess(conn) 100 if err != nil { 101 outConn.Close() 102 return err 103 } 104 return CopyEarlyConn(ctx, conn, outConn) 105 } 106 107 func NewPacketConnection(ctx context.Context, this N.Dialer, conn N.PacketConn, metadata adapter.InboundContext) error { 108 ctx = adapter.WithContext(ctx, &metadata) 109 var outConn net.PacketConn 110 var destinationAddress netip.Addr 111 var err error 112 if len(metadata.DestinationAddresses) > 0 { 113 outConn, destinationAddress, err = N.ListenSerial(ctx, this, metadata.Destination, metadata.DestinationAddresses) 114 } else { 115 outConn, err = this.ListenPacket(ctx, metadata.Destination) 116 } 117 if err != nil { 118 return N.ReportHandshakeFailure(conn, err) 119 } 120 err = N.ReportHandshakeSuccess(conn) 121 if err != nil { 122 outConn.Close() 123 return err 124 } 125 if destinationAddress.IsValid() { 126 if metadata.Destination.IsFqdn() { 127 if metadata.InboundOptions.UDPDisableDomainUnmapping { 128 outConn = bufio.NewUnidirectionalNATPacketConn(bufio.NewPacketConn(outConn), M.SocksaddrFrom(destinationAddress, metadata.Destination.Port), metadata.Destination) 129 } else { 130 outConn = bufio.NewNATPacketConn(bufio.NewPacketConn(outConn), M.SocksaddrFrom(destinationAddress, metadata.Destination.Port), metadata.Destination) 131 } 132 } 133 if natConn, loaded := common.Cast[bufio.NATPacketConn](conn); loaded { 134 natConn.UpdateDestination(destinationAddress) 135 } 136 } 137 switch metadata.Protocol { 138 case C.ProtocolSTUN: 139 ctx, conn = canceler.NewPacketConn(ctx, conn, C.STUNTimeout) 140 case C.ProtocolQUIC: 141 ctx, conn = canceler.NewPacketConn(ctx, conn, C.QUICTimeout) 142 case C.ProtocolDNS: 143 ctx, conn = canceler.NewPacketConn(ctx, conn, C.DNSTimeout) 144 } 145 return bufio.CopyPacketConn(ctx, conn, bufio.NewPacketConn(outConn)) 146 } 147 148 func NewDirectPacketConnection(ctx context.Context, router adapter.Router, this N.Dialer, conn N.PacketConn, metadata adapter.InboundContext, domainStrategy dns.DomainStrategy) error { 149 ctx = adapter.WithContext(ctx, &metadata) 150 var outConn net.PacketConn 151 var destinationAddress netip.Addr 152 var err error 153 if len(metadata.DestinationAddresses) > 0 { 154 outConn, destinationAddress, err = N.ListenSerial(ctx, this, metadata.Destination, metadata.DestinationAddresses) 155 } else if metadata.Destination.IsFqdn() { 156 var destinationAddresses []netip.Addr 157 destinationAddresses, err = router.Lookup(ctx, metadata.Destination.Fqdn, domainStrategy) 158 if err != nil { 159 return N.ReportHandshakeFailure(conn, err) 160 } 161 outConn, destinationAddress, err = N.ListenSerial(ctx, this, metadata.Destination, destinationAddresses) 162 } else { 163 outConn, err = this.ListenPacket(ctx, metadata.Destination) 164 } 165 if err != nil { 166 return N.ReportHandshakeFailure(conn, err) 167 } 168 err = N.ReportHandshakeSuccess(conn) 169 if err != nil { 170 outConn.Close() 171 return err 172 } 173 if destinationAddress.IsValid() { 174 if metadata.Destination.IsFqdn() { 175 outConn = bufio.NewNATPacketConn(bufio.NewPacketConn(outConn), M.SocksaddrFrom(destinationAddress, metadata.Destination.Port), metadata.Destination) 176 } 177 if natConn, loaded := common.Cast[bufio.NATPacketConn](conn); loaded { 178 natConn.UpdateDestination(destinationAddress) 179 } 180 } 181 switch metadata.Protocol { 182 case C.ProtocolSTUN: 183 ctx, conn = canceler.NewPacketConn(ctx, conn, C.STUNTimeout) 184 case C.ProtocolQUIC: 185 ctx, conn = canceler.NewPacketConn(ctx, conn, C.QUICTimeout) 186 case C.ProtocolDNS: 187 ctx, conn = canceler.NewPacketConn(ctx, conn, C.DNSTimeout) 188 } 189 return bufio.CopyPacketConn(ctx, conn, bufio.NewPacketConn(outConn)) 190 } 191 192 func CopyEarlyConn(ctx context.Context, conn net.Conn, serverConn net.Conn) error { 193 if cachedReader, isCached := conn.(N.CachedReader); isCached { 194 payload := cachedReader.ReadCached() 195 if payload != nil && !payload.IsEmpty() { 196 _, err := serverConn.Write(payload.Bytes()) 197 payload.Release() 198 if err != nil { 199 serverConn.Close() 200 return err 201 } 202 return bufio.CopyConn(ctx, conn, serverConn) 203 } 204 } 205 if earlyConn, isEarlyConn := common.Cast[N.EarlyConn](serverConn); isEarlyConn && earlyConn.NeedHandshake() { 206 payload := buf.NewPacket() 207 err := conn.SetReadDeadline(time.Now().Add(C.ReadPayloadTimeout)) 208 if err != os.ErrInvalid { 209 if err != nil { 210 payload.Release() 211 serverConn.Close() 212 return err 213 } 214 _, err = payload.ReadOnceFrom(conn) 215 if err != nil && !E.IsTimeout(err) { 216 payload.Release() 217 serverConn.Close() 218 return E.Cause(err, "read payload") 219 } 220 err = conn.SetReadDeadline(time.Time{}) 221 if err != nil { 222 payload.Release() 223 serverConn.Close() 224 return err 225 } 226 } 227 _, err = serverConn.Write(payload.Bytes()) 228 payload.Release() 229 if err != nil { 230 serverConn.Close() 231 return N.ReportHandshakeFailure(conn, err) 232 } 233 } 234 return bufio.CopyConn(ctx, conn, serverConn) 235 } 236 237 func NewError(logger log.ContextLogger, ctx context.Context, err error) { 238 common.Close(err) 239 if E.IsClosedOrCanceled(err) { 240 logger.DebugContext(ctx, "connection closed: ", err) 241 return 242 } 243 logger.ErrorContext(ctx, err) 244 }