123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263 |
- package direct
- import (
- "context"
- "net"
- "net/netip"
- "time"
- "github.com/sagernet/sing-box/adapter"
- "github.com/sagernet/sing-box/adapter/outbound"
- "github.com/sagernet/sing-box/common/dialer"
- C "github.com/sagernet/sing-box/constant"
- "github.com/sagernet/sing-box/log"
- "github.com/sagernet/sing-box/option"
- "github.com/sagernet/sing-dns"
- "github.com/sagernet/sing/common"
- "github.com/sagernet/sing/common/bufio"
- E "github.com/sagernet/sing/common/exceptions"
- "github.com/sagernet/sing/common/logger"
- M "github.com/sagernet/sing/common/metadata"
- N "github.com/sagernet/sing/common/network"
- )
- func RegisterOutbound(registry *outbound.Registry) {
- outbound.Register[option.DirectOutboundOptions](registry, C.TypeDirect, NewOutbound)
- }
- var (
- _ N.ParallelDialer = (*Outbound)(nil)
- _ dialer.ParallelNetworkDialer = (*Outbound)(nil)
- )
- type Outbound struct {
- outbound.Adapter
- logger logger.ContextLogger
- dialer dialer.ParallelInterfaceDialer
- domainStrategy dns.DomainStrategy
- fallbackDelay time.Duration
- networkStrategy C.NetworkStrategy
- networkType []C.InterfaceType
- fallbackNetworkType []C.InterfaceType
- networkFallbackDelay time.Duration
- overrideOption int
- overrideDestination M.Socksaddr
- // loopBack *loopBackDetector
- }
- func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.DirectOutboundOptions) (adapter.Outbound, error) {
- options.UDPFragmentDefault = true
- outboundDialer, err := dialer.NewDirect(ctx, options.DialerOptions)
- if err != nil {
- return nil, err
- }
- outbound := &Outbound{
- Adapter: outbound.NewAdapterWithDialerOptions(C.TypeDirect, tag, []string{N.NetworkTCP, N.NetworkUDP}, options.DialerOptions),
- logger: logger,
- domainStrategy: dns.DomainStrategy(options.DomainStrategy),
- fallbackDelay: time.Duration(options.FallbackDelay),
- networkStrategy: C.NetworkStrategy(options.NetworkStrategy),
- networkType: common.Map(options.NetworkType, option.InterfaceType.Build),
- fallbackNetworkType: common.Map(options.FallbackNetworkType, option.InterfaceType.Build),
- networkFallbackDelay: time.Duration(options.NetworkFallbackDelay),
- dialer: outboundDialer,
- // loopBack: newLoopBackDetector(router),
- }
- //nolint:staticcheck
- if options.ProxyProtocol != 0 {
- return nil, E.New("Proxy Protocol is deprecated and removed in sing-box 1.6.0")
- }
- //nolint:staticcheck
- if options.OverrideAddress != "" && options.OverridePort != 0 {
- outbound.overrideOption = 1
- outbound.overrideDestination = M.ParseSocksaddrHostPort(options.OverrideAddress, options.OverridePort)
- } else if options.OverrideAddress != "" {
- outbound.overrideOption = 2
- outbound.overrideDestination = M.ParseSocksaddrHostPort(options.OverrideAddress, options.OverridePort)
- } else if options.OverridePort != 0 {
- outbound.overrideOption = 3
- outbound.overrideDestination = M.Socksaddr{Port: options.OverridePort}
- }
- return outbound, nil
- }
- func (h *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
- ctx, metadata := adapter.ExtendContext(ctx)
- metadata.Outbound = h.Tag()
- metadata.Destination = destination
- switch h.overrideOption {
- case 1:
- destination = h.overrideDestination
- case 2:
- newDestination := h.overrideDestination
- newDestination.Port = destination.Port
- destination = newDestination
- case 3:
- destination.Port = h.overrideDestination.Port
- }
- network = N.NetworkName(network)
- switch network {
- case N.NetworkTCP:
- h.logger.InfoContext(ctx, "outbound connection to ", destination)
- case N.NetworkUDP:
- h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
- }
- /*conn, err := h.dialer.DialContext(ctx, network, destination)
- if err != nil {
- return nil, err
- }
- return h.loopBack.NewConn(conn), nil*/
- return h.dialer.DialContext(ctx, network, destination)
- }
- func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
- ctx, metadata := adapter.ExtendContext(ctx)
- metadata.Outbound = h.Tag()
- metadata.Destination = destination
- originDestination := destination
- switch h.overrideOption {
- case 1:
- destination = h.overrideDestination
- case 2:
- newDestination := h.overrideDestination
- newDestination.Port = destination.Port
- destination = newDestination
- case 3:
- destination.Port = h.overrideDestination.Port
- }
- if h.overrideOption == 0 {
- h.logger.InfoContext(ctx, "outbound packet connection")
- } else {
- h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
- }
- conn, err := h.dialer.ListenPacket(ctx, destination)
- if err != nil {
- return nil, err
- }
- // conn = h.loopBack.NewPacketConn(bufio.NewPacketConn(conn), destination)
- if originDestination != destination {
- conn = bufio.NewNATPacketConn(bufio.NewPacketConn(conn), destination, originDestination)
- }
- return conn, nil
- }
- func (h *Outbound) DialParallel(ctx context.Context, network string, destination M.Socksaddr, destinationAddresses []netip.Addr) (net.Conn, error) {
- ctx, metadata := adapter.ExtendContext(ctx)
- metadata.Outbound = h.Tag()
- metadata.Destination = destination
- switch h.overrideOption {
- case 1, 2:
- // override address
- return h.DialContext(ctx, network, destination)
- case 3:
- destination.Port = h.overrideDestination.Port
- }
- network = N.NetworkName(network)
- switch network {
- case N.NetworkTCP:
- h.logger.InfoContext(ctx, "outbound connection to ", destination)
- case N.NetworkUDP:
- h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
- }
- var domainStrategy dns.DomainStrategy
- if h.domainStrategy != dns.DomainStrategyAsIS {
- domainStrategy = h.domainStrategy
- } else {
- //nolint:staticcheck
- domainStrategy = dns.DomainStrategy(metadata.InboundOptions.DomainStrategy)
- }
- switch domainStrategy {
- case dns.DomainStrategyUseIPv4:
- destinationAddresses = common.Filter(destinationAddresses, netip.Addr.Is4)
- if len(destinationAddresses) == 0 {
- return nil, E.New("no IPv4 address available for ", destination)
- }
- case dns.DomainStrategyUseIPv6:
- destinationAddresses = common.Filter(destinationAddresses, netip.Addr.Is6)
- if len(destinationAddresses) == 0 {
- return nil, E.New("no IPv6 address available for ", destination)
- }
- }
- return dialer.DialParallelNetwork(ctx, h.dialer, network, destination, destinationAddresses, domainStrategy == dns.DomainStrategyPreferIPv6, h.networkStrategy, h.networkType, h.fallbackNetworkType, h.fallbackDelay)
- }
- func (h *Outbound) DialParallelNetwork(ctx context.Context, network string, destination M.Socksaddr, destinationAddresses []netip.Addr, networkStrategy C.NetworkStrategy, networkType []C.InterfaceType, fallbackNetworkType []C.InterfaceType, fallbackDelay time.Duration) (net.Conn, error) {
- ctx, metadata := adapter.ExtendContext(ctx)
- metadata.Outbound = h.Tag()
- metadata.Destination = destination
- switch h.overrideOption {
- case 1, 2:
- // override address
- return h.DialContext(ctx, network, destination)
- case 3:
- destination.Port = h.overrideDestination.Port
- }
- network = N.NetworkName(network)
- switch network {
- case N.NetworkTCP:
- h.logger.InfoContext(ctx, "outbound connection to ", destination)
- case N.NetworkUDP:
- h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
- }
- var domainStrategy dns.DomainStrategy
- if h.domainStrategy != dns.DomainStrategyAsIS {
- domainStrategy = h.domainStrategy
- } else {
- //nolint:staticcheck
- domainStrategy = dns.DomainStrategy(metadata.InboundOptions.DomainStrategy)
- }
- switch domainStrategy {
- case dns.DomainStrategyUseIPv4:
- destinationAddresses = common.Filter(destinationAddresses, netip.Addr.Is4)
- if len(destinationAddresses) == 0 {
- return nil, E.New("no IPv4 address available for ", destination)
- }
- case dns.DomainStrategyUseIPv6:
- destinationAddresses = common.Filter(destinationAddresses, netip.Addr.Is6)
- if len(destinationAddresses) == 0 {
- return nil, E.New("no IPv6 address available for ", destination)
- }
- }
- return dialer.DialParallelNetwork(ctx, h.dialer, network, destination, destinationAddresses, domainStrategy == dns.DomainStrategyPreferIPv6, networkStrategy, networkType, fallbackNetworkType, fallbackDelay)
- }
- func (h *Outbound) ListenSerialNetworkPacket(ctx context.Context, destination M.Socksaddr, destinationAddresses []netip.Addr, networkStrategy C.NetworkStrategy, networkType []C.InterfaceType, fallbackNetworkType []C.InterfaceType, fallbackDelay time.Duration) (net.PacketConn, netip.Addr, error) {
- ctx, metadata := adapter.ExtendContext(ctx)
- metadata.Outbound = h.Tag()
- metadata.Destination = destination
- switch h.overrideOption {
- case 1:
- destination = h.overrideDestination
- case 2:
- newDestination := h.overrideDestination
- newDestination.Port = destination.Port
- destination = newDestination
- case 3:
- destination.Port = h.overrideDestination.Port
- }
- if h.overrideOption == 0 {
- h.logger.InfoContext(ctx, "outbound packet connection")
- } else {
- h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
- }
- conn, newDestination, err := dialer.ListenSerialNetworkPacket(ctx, h.dialer, destination, destinationAddresses, networkStrategy, networkType, fallbackNetworkType, fallbackDelay)
- if err != nil {
- return nil, netip.Addr{}, err
- }
- return conn, newDestination, nil
- }
- /*func (h *Outbound) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
- if h.loopBack.CheckConn(metadata.Source.AddrPort(), M.AddrPortFromNet(conn.LocalAddr())) {
- return E.New("reject loopback connection to ", metadata.Destination)
- }
- return NewConnection(ctx, h, conn, metadata)
- }
- func (h *Outbound) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
- if h.loopBack.CheckPacketConn(metadata.Source.AddrPort(), M.AddrPortFromNet(conn.LocalAddr())) {
- return E.New("reject loopback packet connection to ", metadata.Destination)
- }
- return NewPacketConnection(ctx, h, conn, metadata)
- }
- */
|