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 }