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 }