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

     1  package outbound
     2  
     3  import (
     4  	"context"
     5  	"net"
     6  
     7  	"github.com/sagernet/sing-box/adapter"
     8  	"github.com/sagernet/sing-box/common/dialer"
     9  	"github.com/sagernet/sing-box/common/mux"
    10  	C "github.com/sagernet/sing-box/constant"
    11  	"github.com/sagernet/sing-box/log"
    12  	"github.com/sagernet/sing-box/option"
    13  	"github.com/sagernet/sing-box/transport/sip003"
    14  	"github.com/sagernet/sing-shadowsocks2"
    15  	"github.com/sagernet/sing/common"
    16  	"github.com/sagernet/sing/common/bufio"
    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/uot"
    21  )
    22  
    23  var _ adapter.Outbound = (*Shadowsocks)(nil)
    24  
    25  type Shadowsocks struct {
    26  	myOutboundAdapter
    27  	dialer          N.Dialer
    28  	method          shadowsocks.Method
    29  	serverAddr      M.Socksaddr
    30  	plugin          sip003.Plugin
    31  	uotClient       *uot.Client
    32  	multiplexDialer *mux.Client
    33  }
    34  
    35  func NewShadowsocks(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksOutboundOptions) (*Shadowsocks, error) {
    36  	method, err := shadowsocks.CreateMethod(ctx, options.Method, shadowsocks.MethodOptions{
    37  		Password: options.Password,
    38  	})
    39  	if err != nil {
    40  		return nil, err
    41  	}
    42  	outboundDialer, err := dialer.New(router, options.DialerOptions)
    43  	if err != nil {
    44  		return nil, err
    45  	}
    46  	outbound := &Shadowsocks{
    47  		myOutboundAdapter: myOutboundAdapter{
    48  			protocol:     C.TypeShadowsocks,
    49  			network:      options.Network.Build(),
    50  			router:       router,
    51  			logger:       logger,
    52  			tag:          tag,
    53  			dependencies: withDialerDependency(options.DialerOptions),
    54  		},
    55  		dialer:     outboundDialer,
    56  		method:     method,
    57  		serverAddr: options.ServerOptions.Build(),
    58  	}
    59  	if options.Plugin != "" {
    60  		outbound.plugin, err = sip003.CreatePlugin(ctx, options.Plugin, options.PluginOptions, router, outbound.dialer, outbound.serverAddr)
    61  		if err != nil {
    62  			return nil, err
    63  		}
    64  	}
    65  	uotOptions := common.PtrValueOrDefault(options.UDPOverTCP)
    66  	if !uotOptions.Enabled {
    67  		outbound.multiplexDialer, err = mux.NewClientWithOptions((*shadowsocksDialer)(outbound), logger, common.PtrValueOrDefault(options.Multiplex))
    68  		if err != nil {
    69  			return nil, err
    70  		}
    71  	}
    72  	if uotOptions.Enabled {
    73  		outbound.uotClient = &uot.Client{
    74  			Dialer:  (*shadowsocksDialer)(outbound),
    75  			Version: uotOptions.Version,
    76  		}
    77  	}
    78  	return outbound, nil
    79  }
    80  
    81  func (h *Shadowsocks) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
    82  	ctx, metadata := adapter.AppendContext(ctx)
    83  	metadata.Outbound = h.tag
    84  	metadata.Destination = destination
    85  	if h.multiplexDialer == nil {
    86  		switch N.NetworkName(network) {
    87  		case N.NetworkTCP:
    88  			h.logger.InfoContext(ctx, "outbound connection to ", destination)
    89  		case N.NetworkUDP:
    90  			if h.uotClient != nil {
    91  				h.logger.InfoContext(ctx, "outbound UoT connect packet connection to ", destination)
    92  				return h.uotClient.DialContext(ctx, network, destination)
    93  			} else {
    94  				h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
    95  			}
    96  		}
    97  		return (*shadowsocksDialer)(h).DialContext(ctx, network, destination)
    98  	} else {
    99  		switch N.NetworkName(network) {
   100  		case N.NetworkTCP:
   101  			h.logger.InfoContext(ctx, "outbound multiplex connection to ", destination)
   102  		case N.NetworkUDP:
   103  			h.logger.InfoContext(ctx, "outbound multiplex packet connection to ", destination)
   104  		}
   105  		return h.multiplexDialer.DialContext(ctx, network, destination)
   106  	}
   107  }
   108  
   109  func (h *Shadowsocks) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
   110  	ctx, metadata := adapter.AppendContext(ctx)
   111  	metadata.Outbound = h.tag
   112  	metadata.Destination = destination
   113  	if h.multiplexDialer == nil {
   114  		if h.uotClient != nil {
   115  			h.logger.InfoContext(ctx, "outbound UoT packet connection to ", destination)
   116  			return h.uotClient.ListenPacket(ctx, destination)
   117  		} else {
   118  			h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
   119  		}
   120  		h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
   121  		return (*shadowsocksDialer)(h).ListenPacket(ctx, destination)
   122  	} else {
   123  		h.logger.InfoContext(ctx, "outbound multiplex packet connection to ", destination)
   124  		return h.multiplexDialer.ListenPacket(ctx, destination)
   125  	}
   126  }
   127  
   128  func (h *Shadowsocks) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
   129  	return NewConnection(ctx, h, conn, metadata)
   130  }
   131  
   132  func (h *Shadowsocks) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
   133  	return NewPacketConnection(ctx, h, conn, metadata)
   134  }
   135  
   136  func (h *Shadowsocks) InterfaceUpdated() {
   137  	if h.multiplexDialer != nil {
   138  		h.multiplexDialer.Reset()
   139  	}
   140  	return
   141  }
   142  
   143  func (h *Shadowsocks) Close() error {
   144  	return common.Close(common.PtrOrNil(h.multiplexDialer))
   145  }
   146  
   147  var _ N.Dialer = (*shadowsocksDialer)(nil)
   148  
   149  type shadowsocksDialer Shadowsocks
   150  
   151  func (h *shadowsocksDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
   152  	ctx, metadata := adapter.AppendContext(ctx)
   153  	metadata.Outbound = h.tag
   154  	metadata.Destination = destination
   155  	switch N.NetworkName(network) {
   156  	case N.NetworkTCP:
   157  		var outConn net.Conn
   158  		var err error
   159  		if h.plugin != nil {
   160  			outConn, err = h.plugin.DialContext(ctx)
   161  		} else {
   162  			outConn, err = h.dialer.DialContext(ctx, N.NetworkTCP, h.serverAddr)
   163  		}
   164  		if err != nil {
   165  			return nil, err
   166  		}
   167  		return h.method.DialEarlyConn(outConn, destination), nil
   168  	case N.NetworkUDP:
   169  		outConn, err := h.dialer.DialContext(ctx, N.NetworkUDP, h.serverAddr)
   170  		if err != nil {
   171  			return nil, err
   172  		}
   173  		return bufio.NewBindPacketConn(h.method.DialPacketConn(outConn), destination), nil
   174  	default:
   175  		return nil, E.Extend(N.ErrUnknownNetwork, network)
   176  	}
   177  }
   178  
   179  func (h *shadowsocksDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
   180  	ctx, metadata := adapter.AppendContext(ctx)
   181  	metadata.Outbound = h.tag
   182  	metadata.Destination = destination
   183  	outConn, err := h.dialer.DialContext(ctx, N.NetworkUDP, h.serverAddr)
   184  	if err != nil {
   185  		return nil, err
   186  	}
   187  	return h.method.DialPacketConn(outConn), nil
   188  }