github.com/metacubex/mihomo@v1.18.5/adapter/outbound/shadowsocks.go (about)

     1  package outbound
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"net"
     8  	"strconv"
     9  
    10  	N "github.com/metacubex/mihomo/common/net"
    11  	"github.com/metacubex/mihomo/common/structure"
    12  	"github.com/metacubex/mihomo/component/dialer"
    13  	"github.com/metacubex/mihomo/component/proxydialer"
    14  	"github.com/metacubex/mihomo/component/resolver"
    15  	C "github.com/metacubex/mihomo/constant"
    16  	"github.com/metacubex/mihomo/transport/restls"
    17  	obfs "github.com/metacubex/mihomo/transport/simple-obfs"
    18  	shadowtls "github.com/metacubex/mihomo/transport/sing-shadowtls"
    19  	v2rayObfs "github.com/metacubex/mihomo/transport/v2ray-plugin"
    20  
    21  	restlsC "github.com/3andne/restls-client-go"
    22  	shadowsocks "github.com/metacubex/sing-shadowsocks2"
    23  	"github.com/sagernet/sing/common/bufio"
    24  	M "github.com/sagernet/sing/common/metadata"
    25  	"github.com/sagernet/sing/common/uot"
    26  )
    27  
    28  type ShadowSocks struct {
    29  	*Base
    30  	method shadowsocks.Method
    31  
    32  	option *ShadowSocksOption
    33  	// obfs
    34  	obfsMode        string
    35  	obfsOption      *simpleObfsOption
    36  	v2rayOption     *v2rayObfs.Option
    37  	shadowTLSOption *shadowtls.ShadowTLSOption
    38  	restlsConfig    *restlsC.Config
    39  }
    40  
    41  type ShadowSocksOption struct {
    42  	BasicOption
    43  	Name              string         `proxy:"name"`
    44  	Server            string         `proxy:"server"`
    45  	Port              int            `proxy:"port"`
    46  	Password          string         `proxy:"password"`
    47  	Cipher            string         `proxy:"cipher"`
    48  	UDP               bool           `proxy:"udp,omitempty"`
    49  	Plugin            string         `proxy:"plugin,omitempty"`
    50  	PluginOpts        map[string]any `proxy:"plugin-opts,omitempty"`
    51  	UDPOverTCP        bool           `proxy:"udp-over-tcp,omitempty"`
    52  	UDPOverTCPVersion int            `proxy:"udp-over-tcp-version,omitempty"`
    53  	ClientFingerprint string         `proxy:"client-fingerprint,omitempty"`
    54  }
    55  
    56  type simpleObfsOption struct {
    57  	Mode string `obfs:"mode,omitempty"`
    58  	Host string `obfs:"host,omitempty"`
    59  }
    60  
    61  type v2rayObfsOption struct {
    62  	Mode                     string            `obfs:"mode"`
    63  	Host                     string            `obfs:"host,omitempty"`
    64  	Path                     string            `obfs:"path,omitempty"`
    65  	TLS                      bool              `obfs:"tls,omitempty"`
    66  	Fingerprint              string            `obfs:"fingerprint,omitempty"`
    67  	Headers                  map[string]string `obfs:"headers,omitempty"`
    68  	SkipCertVerify           bool              `obfs:"skip-cert-verify,omitempty"`
    69  	Mux                      bool              `obfs:"mux,omitempty"`
    70  	V2rayHttpUpgrade         bool              `obfs:"v2ray-http-upgrade,omitempty"`
    71  	V2rayHttpUpgradeFastOpen bool              `obfs:"v2ray-http-upgrade-fast-open,omitempty"`
    72  }
    73  
    74  type shadowTLSOption struct {
    75  	Password       string `obfs:"password"`
    76  	Host           string `obfs:"host"`
    77  	Fingerprint    string `obfs:"fingerprint,omitempty"`
    78  	SkipCertVerify bool   `obfs:"skip-cert-verify,omitempty"`
    79  	Version        int    `obfs:"version,omitempty"`
    80  }
    81  
    82  type restlsOption struct {
    83  	Password     string `obfs:"password"`
    84  	Host         string `obfs:"host"`
    85  	VersionHint  string `obfs:"version-hint"`
    86  	RestlsScript string `obfs:"restls-script,omitempty"`
    87  }
    88  
    89  // StreamConnContext implements C.ProxyAdapter
    90  func (ss *ShadowSocks) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.Metadata) (net.Conn, error) {
    91  	useEarly := false
    92  	switch ss.obfsMode {
    93  	case "tls":
    94  		c = obfs.NewTLSObfs(c, ss.obfsOption.Host)
    95  	case "http":
    96  		_, port, _ := net.SplitHostPort(ss.addr)
    97  		c = obfs.NewHTTPObfs(c, ss.obfsOption.Host, port)
    98  	case "websocket":
    99  		var err error
   100  		c, err = v2rayObfs.NewV2rayObfs(ctx, c, ss.v2rayOption)
   101  		if err != nil {
   102  			return nil, fmt.Errorf("%s connect error: %w", ss.addr, err)
   103  		}
   104  	case shadowtls.Mode:
   105  		var err error
   106  		c, err = shadowtls.NewShadowTLS(ctx, c, ss.shadowTLSOption)
   107  		if err != nil {
   108  			return nil, err
   109  		}
   110  		useEarly = true
   111  	case restls.Mode:
   112  		var err error
   113  		c, err = restls.NewRestls(ctx, c, ss.restlsConfig)
   114  		if err != nil {
   115  			return nil, fmt.Errorf("%s (restls) connect error: %w", ss.addr, err)
   116  		}
   117  		useEarly = true
   118  	}
   119  	useEarly = useEarly || N.NeedHandshake(c)
   120  	if metadata.NetWork == C.UDP && ss.option.UDPOverTCP {
   121  		uotDestination := uot.RequestDestination(uint8(ss.option.UDPOverTCPVersion))
   122  		if useEarly {
   123  			return ss.method.DialEarlyConn(c, uotDestination), nil
   124  		} else {
   125  			return ss.method.DialConn(c, uotDestination)
   126  		}
   127  	}
   128  	if useEarly {
   129  		return ss.method.DialEarlyConn(c, M.ParseSocksaddrHostPort(metadata.String(), metadata.DstPort)), nil
   130  	} else {
   131  		return ss.method.DialConn(c, M.ParseSocksaddrHostPort(metadata.String(), metadata.DstPort))
   132  	}
   133  }
   134  
   135  // DialContext implements C.ProxyAdapter
   136  func (ss *ShadowSocks) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) {
   137  	return ss.DialContextWithDialer(ctx, dialer.NewDialer(ss.Base.DialOptions(opts...)...), metadata)
   138  }
   139  
   140  // DialContextWithDialer implements C.ProxyAdapter
   141  func (ss *ShadowSocks) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.Conn, err error) {
   142  	if len(ss.option.DialerProxy) > 0 {
   143  		dialer, err = proxydialer.NewByName(ss.option.DialerProxy, dialer)
   144  		if err != nil {
   145  			return nil, err
   146  		}
   147  	}
   148  	c, err := dialer.DialContext(ctx, "tcp", ss.addr)
   149  	if err != nil {
   150  		return nil, fmt.Errorf("%s connect error: %w", ss.addr, err)
   151  	}
   152  	N.TCPKeepAlive(c)
   153  
   154  	defer func(c net.Conn) {
   155  		safeConnClose(c, err)
   156  	}(c)
   157  
   158  	c, err = ss.StreamConnContext(ctx, c, metadata)
   159  	return NewConn(c, ss), err
   160  }
   161  
   162  // ListenPacketContext implements C.ProxyAdapter
   163  func (ss *ShadowSocks) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) {
   164  	return ss.ListenPacketWithDialer(ctx, dialer.NewDialer(ss.Base.DialOptions(opts...)...), metadata)
   165  }
   166  
   167  // ListenPacketWithDialer implements C.ProxyAdapter
   168  func (ss *ShadowSocks) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.PacketConn, err error) {
   169  	if ss.option.UDPOverTCP {
   170  		tcpConn, err := ss.DialContextWithDialer(ctx, dialer, metadata)
   171  		if err != nil {
   172  			return nil, err
   173  		}
   174  		return ss.ListenPacketOnStreamConn(ctx, tcpConn, metadata)
   175  	}
   176  	if len(ss.option.DialerProxy) > 0 {
   177  		dialer, err = proxydialer.NewByName(ss.option.DialerProxy, dialer)
   178  		if err != nil {
   179  			return nil, err
   180  		}
   181  	}
   182  	addr, err := resolveUDPAddrWithPrefer(ctx, "udp", ss.addr, ss.prefer)
   183  	if err != nil {
   184  		return nil, err
   185  	}
   186  
   187  	pc, err := dialer.ListenPacket(ctx, "udp", "", addr.AddrPort())
   188  	if err != nil {
   189  		return nil, err
   190  	}
   191  	pc = ss.method.DialPacketConn(bufio.NewBindPacketConn(pc, addr))
   192  	return newPacketConn(pc, ss), nil
   193  }
   194  
   195  // SupportWithDialer implements C.ProxyAdapter
   196  func (ss *ShadowSocks) SupportWithDialer() C.NetWork {
   197  	return C.ALLNet
   198  }
   199  
   200  // ListenPacketOnStreamConn implements C.ProxyAdapter
   201  func (ss *ShadowSocks) ListenPacketOnStreamConn(ctx context.Context, c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) {
   202  	if ss.option.UDPOverTCP {
   203  		// ss uot use stream-oriented udp with a special address, so we need a net.UDPAddr
   204  		if !metadata.Resolved() {
   205  			ip, err := resolver.ResolveIP(ctx, metadata.Host)
   206  			if err != nil {
   207  				return nil, errors.New("can't resolve ip")
   208  			}
   209  			metadata.DstIP = ip
   210  		}
   211  
   212  		destination := M.SocksaddrFromNet(metadata.UDPAddr())
   213  		if ss.option.UDPOverTCPVersion == uot.LegacyVersion {
   214  			return newPacketConn(N.NewThreadSafePacketConn(uot.NewConn(c, uot.Request{Destination: destination})), ss), nil
   215  		} else {
   216  			return newPacketConn(N.NewThreadSafePacketConn(uot.NewLazyConn(c, uot.Request{Destination: destination})), ss), nil
   217  		}
   218  	}
   219  	return nil, C.ErrNotSupport
   220  }
   221  
   222  // SupportUOT implements C.ProxyAdapter
   223  func (ss *ShadowSocks) SupportUOT() bool {
   224  	return ss.option.UDPOverTCP
   225  }
   226  
   227  func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) {
   228  	addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port))
   229  	method, err := shadowsocks.CreateMethod(context.Background(), option.Cipher, shadowsocks.MethodOptions{
   230  		Password: option.Password,
   231  	})
   232  	if err != nil {
   233  		return nil, fmt.Errorf("ss %s initialize error: %w", addr, err)
   234  	}
   235  
   236  	var v2rayOption *v2rayObfs.Option
   237  	var obfsOption *simpleObfsOption
   238  	var shadowTLSOpt *shadowtls.ShadowTLSOption
   239  	var restlsConfig *restlsC.Config
   240  	obfsMode := ""
   241  
   242  	decoder := structure.NewDecoder(structure.Option{TagName: "obfs", WeaklyTypedInput: true})
   243  	if option.Plugin == "obfs" {
   244  		opts := simpleObfsOption{Host: "bing.com"}
   245  		if err := decoder.Decode(option.PluginOpts, &opts); err != nil {
   246  			return nil, fmt.Errorf("ss %s initialize obfs error: %w", addr, err)
   247  		}
   248  
   249  		if opts.Mode != "tls" && opts.Mode != "http" {
   250  			return nil, fmt.Errorf("ss %s obfs mode error: %s", addr, opts.Mode)
   251  		}
   252  		obfsMode = opts.Mode
   253  		obfsOption = &opts
   254  	} else if option.Plugin == "v2ray-plugin" {
   255  		opts := v2rayObfsOption{Host: "bing.com", Mux: true}
   256  		if err := decoder.Decode(option.PluginOpts, &opts); err != nil {
   257  			return nil, fmt.Errorf("ss %s initialize v2ray-plugin error: %w", addr, err)
   258  		}
   259  
   260  		if opts.Mode != "websocket" {
   261  			return nil, fmt.Errorf("ss %s obfs mode error: %s", addr, opts.Mode)
   262  		}
   263  		obfsMode = opts.Mode
   264  		v2rayOption = &v2rayObfs.Option{
   265  			Host:                     opts.Host,
   266  			Path:                     opts.Path,
   267  			Headers:                  opts.Headers,
   268  			Mux:                      opts.Mux,
   269  			V2rayHttpUpgrade:         opts.V2rayHttpUpgrade,
   270  			V2rayHttpUpgradeFastOpen: opts.V2rayHttpUpgradeFastOpen,
   271  		}
   272  
   273  		if opts.TLS {
   274  			v2rayOption.TLS = true
   275  			v2rayOption.SkipCertVerify = opts.SkipCertVerify
   276  			v2rayOption.Fingerprint = opts.Fingerprint
   277  		}
   278  	} else if option.Plugin == shadowtls.Mode {
   279  		obfsMode = shadowtls.Mode
   280  		opt := &shadowTLSOption{
   281  			Version: 2,
   282  		}
   283  		if err := decoder.Decode(option.PluginOpts, opt); err != nil {
   284  			return nil, fmt.Errorf("ss %s initialize shadow-tls-plugin error: %w", addr, err)
   285  		}
   286  
   287  		shadowTLSOpt = &shadowtls.ShadowTLSOption{
   288  			Password:          opt.Password,
   289  			Host:              opt.Host,
   290  			Fingerprint:       opt.Fingerprint,
   291  			ClientFingerprint: option.ClientFingerprint,
   292  			SkipCertVerify:    opt.SkipCertVerify,
   293  			Version:           opt.Version,
   294  		}
   295  	} else if option.Plugin == restls.Mode {
   296  		obfsMode = restls.Mode
   297  		restlsOpt := &restlsOption{}
   298  		if err := decoder.Decode(option.PluginOpts, restlsOpt); err != nil {
   299  			return nil, fmt.Errorf("ss %s initialize restls-plugin error: %w", addr, err)
   300  		}
   301  
   302  		restlsConfig, err = restlsC.NewRestlsConfig(restlsOpt.Host, restlsOpt.Password, restlsOpt.VersionHint, restlsOpt.RestlsScript, option.ClientFingerprint)
   303  		if err != nil {
   304  			return nil, fmt.Errorf("ss %s initialize restls-plugin error: %w", addr, err)
   305  		}
   306  
   307  	}
   308  	switch option.UDPOverTCPVersion {
   309  	case uot.Version, uot.LegacyVersion:
   310  	case 0:
   311  		option.UDPOverTCPVersion = uot.LegacyVersion
   312  	default:
   313  		return nil, fmt.Errorf("ss %s unknown udp over tcp protocol version: %d", addr, option.UDPOverTCPVersion)
   314  	}
   315  
   316  	return &ShadowSocks{
   317  		Base: &Base{
   318  			name:   option.Name,
   319  			addr:   addr,
   320  			tp:     C.Shadowsocks,
   321  			udp:    option.UDP,
   322  			tfo:    option.TFO,
   323  			mpTcp:  option.MPTCP,
   324  			iface:  option.Interface,
   325  			rmark:  option.RoutingMark,
   326  			prefer: C.NewDNSPrefer(option.IPVersion),
   327  		},
   328  		method: method,
   329  
   330  		option:          &option,
   331  		obfsMode:        obfsMode,
   332  		v2rayOption:     v2rayOption,
   333  		obfsOption:      obfsOption,
   334  		shadowTLSOption: shadowTLSOpt,
   335  		restlsConfig:    restlsConfig,
   336  	}, nil
   337  }