feat(ss): support none/dummy cipher via no-op AEAD implementation
The go-gost fork of go-shadowsocks2 dropped built-in support for the none/dummy cipher that was present in the original go-shadowsocks2. Combined with the SS components' hard requirement for auth (and therefore a valid cipher), this made gost fail to start with method=none or method=dummy. Add a no-op cipher in x/internal/util/ss/none/ that implements the core.ShadowCipher interface using the standard go-shadowsocks2 AEAD framing (salt + length-prefixed chunks, target address embedding) without actual encryption. SaltSize=16 ensures random salts on every connection to avoid bloom-ring replay detection. Update SS TCP connector/handler and SS UDP connector/handler to detect method=none/dummy (case-insensitive) and use the no-op cipher instead of utils.NewClientConfig/NewServerConfig. Fixes go-gost/x#105
This commit is contained in:
@@ -7,6 +7,8 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/go-gost/core/common/bufpool"
|
"github.com/go-gost/core/common/bufpool"
|
||||||
"github.com/go-gost/core/connector"
|
"github.com/go-gost/core/connector"
|
||||||
md "github.com/go-gost/core/metadata"
|
md "github.com/go-gost/core/metadata"
|
||||||
@@ -15,6 +17,7 @@ import (
|
|||||||
"github.com/go-gost/go-shadowsocks2/utils"
|
"github.com/go-gost/go-shadowsocks2/utils"
|
||||||
"github.com/go-gost/gosocks5"
|
"github.com/go-gost/gosocks5"
|
||||||
ctxvalue "github.com/go-gost/x/ctx"
|
ctxvalue "github.com/go-gost/x/ctx"
|
||||||
|
"github.com/go-gost/x/internal/util/ss/none"
|
||||||
"github.com/go-gost/x/registry"
|
"github.com/go-gost/x/registry"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -51,6 +54,11 @@ func (c *ssConnector) Init(md md.Metadata) (err error) {
|
|||||||
method := c.options.Auth.Username()
|
method := c.options.Auth.Username()
|
||||||
password, _ := c.options.Auth.Password()
|
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)
|
clientConfig, err := utils.NewClientConfig(method, password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-gost/core/connector"
|
"github.com/go-gost/core/connector"
|
||||||
@@ -16,6 +17,7 @@ import (
|
|||||||
ctxvalue "github.com/go-gost/x/ctx"
|
ctxvalue "github.com/go-gost/x/ctx"
|
||||||
"github.com/go-gost/x/internal/util/relay"
|
"github.com/go-gost/x/internal/util/relay"
|
||||||
"github.com/go-gost/x/internal/util/ss"
|
"github.com/go-gost/x/internal/util/ss"
|
||||||
|
ssnone "github.com/go-gost/x/internal/util/ss/none"
|
||||||
"github.com/go-gost/x/registry"
|
"github.com/go-gost/x/registry"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -52,6 +54,13 @@ func (c *ssuConnector) Init(md md.Metadata) (err error) {
|
|||||||
|
|
||||||
method := c.options.Auth.Username()
|
method := c.options.Auth.Username()
|
||||||
password, _ := c.options.Auth.Password()
|
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)
|
clientConfig, err := utils.NewClientConfig(method, password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
+18
-8
@@ -7,6 +7,7 @@ import (
|
|||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"errors"
|
"errors"
|
||||||
"net"
|
"net"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-gost/core/bypass"
|
"github.com/go-gost/core/bypass"
|
||||||
@@ -21,6 +22,7 @@ import (
|
|||||||
ictx "github.com/go-gost/x/internal/ctx"
|
ictx "github.com/go-gost/x/internal/ctx"
|
||||||
xnet "github.com/go-gost/x/internal/net"
|
xnet "github.com/go-gost/x/internal/net"
|
||||||
"github.com/go-gost/x/internal/util/sniffing"
|
"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"
|
tls_util "github.com/go-gost/x/internal/util/tls"
|
||||||
rate_limiter "github.com/go-gost/x/limiter/rate"
|
rate_limiter "github.com/go-gost/x/limiter/rate"
|
||||||
xstats "github.com/go-gost/x/observer/stats"
|
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()
|
method := h.options.Auth.Username()
|
||||||
password, _ := h.options.Auth.Password()
|
password, _ := h.options.Auth.Password()
|
||||||
|
|
||||||
serverConfig, err := utils.NewServerConfig(method, password, h.md.users)
|
if strings.EqualFold(method, "none") || strings.EqualFold(method, "dummy") {
|
||||||
if err != nil {
|
h.server = core.NewTCPServer(core.ServerConfig{Cipher: none.Cipher, Users: h.md.users})
|
||||||
return err
|
err = h.server.Init()
|
||||||
}
|
if err != nil {
|
||||||
h.server = core.NewTCPServer(serverConfig)
|
return err
|
||||||
err = h.server.Init()
|
}
|
||||||
if err != nil {
|
} else {
|
||||||
return err
|
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 {
|
for _, ro := range h.options.Recorders {
|
||||||
|
|||||||
+30
-14
@@ -6,6 +6,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -20,6 +21,7 @@ import (
|
|||||||
ictx "github.com/go-gost/x/internal/ctx"
|
ictx "github.com/go-gost/x/internal/ctx"
|
||||||
"github.com/go-gost/x/internal/net/udp"
|
"github.com/go-gost/x/internal/net/udp"
|
||||||
"github.com/go-gost/x/internal/util/relay"
|
"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"
|
rate_limiter "github.com/go-gost/x/limiter/rate"
|
||||||
xrecorder "github.com/go-gost/x/recorder"
|
xrecorder "github.com/go-gost/x/recorder"
|
||||||
"github.com/go-gost/x/registry"
|
"github.com/go-gost/x/registry"
|
||||||
@@ -62,21 +64,35 @@ func (h *ssuHandler) Init(md md.Metadata) (err error) {
|
|||||||
method := h.options.Auth.Username()
|
method := h.options.Auth.Username()
|
||||||
password, _ := h.options.Auth.Password()
|
password, _ := h.options.Auth.Password()
|
||||||
|
|
||||||
serverConfig, err := utils.NewServerConfig(method, password, h.md.users)
|
if strings.EqualFold(method, "none") || strings.EqualFold(method, "dummy") {
|
||||||
if err != nil {
|
c := core.ServerConfig{Cipher: none.Cipher, Users: h.md.users, UDPTimeout: time.Minute}
|
||||||
return err
|
h.udpServer = core.NewUDPServer(c)
|
||||||
}
|
err = h.udpServer.Init()
|
||||||
serverConfig.UDPTimeout = time.Minute
|
if err != nil {
|
||||||
h.udpServer = core.NewUDPServer(serverConfig)
|
return err
|
||||||
err = h.udpServer.Init()
|
}
|
||||||
if err != nil {
|
h.tcpServer = core.NewTCPServer(c)
|
||||||
return err
|
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)
|
h.tcpServer = core.NewTCPServer(serverConfig)
|
||||||
err = h.tcpServer.Init()
|
err = h.tcpServer.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, ro := range h.options.Recorders {
|
for _, ro := range h.options.Recorders {
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user