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