github.com/yaling888/clash@v1.53.0/adapter/outbound/shadowsocks.go (about)

     1  package outbound
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"net"
     8  	"strconv"
     9  
    10  	"github.com/yaling888/clash/common/structure"
    11  	"github.com/yaling888/clash/component/dialer"
    12  	C "github.com/yaling888/clash/constant"
    13  	"github.com/yaling888/clash/transport/shadowsocks/core"
    14  	obfs "github.com/yaling888/clash/transport/simple-obfs"
    15  	"github.com/yaling888/clash/transport/socks5"
    16  	v2rayObfs "github.com/yaling888/clash/transport/v2ray-plugin"
    17  )
    18  
    19  var _ C.ProxyAdapter = (*ShadowSocks)(nil)
    20  
    21  type ShadowSocks struct {
    22  	*Base
    23  	cipher core.Cipher
    24  
    25  	// obfs
    26  	obfsMode    string
    27  	obfsOption  *simpleObfsOption
    28  	v2rayOption *v2rayObfs.Option
    29  }
    30  
    31  type ShadowSocksOption struct {
    32  	BasicOption
    33  	Name             string         `proxy:"name"`
    34  	Server           string         `proxy:"server"`
    35  	Port             int            `proxy:"port"`
    36  	Password         string         `proxy:"password"`
    37  	Cipher           string         `proxy:"cipher"`
    38  	UDP              bool           `proxy:"udp,omitempty"`
    39  	Plugin           string         `proxy:"plugin,omitempty"`
    40  	PluginOpts       map[string]any `proxy:"plugin-opts,omitempty"`
    41  	RandomHost       bool           `proxy:"rand-host,omitempty"`
    42  	RemoteDnsResolve bool           `proxy:"remote-dns-resolve,omitempty"`
    43  }
    44  
    45  type simpleObfsOption struct {
    46  	Mode       string `obfs:"mode,omitempty"`
    47  	Host       string `obfs:"host,omitempty"`
    48  	RandomHost bool   `obfs:"rand-host,omitempty"`
    49  }
    50  
    51  type v2rayObfsOption struct {
    52  	Mode           string            `obfs:"mode"`
    53  	Host           string            `obfs:"host,omitempty"`
    54  	Path           string            `obfs:"path,omitempty"`
    55  	TLS            bool              `obfs:"tls,omitempty"`
    56  	Headers        map[string]string `obfs:"headers,omitempty"`
    57  	SkipCertVerify bool              `obfs:"skip-cert-verify,omitempty"`
    58  	Mux            bool              `obfs:"mux,omitempty"`
    59  }
    60  
    61  // StreamConn implements C.ProxyAdapter
    62  func (ss *ShadowSocks) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
    63  	switch ss.obfsMode {
    64  	case "tls":
    65  		c = obfs.NewTLSObfs(c, ss.obfsOption.Host)
    66  	case "http":
    67  		_, port, _ := net.SplitHostPort(ss.addr)
    68  		c = obfs.NewHTTPObfs(c, ss.obfsOption.Host, port, ss.obfsOption.RandomHost)
    69  	case "websocket":
    70  		var err error
    71  		c, err = v2rayObfs.NewV2rayObfs(c, ss.v2rayOption)
    72  		if err != nil {
    73  			return nil, fmt.Errorf("%s connect error: %w", ss.addr, err)
    74  		}
    75  	}
    76  	c = ss.cipher.StreamConn(c)
    77  	_, err := c.Write(serializesSocksAddr(metadata))
    78  	return c, err
    79  }
    80  
    81  // StreamPacketConn implements C.ProxyAdapter
    82  func (ss *ShadowSocks) StreamPacketConn(c net.Conn, _ *C.Metadata) (net.Conn, error) {
    83  	if !IsPacketConn(c) {
    84  		return c, fmt.Errorf("%s connect error: can not convert net.Conn to net.PacketConn", ss.addr)
    85  	}
    86  
    87  	addr, err := resolveUDPAddr("udp", ss.addr)
    88  	if err != nil {
    89  		return c, err
    90  	}
    91  
    92  	pc := ss.cipher.PacketConn(c.(net.PacketConn))
    93  	return WrapConn(&ssPacketConn{PacketConn: pc, rAddr: addr}), nil
    94  }
    95  
    96  // DialContext implements C.ProxyAdapter
    97  func (ss *ShadowSocks) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) {
    98  	c, err := dialer.DialContext(ctx, "tcp", ss.addr, ss.Base.DialOptions(opts...)...)
    99  	if err != nil {
   100  		return nil, fmt.Errorf("%s connect error: %w", ss.addr, err)
   101  	}
   102  	tcpKeepAlive(c)
   103  
   104  	defer func(cc net.Conn, e error) {
   105  		safeConnClose(cc, e)
   106  	}(c, err)
   107  
   108  	c, err = ss.StreamConn(c, metadata)
   109  	return NewConn(c, ss), err
   110  }
   111  
   112  // ListenPacketContext implements C.ProxyAdapter
   113  func (ss *ShadowSocks) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) {
   114  	pc, err := dialer.ListenPacket(ctx, "udp", "", ss.Base.DialOptions(opts...)...)
   115  	if err != nil {
   116  		return nil, err
   117  	}
   118  
   119  	c, err := ss.StreamPacketConn(WrapConn(pc), metadata)
   120  	if err != nil {
   121  		_ = pc.Close()
   122  		return nil, err
   123  	}
   124  
   125  	return NewPacketConn(c.(net.PacketConn), ss), nil
   126  }
   127  
   128  func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) {
   129  	addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port))
   130  	cipher := option.Cipher
   131  	password := option.Password
   132  	ciph, err := core.PickCipher(cipher, nil, password)
   133  	if err != nil {
   134  		return nil, fmt.Errorf("ss %s initialize error: %w", addr, err)
   135  	}
   136  
   137  	var v2rayOption *v2rayObfs.Option
   138  	var obfsOption *simpleObfsOption
   139  	obfsMode := ""
   140  
   141  	decoder := structure.NewDecoder(structure.Option{TagName: "obfs", WeaklyTypedInput: true})
   142  	if option.Plugin == "obfs" {
   143  		opts := simpleObfsOption{Host: "bing.com", RandomHost: option.RandomHost}
   144  		if err := decoder.Decode(option.PluginOpts, &opts); err != nil {
   145  			return nil, fmt.Errorf("ss %s initialize obfs error: %w", addr, err)
   146  		}
   147  
   148  		if opts.Mode != "tls" && opts.Mode != "http" {
   149  			return nil, fmt.Errorf("ss %s obfs mode error: %s", addr, opts.Mode)
   150  		}
   151  		obfsMode = opts.Mode
   152  		obfsOption = &opts
   153  	} else if option.Plugin == "v2ray-plugin" {
   154  		opts := v2rayObfsOption{Host: "bing.com", Mux: true}
   155  		if err := decoder.Decode(option.PluginOpts, &opts); err != nil {
   156  			return nil, fmt.Errorf("ss %s initialize v2ray-plugin error: %w", addr, err)
   157  		}
   158  
   159  		if opts.Mode != "websocket" {
   160  			return nil, fmt.Errorf("ss %s obfs mode error: %s", addr, opts.Mode)
   161  		}
   162  		obfsMode = opts.Mode
   163  		v2rayOption = &v2rayObfs.Option{
   164  			Host:       opts.Host,
   165  			Path:       opts.Path,
   166  			Headers:    opts.Headers,
   167  			Mux:        opts.Mux,
   168  			RandomHost: option.RandomHost,
   169  		}
   170  
   171  		if opts.TLS {
   172  			v2rayOption.TLS = true
   173  			v2rayOption.SkipCertVerify = opts.SkipCertVerify
   174  		}
   175  	}
   176  
   177  	return &ShadowSocks{
   178  		Base: &Base{
   179  			name:  option.Name,
   180  			addr:  addr,
   181  			tp:    C.Shadowsocks,
   182  			udp:   option.UDP,
   183  			iface: option.Interface,
   184  			rmark: option.RoutingMark,
   185  			dns:   option.RemoteDnsResolve,
   186  		},
   187  		cipher: ciph,
   188  
   189  		obfsMode:    obfsMode,
   190  		v2rayOption: v2rayOption,
   191  		obfsOption:  obfsOption,
   192  	}, nil
   193  }
   194  
   195  type ssPacketConn struct {
   196  	net.PacketConn
   197  	rAddr net.Addr
   198  }
   199  
   200  func (spc *ssPacketConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
   201  	packet, err := socks5.EncodeUDPPacket(socks5.ParseAddrToSocksAddr(addr), b)
   202  	if err != nil {
   203  		return
   204  	}
   205  	return spc.PacketConn.WriteTo(packet[3:], spc.rAddr)
   206  }
   207  
   208  func (spc *ssPacketConn) ReadFrom(b []byte) (int, net.Addr, error) {
   209  	n, _, e := spc.PacketConn.ReadFrom(b)
   210  	if e != nil {
   211  		return 0, nil, e
   212  	}
   213  
   214  	addr := socks5.SplitAddr(b[:n])
   215  	if addr == nil {
   216  		return 0, nil, errors.New("parse addr error")
   217  	}
   218  
   219  	udpAddr := addr.UDPAddr()
   220  	if udpAddr == nil {
   221  		return 0, nil, errors.New("parse addr error")
   222  	}
   223  
   224  	copy(b, b[len(addr):])
   225  	return n - len(addr), udpAddr, e
   226  }