Files
x/connector/ss/connector.go
T
Evsyukov Denis 802943bd28 fix(ss): propagate cipher errors and decode SIP002 base64 auth
- Return err instead of nil in SS TCP/UDP connector Init when
  NewClientConfig fails, preventing nil pointer dereference in WrapConn
- Initialize tcpClient in UDP connector for UDP-over-TCP path
- Add decodeSIP002Auth to handle ss://BASE64(method:password)@host:port
  URL format with RawURLEncoding and StdEncoding fallback
- Update buildServiceConfig and buildNodeConfig to decode SIP002 auth
  for ss* schemes before falling back to standard userinfo parsing
- Support RawURLEncoding in parseAuthFromCmd with StdEncoding fallback
- Add tests for decodeSIP002Auth, parseAuthFromCmd, and integration
  tests for buildNodeConfig/buildServiceConfig
2026-04-21 20:55:45 +08:00

120 lines
2.5 KiB
Go

package ss
import (
"context"
"fmt"
"net"
"time"
"github.com/go-gost/core/common/bufpool"
"github.com/go-gost/core/connector"
md "github.com/go-gost/core/metadata"
"github.com/go-gost/go-shadowsocks2/core"
"github.com/go-gost/go-shadowsocks2/socks"
"github.com/go-gost/go-shadowsocks2/utils"
"github.com/go-gost/gosocks5"
ctxvalue "github.com/go-gost/x/ctx"
"github.com/go-gost/x/registry"
)
func init() {
registry.ConnectorRegistry().Register("ss", NewConnector)
}
type ssConnector struct {
client core.TCPClient
md metadata
options connector.Options
}
func NewConnector(opts ...connector.Option) connector.Connector {
options := connector.Options{}
for _, opt := range opts {
opt(&options)
}
return &ssConnector{
options: options,
}
}
func (c *ssConnector) Init(md md.Metadata) (err error) {
if err = c.parseMetadata(md); err != nil {
return
}
if c.options.Auth != nil {
method := c.options.Auth.Username()
password, _ := c.options.Auth.Password()
clientConfig, err := utils.NewClientConfig(method, password)
if err != nil {
return err
}
c.client = core.NewTCPClient(clientConfig)
}
return
}
func (c *ssConnector) Connect(ctx context.Context, conn net.Conn, network, address string, opts ...connector.ConnectOption) (net.Conn, error) {
log := c.options.Logger.WithFields(map[string]any{
"remote": conn.RemoteAddr().String(),
"local": conn.LocalAddr().String(),
"network": network,
"address": address,
"sid": string(ctxvalue.SidFromContext(ctx)),
})
log.Debugf("connect %s/%s", address, network)
switch network {
case "tcp", "tcp4", "tcp6":
if _, ok := conn.(net.PacketConn); ok {
err := fmt.Errorf("tcp over udp is unsupported")
log.Error(err)
return nil, err
}
default:
err := fmt.Errorf("network %s is unsupported", network)
log.Error(err)
return nil, err
}
addr := gosocks5.Addr{}
if err := addr.ParseFrom(address); err != nil {
log.Error(err)
return nil, err
}
rawaddr := bufpool.Get(512)
defer bufpool.Put(rawaddr)
n, err := addr.Encode(rawaddr)
if err != nil {
log.Error("encoding addr: ", err)
return nil, err
}
if c.md.connectTimeout > 0 {
conn.SetDeadline(time.Now().Add(c.md.connectTimeout))
defer conn.SetDeadline(time.Time{})
}
target := socks.Addr(rawaddr[:n])
conn, err = c.client.WrapConn(conn, target)
if err != nil {
return nil, err
}
var sc net.Conn
if c.md.noDelay {
err := conn.(core.TCPConn).ClientFirstWrite()
if err != nil {
return nil, err
}
}
sc = conn
return sc, nil
}