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