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

     1  package outbound
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"net"
     7  	"strconv"
     8  	"strings"
     9  
    10  	"github.com/yaling888/clash/common/convert"
    11  	"github.com/yaling888/clash/component/dialer"
    12  	C "github.com/yaling888/clash/constant"
    13  	"github.com/yaling888/clash/transport/shadowsocks/core"
    14  	"github.com/yaling888/clash/transport/shadowsocks/shadowaead"
    15  	"github.com/yaling888/clash/transport/shadowsocks/shadowstream"
    16  	"github.com/yaling888/clash/transport/ssr/obfs"
    17  	"github.com/yaling888/clash/transport/ssr/protocol"
    18  )
    19  
    20  var _ C.ProxyAdapter = (*ShadowSocksR)(nil)
    21  
    22  type ShadowSocksR struct {
    23  	*Base
    24  	cipher   core.Cipher
    25  	obfs     obfs.Obfs
    26  	protocol protocol.Protocol
    27  }
    28  
    29  type ShadowSocksROption 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  	Obfs             string `proxy:"obfs"`
    37  	ObfsParam        string `proxy:"obfs-param,omitempty"`
    38  	Protocol         string `proxy:"protocol"`
    39  	ProtocolParam    string `proxy:"protocol-param,omitempty"`
    40  	UDP              bool   `proxy:"udp,omitempty"`
    41  	RandomHost       bool   `proxy:"rand-host,omitempty"`
    42  	RemoteDnsResolve bool   `proxy:"remote-dns-resolve,omitempty"`
    43  }
    44  
    45  // StreamConn implements C.ProxyAdapter
    46  func (ssr *ShadowSocksR) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
    47  	c = ssr.obfs.StreamConn(c)
    48  	c = ssr.cipher.StreamConn(c)
    49  	var (
    50  		iv  []byte
    51  		err error
    52  	)
    53  	switch conn := c.(type) {
    54  	case *shadowstream.Conn:
    55  		iv, err = conn.ObtainWriteIV()
    56  		if err != nil {
    57  			return nil, err
    58  		}
    59  	case *shadowaead.Conn:
    60  		return nil, fmt.Errorf("invalid connection type")
    61  	}
    62  	c = ssr.protocol.StreamConn(c, iv)
    63  	_, err = c.Write(serializesSocksAddr(metadata))
    64  	return c, err
    65  }
    66  
    67  // StreamPacketConn implements C.ProxyAdapter
    68  func (ssr *ShadowSocksR) StreamPacketConn(c net.Conn, _ *C.Metadata) (net.Conn, error) {
    69  	if !IsPacketConn(c) {
    70  		return c, fmt.Errorf("%s connect error: can not convert net.Conn to net.PacketConn", ssr.addr)
    71  	}
    72  
    73  	addr, err := resolveUDPAddr("udp", ssr.addr)
    74  	if err != nil {
    75  		return c, err
    76  	}
    77  
    78  	pc := ssr.cipher.PacketConn(c.(net.PacketConn))
    79  	pc = ssr.protocol.PacketConn(pc)
    80  	return WrapConn(&ssPacketConn{PacketConn: pc, rAddr: addr}), nil
    81  }
    82  
    83  // DialContext implements C.ProxyAdapter
    84  func (ssr *ShadowSocksR) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) {
    85  	c, err := dialer.DialContext(ctx, "tcp", ssr.addr, ssr.Base.DialOptions(opts...)...)
    86  	if err != nil {
    87  		return nil, fmt.Errorf("%s connect error: %w", ssr.addr, err)
    88  	}
    89  	tcpKeepAlive(c)
    90  
    91  	defer func(cc net.Conn, e error) {
    92  		safeConnClose(cc, e)
    93  	}(c, err)
    94  
    95  	c, err = ssr.StreamConn(c, metadata)
    96  	return NewConn(c, ssr), err
    97  }
    98  
    99  // ListenPacketContext implements C.ProxyAdapter
   100  func (ssr *ShadowSocksR) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) {
   101  	pc, err := dialer.ListenPacket(ctx, "udp", "", ssr.Base.DialOptions(opts...)...)
   102  	if err != nil {
   103  		return nil, err
   104  	}
   105  
   106  	c, err := ssr.StreamPacketConn(WrapConn(pc), metadata)
   107  	if err != nil {
   108  		_ = pc.Close()
   109  		return nil, err
   110  	}
   111  
   112  	return NewPacketConn(c.(net.PacketConn), ssr), nil
   113  }
   114  
   115  func NewShadowSocksR(option ShadowSocksROption) (*ShadowSocksR, error) {
   116  	// SSR protocol compatibility
   117  	// https://github.com/yaling888/clash/pull/2056
   118  	if strings.EqualFold(option.Cipher, "none") {
   119  		option.Cipher = "dummy"
   120  	}
   121  
   122  	addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port))
   123  	cipher := option.Cipher
   124  	password := option.Password
   125  	coreCiph, err := core.PickCipher(cipher, nil, password)
   126  	if err != nil {
   127  		return nil, fmt.Errorf("ssr %s initialize error: %w", addr, err)
   128  	}
   129  	var (
   130  		ivSize int
   131  		key    []byte
   132  	)
   133  
   134  	if strings.EqualFold(option.Cipher, "dummy") {
   135  		ivSize = 0
   136  		key = core.Kdf(option.Password, 16)
   137  	} else {
   138  		ciph, ok := coreCiph.(*core.StreamCipher)
   139  		if !ok {
   140  			return nil, fmt.Errorf("%s is not none or a supported stream cipher in ssr", cipher)
   141  		}
   142  		ivSize = ciph.IVSize()
   143  		key = ciph.Key
   144  	}
   145  
   146  	option.Obfs = strings.ToLower(option.Obfs)
   147  	if strings.HasPrefix(option.Obfs, "http_") && (option.RandomHost || len(option.ObfsParam) == 0) {
   148  		option.ObfsParam = convert.RandHost()
   149  	}
   150  
   151  	obfsM, obfsOverhead, err := obfs.PickObfs(option.Obfs, &obfs.Base{
   152  		Host:   option.Server,
   153  		Port:   option.Port,
   154  		Key:    key,
   155  		IVSize: ivSize,
   156  		Param:  option.ObfsParam,
   157  	})
   158  	if err != nil {
   159  		return nil, fmt.Errorf("ssr %s initialize obfs error: %w", addr, err)
   160  	}
   161  
   162  	option.Protocol = strings.ToLower(option.Protocol)
   163  	protocolM, err := protocol.PickProtocol(option.Protocol, &protocol.Base{
   164  		Key:      key,
   165  		Overhead: obfsOverhead,
   166  		Param:    option.ProtocolParam,
   167  	})
   168  	if err != nil {
   169  		return nil, fmt.Errorf("ssr %s initialize protocol error: %w", addr, err)
   170  	}
   171  
   172  	return &ShadowSocksR{
   173  		Base: &Base{
   174  			name:  option.Name,
   175  			addr:  addr,
   176  			tp:    C.ShadowsocksR,
   177  			udp:   option.UDP,
   178  			iface: option.Interface,
   179  			rmark: option.RoutingMark,
   180  			dns:   option.RemoteDnsResolve,
   181  		},
   182  		cipher:   coreCiph,
   183  		obfs:     obfsM,
   184  		protocol: protocolM,
   185  	}, nil
   186  }