diff --git a/connector/ss/connector.go b/connector/ss/connector.go index 3c4d0338..df2cd201 100644 --- a/connector/ss/connector.go +++ b/connector/ss/connector.go @@ -7,6 +7,8 @@ import ( "net" "time" + "strings" + "github.com/go-gost/core/common/bufpool" "github.com/go-gost/core/connector" md "github.com/go-gost/core/metadata" @@ -15,6 +17,7 @@ import ( "github.com/go-gost/go-shadowsocks2/utils" "github.com/go-gost/gosocks5" ctxvalue "github.com/go-gost/x/ctx" + "github.com/go-gost/x/internal/util/ss/none" "github.com/go-gost/x/registry" ) @@ -51,6 +54,11 @@ func (c *ssConnector) Init(md md.Metadata) (err error) { method := c.options.Auth.Username() password, _ := c.options.Auth.Password() + if strings.EqualFold(method, "none") || strings.EqualFold(method, "dummy") { + c.client = core.NewTCPClient(core.ClientConfig{Cipher: none.Cipher}) + return + } + clientConfig, err := utils.NewClientConfig(method, password) if err != nil { return err diff --git a/connector/ss/udp/connector.go b/connector/ss/udp/connector.go index 9a30a488..0d76154e 100644 --- a/connector/ss/udp/connector.go +++ b/connector/ss/udp/connector.go @@ -6,6 +6,7 @@ import ( "fmt" "net" "net/netip" + "strings" "time" "github.com/go-gost/core/connector" @@ -16,6 +17,7 @@ import ( ctxvalue "github.com/go-gost/x/ctx" "github.com/go-gost/x/internal/util/relay" "github.com/go-gost/x/internal/util/ss" + ssnone "github.com/go-gost/x/internal/util/ss/none" "github.com/go-gost/x/registry" ) @@ -52,6 +54,13 @@ func (c *ssuConnector) Init(md md.Metadata) (err error) { method := c.options.Auth.Username() password, _ := c.options.Auth.Password() + + if strings.EqualFold(method, "none") || strings.EqualFold(method, "dummy") { + c.clientCfg = core.ClientConfig{Cipher: ssnone.Cipher, UDPTimeout: time.Minute} + c.tcpClient = core.NewTCPClient(core.ClientConfig{Cipher: ssnone.Cipher}) + return + } + clientConfig, err := utils.NewClientConfig(method, password) if err != nil { return err diff --git a/handler/ss/handler.go b/handler/ss/handler.go index 973530c6..ca7f7f0c 100644 --- a/handler/ss/handler.go +++ b/handler/ss/handler.go @@ -7,6 +7,7 @@ import ( "crypto/tls" "errors" "net" + "strings" "time" "github.com/go-gost/core/bypass" @@ -21,6 +22,7 @@ import ( ictx "github.com/go-gost/x/internal/ctx" xnet "github.com/go-gost/x/internal/net" "github.com/go-gost/x/internal/util/sniffing" + "github.com/go-gost/x/internal/util/ss/none" tls_util "github.com/go-gost/x/internal/util/tls" rate_limiter "github.com/go-gost/x/limiter/rate" xstats "github.com/go-gost/x/observer/stats" @@ -64,14 +66,22 @@ func (h *ssHandler) Init(md md.Metadata) (err error) { method := h.options.Auth.Username() password, _ := h.options.Auth.Password() - serverConfig, err := utils.NewServerConfig(method, password, h.md.users) - if err != nil { - return err - } - h.server = core.NewTCPServer(serverConfig) - err = h.server.Init() - if err != nil { - return err + if strings.EqualFold(method, "none") || strings.EqualFold(method, "dummy") { + h.server = core.NewTCPServer(core.ServerConfig{Cipher: none.Cipher, Users: h.md.users}) + err = h.server.Init() + if err != nil { + return err + } + } else { + serverConfig, err := utils.NewServerConfig(method, password, h.md.users) + if err != nil { + return err + } + h.server = core.NewTCPServer(serverConfig) + err = h.server.Init() + if err != nil { + return err + } } for _, ro := range h.options.Recorders { diff --git a/handler/ss/udp/handler.go b/handler/ss/udp/handler.go index 9f650889..ebdc6f64 100644 --- a/handler/ss/udp/handler.go +++ b/handler/ss/udp/handler.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "net" + "strings" "sync" "time" @@ -20,6 +21,7 @@ import ( ictx "github.com/go-gost/x/internal/ctx" "github.com/go-gost/x/internal/net/udp" "github.com/go-gost/x/internal/util/relay" + "github.com/go-gost/x/internal/util/ss/none" rate_limiter "github.com/go-gost/x/limiter/rate" xrecorder "github.com/go-gost/x/recorder" "github.com/go-gost/x/registry" @@ -62,21 +64,35 @@ func (h *ssuHandler) Init(md md.Metadata) (err error) { method := h.options.Auth.Username() password, _ := h.options.Auth.Password() - serverConfig, err := utils.NewServerConfig(method, password, h.md.users) - if err != nil { - return err - } - serverConfig.UDPTimeout = time.Minute - h.udpServer = core.NewUDPServer(serverConfig) - err = h.udpServer.Init() - if err != nil { - return err - } + if strings.EqualFold(method, "none") || strings.EqualFold(method, "dummy") { + c := core.ServerConfig{Cipher: none.Cipher, Users: h.md.users, UDPTimeout: time.Minute} + h.udpServer = core.NewUDPServer(c) + err = h.udpServer.Init() + if err != nil { + return err + } + h.tcpServer = core.NewTCPServer(c) + err = h.tcpServer.Init() + if err != nil { + return err + } + } else { + serverConfig, err := utils.NewServerConfig(method, password, h.md.users) + if err != nil { + return err + } + serverConfig.UDPTimeout = time.Minute + h.udpServer = core.NewUDPServer(serverConfig) + err = h.udpServer.Init() + if err != nil { + return err + } - h.tcpServer = core.NewTCPServer(serverConfig) - err = h.tcpServer.Init() - if err != nil { - return err + h.tcpServer = core.NewTCPServer(serverConfig) + err = h.tcpServer.Init() + if err != nil { + return err + } } for _, ro := range h.options.Recorders { diff --git a/internal/util/ss/none/aead.go b/internal/util/ss/none/aead.go new file mode 100644 index 00000000..ce36cf42 --- /dev/null +++ b/internal/util/ss/none/aead.go @@ -0,0 +1,25 @@ +// Package none provides a no-op cipher for Shadowsocks that passes data +// through without encryption. It implements the go-shadowsocks2 core.ShadowCipher +// interface. This cipher is intended for debugging, testing, and compatibility +// purposes and MUST NOT be used in production. +package none + +// noopAEAD is a cipher.AEAD that performs no encryption or authentication. +// All data passes through unchanged. +type noopAEAD struct{} + +// NonceSize returns 0 since no nonce is needed. +func (noopAEAD) NonceSize() int { return 0 } + +// Overhead returns 0 since no authentication tag is added. +func (noopAEAD) Overhead() int { return 0 } + +// Seal appends plaintext to dst without encryption. +func (noopAEAD) Seal(dst, nonce, plaintext, additionalData []byte) []byte { + return append(dst, plaintext...) +} + +// Open appends ciphertext to dst without decryption. +func (noopAEAD) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) { + return append(dst, ciphertext...), nil +} diff --git a/internal/util/ss/none/cipher.go b/internal/util/ss/none/cipher.go new file mode 100644 index 00000000..f2895524 --- /dev/null +++ b/internal/util/ss/none/cipher.go @@ -0,0 +1,65 @@ +package none + +import ( + "crypto/cipher" + "net" + "time" + + "github.com/go-gost/go-shadowsocks2/core" + "github.com/go-gost/go-shadowsocks2/shadowaead" +) + +// Cipher is a no-op Shadowsocks cipher that passes data through without +// encryption. It supports both TCP and UDP via the standard go-shadowsocks2 +// ShadowCipher interface. The SS protocol framing (length-prefixed chunks) +// is preserved, but no encryption or authentication is applied. +var Cipher core.ShadowCipher = &noneCipher{} + +type noneCipher struct{} + +// SaltSize returns a non-zero value so the go-shadowsocks2 bloom ring +// does not reject every connection as a repeated salt. The 16-byte salt +// is generated randomly and is not used for any actual key derivation. +func (noneCipher) SaltSize() int { return 16 } + +// KeySize returns 0 since no encryption key is used. +func (noneCipher) KeySize() int { return 0 } + +// NonceSize returns 0 since no nonce is used. +func (noneCipher) NonceSize() int { return 0 } + +// TagSize returns 0 since no authentication tag is used. +func (noneCipher) TagSize() int { return 0 } + +// Key returns nil. +func (noneCipher) Key() []byte { return nil } + +// Keys returns nil. +func (noneCipher) Keys() [][]byte { return nil } + +// FirstKey returns nil. +func (noneCipher) FirstKey() []byte { return nil } + +// Encrypter returns a no-op AEAD. +func (noneCipher) Encrypter(key, salt []byte) (cipher.AEAD, error) { + return noopAEAD{}, nil +} + +// Decrypter returns a no-op AEAD. +func (noneCipher) Decrypter(key, salt []byte) (cipher.AEAD, error) { + return noopAEAD{}, nil +} + +// TCPConn wraps a net.Conn with the no-op cipher using the standard +// go-shadowsocks2 AEAD stream connection. The SS protocol framing +// (salt + length-prefixed chunks) is preserved but unencrypted. +func (c noneCipher) TCPConn(conn net.Conn, users []core.UserConfig, role int) core.TCPConn { + return shadowaead.NewConn(conn, c) +} + +// NewUDPSessionManager creates a UDP session manager using the standard +// go-shadowsocks2 AEAD UDP implementation. Packets are framed but +// unencrypted. +func (c noneCipher) NewUDPSessionManager(timeout time.Duration, users []core.UserConfig, windowSize, role int) core.UDPSessionManager { + return shadowaead.NewAEADSessionManager(c, timeout, role) +}