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

     1  package outbound
     2  
     3  import (
     4  	"context"
     5  	"net"
     6  	"net/netip"
     7  	"time"
     8  
     9  	"github.com/sagernet/sing-box/adapter"
    10  	"github.com/sagernet/sing-box/common/dialer"
    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/bufio"
    16  	E "github.com/sagernet/sing/common/exceptions"
    17  	M "github.com/sagernet/sing/common/metadata"
    18  	N "github.com/sagernet/sing/common/network"
    19  )
    20  
    21  var (
    22  	_ adapter.Outbound = (*Direct)(nil)
    23  	_ N.ParallelDialer = (*Direct)(nil)
    24  )
    25  
    26  type Direct struct {
    27  	myOutboundAdapter
    28  	dialer              N.Dialer
    29  	domainStrategy      dns.DomainStrategy
    30  	fallbackDelay       time.Duration
    31  	overrideOption      int
    32  	overrideDestination M.Socksaddr
    33  	loopBack            *loopBackDetector
    34  }
    35  
    36  func NewDirect(router adapter.Router, logger log.ContextLogger, tag string, options option.DirectOutboundOptions) (*Direct, error) {
    37  	options.UDPFragmentDefault = true
    38  	outboundDialer, err := dialer.New(router, options.DialerOptions)
    39  	if err != nil {
    40  		return nil, err
    41  	}
    42  	outbound := &Direct{
    43  		myOutboundAdapter: myOutboundAdapter{
    44  			protocol:     C.TypeDirect,
    45  			network:      []string{N.NetworkTCP, N.NetworkUDP},
    46  			router:       router,
    47  			logger:       logger,
    48  			tag:          tag,
    49  			dependencies: withDialerDependency(options.DialerOptions),
    50  		},
    51  		domainStrategy: dns.DomainStrategy(options.DomainStrategy),
    52  		fallbackDelay:  time.Duration(options.FallbackDelay),
    53  		dialer:         outboundDialer,
    54  		loopBack:       newLoopBackDetector(router),
    55  	}
    56  	if options.ProxyProtocol != 0 {
    57  		return nil, E.New("Proxy Protocol is deprecated and removed in sing-box 1.6.0")
    58  	}
    59  	if options.OverrideAddress != "" && options.OverridePort != 0 {
    60  		outbound.overrideOption = 1
    61  		outbound.overrideDestination = M.ParseSocksaddrHostPort(options.OverrideAddress, options.OverridePort)
    62  	} else if options.OverrideAddress != "" {
    63  		outbound.overrideOption = 2
    64  		outbound.overrideDestination = M.ParseSocksaddrHostPort(options.OverrideAddress, options.OverridePort)
    65  	} else if options.OverridePort != 0 {
    66  		outbound.overrideOption = 3
    67  		outbound.overrideDestination = M.Socksaddr{Port: options.OverridePort}
    68  	}
    69  	return outbound, nil
    70  }
    71  
    72  func (h *Direct) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
    73  	ctx, metadata := adapter.AppendContext(ctx)
    74  	metadata.Outbound = h.tag
    75  	metadata.Destination = destination
    76  	switch h.overrideOption {
    77  	case 1:
    78  		destination = h.overrideDestination
    79  	case 2:
    80  		newDestination := h.overrideDestination
    81  		newDestination.Port = destination.Port
    82  		destination = newDestination
    83  	case 3:
    84  		destination.Port = h.overrideDestination.Port
    85  	}
    86  	network = N.NetworkName(network)
    87  	switch network {
    88  	case N.NetworkTCP:
    89  		h.logger.InfoContext(ctx, "outbound connection to ", destination)
    90  	case N.NetworkUDP:
    91  		h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
    92  	}
    93  	conn, err := h.dialer.DialContext(ctx, network, destination)
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  	return h.loopBack.NewConn(conn), nil
    98  }
    99  
   100  func (h *Direct) DialParallel(ctx context.Context, network string, destination M.Socksaddr, destinationAddresses []netip.Addr) (net.Conn, error) {
   101  	ctx, metadata := adapter.AppendContext(ctx)
   102  	metadata.Outbound = h.tag
   103  	metadata.Destination = destination
   104  	switch h.overrideOption {
   105  	case 1, 2:
   106  		// override address
   107  		return h.DialContext(ctx, network, destination)
   108  	case 3:
   109  		destination.Port = h.overrideDestination.Port
   110  	}
   111  	network = N.NetworkName(network)
   112  	switch network {
   113  	case N.NetworkTCP:
   114  		h.logger.InfoContext(ctx, "outbound connection to ", destination)
   115  	case N.NetworkUDP:
   116  		h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
   117  	}
   118  	var domainStrategy dns.DomainStrategy
   119  	if h.domainStrategy != dns.DomainStrategyAsIS {
   120  		domainStrategy = h.domainStrategy
   121  	} else {
   122  		domainStrategy = dns.DomainStrategy(metadata.InboundOptions.DomainStrategy)
   123  	}
   124  	return N.DialParallel(ctx, h.dialer, network, destination, destinationAddresses, domainStrategy == dns.DomainStrategyPreferIPv6, h.fallbackDelay)
   125  }
   126  
   127  func (h *Direct) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
   128  	ctx, metadata := adapter.ExtendContext(ctx)
   129  	metadata.Outbound = h.tag
   130  	metadata.Destination = destination
   131  	originDestination := destination
   132  	switch h.overrideOption {
   133  	case 1:
   134  		destination = h.overrideDestination
   135  	case 2:
   136  		newDestination := h.overrideDestination
   137  		newDestination.Port = destination.Port
   138  		destination = newDestination
   139  	case 3:
   140  		destination.Port = h.overrideDestination.Port
   141  	}
   142  	if h.overrideOption == 0 {
   143  		h.logger.InfoContext(ctx, "outbound packet connection")
   144  	} else {
   145  		h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
   146  	}
   147  	conn, err := h.dialer.ListenPacket(ctx, destination)
   148  	if err != nil {
   149  		return nil, err
   150  	}
   151  	conn = h.loopBack.NewPacketConn(bufio.NewPacketConn(conn), destination)
   152  	if originDestination != destination {
   153  		conn = bufio.NewNATPacketConn(bufio.NewPacketConn(conn), destination, originDestination)
   154  	}
   155  	return conn, nil
   156  }
   157  
   158  func (h *Direct) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
   159  	if h.loopBack.CheckConn(metadata.Source.AddrPort(), M.AddrPortFromNet(conn.LocalAddr())) {
   160  		return E.New("reject loopback connection to ", metadata.Destination)
   161  	}
   162  	return NewConnection(ctx, h, conn, metadata)
   163  }
   164  
   165  func (h *Direct) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
   166  	if h.loopBack.CheckPacketConn(metadata.Source.AddrPort(), M.AddrPortFromNet(conn.LocalAddr())) {
   167  		return E.New("reject loopback packet connection to ", metadata.Destination)
   168  	}
   169  	return NewPacketConnection(ctx, h, conn, metadata)
   170  }