@@ -13,6 +13,7 @@ import (
1313 C "github.com/metacubex/mihomo/constant"
1414 "github.com/metacubex/mihomo/ntp"
1515 gost "github.com/metacubex/mihomo/transport/gost-plugin"
16+ "github.com/metacubex/mihomo/transport/kcptun"
1617 "github.com/metacubex/mihomo/transport/restls"
1718 obfs "github.com/metacubex/mihomo/transport/simple-obfs"
1819 shadowtls "github.com/metacubex/mihomo/transport/sing-shadowtls"
@@ -36,6 +37,7 @@ type ShadowSocks struct {
3637 gostOption * gost.Option
3738 shadowTLSOption * shadowtls.ShadowTLSOption
3839 restlsConfig * restls.Config
40+ kcptunClient * kcptun.Client
3941}
4042
4143type ShadowSocksOption struct {
@@ -106,6 +108,32 @@ type restlsOption struct {
106108 RestlsScript string `obfs:"restls-script,omitempty"`
107109}
108110
111+ type kcpTunOption struct {
112+ Key string `obfs:"key,omitempty"`
113+ Crypt string `obfs:"crypt,omitempty"`
114+ Mode string `obfs:"mode,omitempty"`
115+ Conn int `obfs:"conn,omitempty"`
116+ AutoExpire int `obfs:"autoexpire,omitempty"`
117+ ScavengeTTL int `obfs:"scavengettl,omitempty"`
118+ MTU int `obfs:"mtu,omitempty"`
119+ SndWnd int `obfs:"sndwnd,omitempty"`
120+ RcvWnd int `obfs:"rcvwnd,omitempty"`
121+ DataShard int `obfs:"datashard,omitempty"`
122+ ParityShard int `obfs:"parityshard,omitempty"`
123+ DSCP int `obfs:"dscp,omitempty"`
124+ NoComp bool `obfs:"nocomp,omitempty"`
125+ AckNodelay bool `obfs:"acknodelay,omitempty"`
126+ NoDelay int `obfs:"nodelay,omitempty"`
127+ Interval int `obfs:"interval,omitempty"`
128+ Resend int `obfs:"resend,omitempty"`
129+ NoCongestion int `obfs:"nc,omitempty"`
130+ SockBuf int `obfs:"sockbuf,omitempty"`
131+ SmuxVer int `obfs:"smuxver,omitempty"`
132+ SmuxBuf int `obfs:"smuxbuf,omitempty"`
133+ StreamBuf int `obfs:"streambuf,omitempty"`
134+ KeepAlive int `obfs:"keepalive,omitempty"`
135+ }
136+
109137// StreamConnContext implements C.ProxyAdapter
110138func (ss * ShadowSocks ) StreamConnContext (ctx context.Context , c net.Conn , metadata * C.Metadata ) (_ net.Conn , err error ) {
111139 useEarly := false
@@ -174,7 +202,27 @@ func (ss *ShadowSocks) DialContextWithDialer(ctx context.Context, dialer C.Diale
174202 return nil , err
175203 }
176204 }
177- c , err := dialer .DialContext (ctx , "tcp" , ss .addr )
205+ var c net.Conn
206+ if ss .kcptunClient != nil {
207+ c , err = ss .kcptunClient .OpenStream (ctx , func (ctx context.Context ) (net.PacketConn , net.Addr , error ) {
208+ if err = ss .ResolveUDP (ctx , metadata ); err != nil {
209+ return nil , nil , err
210+ }
211+ addr , err := resolveUDPAddr (ctx , "udp" , ss .addr , ss .prefer )
212+ if err != nil {
213+ return nil , nil , err
214+ }
215+
216+ pc , err := dialer .ListenPacket (ctx , "udp" , "" , addr .AddrPort ())
217+ if err != nil {
218+ return nil , nil , err
219+ }
220+
221+ return pc , addr , nil
222+ })
223+ } else {
224+ c , err = dialer .DialContext (ctx , "tcp" , ss .addr )
225+ }
178226 if err != nil {
179227 return nil , fmt .Errorf ("%s connect error: %w" , ss .addr , err )
180228 }
@@ -256,6 +304,13 @@ func (ss *ShadowSocks) SupportUOT() bool {
256304 return ss .option .UDPOverTCP
257305}
258306
307+ func (ss * ShadowSocks ) Close () error {
308+ if ss .kcptunClient != nil {
309+ return ss .kcptunClient .Close ()
310+ }
311+ return nil
312+ }
313+
259314func NewShadowSocks (option ShadowSocksOption ) (* ShadowSocks , error ) {
260315 addr := net .JoinHostPort (option .Server , strconv .Itoa (option .Port ))
261316 method , err := shadowsocks .CreateMethod (option .Cipher , shadowsocks.MethodOptions {
@@ -271,6 +326,7 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) {
271326 var obfsOption * simpleObfsOption
272327 var shadowTLSOpt * shadowtls.ShadowTLSOption
273328 var restlsConfig * restls.Config
329+ var kcptunClient * kcptun.Client
274330 obfsMode := ""
275331
276332 decoder := structure .NewDecoder (structure.Option {TagName : "obfs" , WeaklyTypedInput : true })
@@ -384,6 +440,39 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) {
384440 return nil , fmt .Errorf ("ss %s initialize restls-plugin error: %w" , addr , err )
385441 }
386442
443+ } else if option .Plugin == kcptun .Mode {
444+ obfsMode = kcptun .Mode
445+ kcptunOpt := & kcpTunOption {}
446+ if err := decoder .Decode (option .PluginOpts , kcptunOpt ); err != nil {
447+ return nil , fmt .Errorf ("ss %s initialize kcptun-plugin error: %w" , addr , err )
448+ }
449+
450+ kcptunClient = kcptun .NewClient (kcptun.Config {
451+ Key : kcptunOpt .Key ,
452+ Crypt : kcptunOpt .Crypt ,
453+ Mode : kcptunOpt .Mode ,
454+ Conn : kcptunOpt .Conn ,
455+ AutoExpire : kcptunOpt .AutoExpire ,
456+ ScavengeTTL : kcptunOpt .ScavengeTTL ,
457+ MTU : kcptunOpt .MTU ,
458+ SndWnd : kcptunOpt .SndWnd ,
459+ RcvWnd : kcptunOpt .RcvWnd ,
460+ DataShard : kcptunOpt .DataShard ,
461+ ParityShard : kcptunOpt .ParityShard ,
462+ DSCP : kcptunOpt .DSCP ,
463+ NoComp : kcptunOpt .NoComp ,
464+ AckNodelay : kcptunOpt .AckNodelay ,
465+ NoDelay : kcptunOpt .NoDelay ,
466+ Interval : kcptunOpt .Interval ,
467+ Resend : kcptunOpt .Resend ,
468+ NoCongestion : kcptunOpt .NoCongestion ,
469+ SockBuf : kcptunOpt .SockBuf ,
470+ SmuxVer : kcptunOpt .SmuxVer ,
471+ SmuxBuf : kcptunOpt .SmuxBuf ,
472+ StreamBuf : kcptunOpt .StreamBuf ,
473+ KeepAlive : kcptunOpt .KeepAlive ,
474+ })
475+ option .UDPOverTCP = true // must open uot
387476 }
388477 switch option .UDPOverTCPVersion {
389478 case uot .Version , uot .LegacyVersion :
@@ -414,5 +503,6 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) {
414503 obfsOption : obfsOption ,
415504 shadowTLSOption : shadowTLSOpt ,
416505 restlsConfig : restlsConfig ,
506+ kcptunClient : kcptunClient ,
417507 }, nil
418508}
0 commit comments