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  }