Files
sing-box-extended-mirror/protocol/vless/outbound.go
T

461 lines
13 KiB
Go
Raw Normal View History

2024-11-02 00:39:02 +08:00
package vless
2022-09-12 21:59:27 +08:00
import (
"context"
2026-02-26 18:03:59 +03:00
stdtls "crypto/tls"
"encoding/base64"
2022-09-12 21:59:27 +08:00
"net"
2026-02-26 18:03:59 +03:00
"strings"
"sync"
2022-09-12 21:59:27 +08:00
"github.com/sagernet/sing-box/adapter"
2024-11-02 00:39:02 +08:00
"github.com/sagernet/sing-box/adapter/outbound"
2022-09-12 21:59:27 +08:00
"github.com/sagernet/sing-box/common/dialer"
2023-04-24 09:55:46 +08:00
"github.com/sagernet/sing-box/common/mux"
2022-09-12 21:59:27 +08:00
"github.com/sagernet/sing-box/common/tls"
2026-02-26 18:03:59 +03:00
"github.com/sagernet/sing-box/common/vision"
2022-09-12 21:59:27 +08:00
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
2026-02-26 18:03:59 +03:00
"github.com/sagernet/sing-box/protocol/vless/encryption"
2022-09-12 21:59:27 +08:00
"github.com/sagernet/sing-box/transport/v2ray"
"github.com/sagernet/sing-vmess/packetaddr"
2024-07-02 14:01:35 +08:00
"github.com/sagernet/sing-vmess/vless"
2022-09-12 21:59:27 +08:00
"github.com/sagernet/sing/common"
2023-03-03 16:31:07 +08:00
"github.com/sagernet/sing/common/bufio"
2022-09-12 21:59:27 +08:00
E "github.com/sagernet/sing/common/exceptions"
2024-11-02 00:39:02 +08:00
"github.com/sagernet/sing/common/logger"
2022-09-12 21:59:27 +08:00
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
)
2024-11-02 00:39:02 +08:00
func RegisterOutbound(registry *outbound.Registry) {
outbound.Register[option.VLESSOutboundOptions](registry, C.TypeVLESS, NewOutbound)
}
2022-09-12 21:59:27 +08:00
2026-07-25 13:05:31 +08:00
var _ adapter.OutboundWithMultiplex = (*Outbound)(nil)
2024-11-02 00:39:02 +08:00
type Outbound struct {
outbound.Adapter
logger logger.ContextLogger
2023-04-24 09:55:46 +08:00
dialer N.Dialer
client *vless.Client
serverAddr M.Socksaddr
multiplexDialer *mux.Client
tlsConfig tls.Config
2025-08-11 21:18:34 +08:00
tlsDialer tls.Dialer
2023-04-24 09:55:46 +08:00
transport adapter.V2RayClientTransport
packetAddr bool
xudp bool
2026-02-26 18:03:59 +03:00
encryption *encryption.ClientInstance
vision bool
2022-09-12 21:59:27 +08:00
}
2024-11-02 00:39:02 +08:00
func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.VLESSOutboundOptions) (adapter.Outbound, error) {
2025-01-12 12:45:27 +08:00
outboundDialer, err := dialer.New(ctx, options.DialerOptions, options.ServerIsDomain())
2023-08-08 16:14:03 +08:00
if err != nil {
return nil, err
}
2024-11-02 00:39:02 +08:00
outbound := &Outbound{
2024-11-21 18:10:41 +08:00
Adapter: outbound.NewAdapterWithDialerOptions(C.TypeVLESS, tag, options.Network.Build(), options.DialerOptions),
2024-11-02 00:39:02 +08:00
logger: logger,
2023-08-08 16:14:03 +08:00
dialer: outboundDialer,
2022-09-12 21:59:27 +08:00
serverAddr: options.ServerOptions.Build(),
2026-02-26 18:03:59 +03:00
vision: strings.HasPrefix(options.Flow, "xtls-rprx-vision"),
2022-09-12 21:59:27 +08:00
}
if options.TLS != nil {
outbound.tlsConfig, err = tls.NewClientWithOptions(tls.ClientOptions{
Context: ctx,
Logger: logger,
ServerAddress: options.Server,
Options: common.PtrValueOrDefault(options.TLS),
KTLSCompatible: common.PtrValueOrDefault(options.Transport).Type == "" &&
!common.PtrValueOrDefault(options.Multiplex).Enabled &&
options.Flow == "",
})
2022-09-12 21:59:27 +08:00
if err != nil {
return nil, err
}
2025-08-11 21:18:34 +08:00
outbound.tlsDialer = tls.NewDialer(outboundDialer, outbound.tlsConfig)
2022-09-12 21:59:27 +08:00
}
if options.Transport != nil {
2026-03-10 04:25:01 +03:00
outbound.transport, err = v2ray.NewClientTransport(ctx, logger, outbound.dialer, outbound.serverAddr, common.PtrValueOrDefault(options.Transport), outbound.tlsConfig)
2022-09-12 21:59:27 +08:00
if err != nil {
return nil, E.Cause(err, "create client transport: ", options.Transport.Type)
}
}
2023-02-28 20:27:30 +08:00
if options.PacketEncoding == nil {
2022-09-12 21:59:27 +08:00
outbound.xudp = true
2023-02-28 20:27:30 +08:00
} else {
switch *options.PacketEncoding {
case "":
case "packetaddr":
outbound.packetAddr = true
case "xudp":
outbound.xudp = true
default:
2026-06-19 23:27:23 +08:00
return nil, E.New("unknown packet encoding: ", *options.PacketEncoding)
2023-02-28 20:27:30 +08:00
}
2022-09-12 21:59:27 +08:00
}
muxOpts := common.PtrValueOrDefault(options.Multiplex)
if muxOpts.Enabled {
options.Flow = ""
}
2026-02-26 18:03:59 +03:00
if options.Encryption != "" && options.Encryption != "none" {
encryptionConfig, err := parseClientEncryption(options.Encryption)
if err != nil {
return nil, E.Cause(err, "parse encryption")
}
outbound.encryption = &encryption.ClientInstance{}
if err := outbound.encryption.Init(encryptionConfig.keys, encryptionConfig.xorMode, encryptionConfig.seconds, encryptionConfig.padding); err != nil {
return nil, E.Cause(err, "initialize encryption")
}
logger.Debug("encryption initialized: keys=", len(encryptionConfig.keys), " xorMode=", encryptionConfig.xorMode, " seconds=", encryptionConfig.seconds, " padding=", encryptionConfig.padding)
}
2023-02-27 15:07:15 +08:00
outbound.client, err = vless.NewClient(options.UUID, options.Flow, logger)
2022-09-12 21:59:27 +08:00
if err != nil {
return nil, err
}
2026-02-26 18:03:59 +03:00
outbound.multiplexDialer, err = mux.NewClientWithOptions((*vlessDialer)(outbound), logger, muxOpts)
2023-04-24 09:55:46 +08:00
if err != nil {
return nil, err
}
2022-09-12 21:59:27 +08:00
return outbound, nil
}
2024-11-02 00:39:02 +08:00
func (h *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
2023-04-24 09:55:46 +08:00
if h.multiplexDialer == nil {
switch N.NetworkName(network) {
case N.NetworkTCP:
h.logger.InfoContext(ctx, "outbound connection to ", destination)
case N.NetworkUDP:
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
}
return (*vlessDialer)(h).DialContext(ctx, network, destination)
} else {
switch N.NetworkName(network) {
case N.NetworkTCP:
h.logger.InfoContext(ctx, "outbound multiplex connection to ", destination)
case N.NetworkUDP:
h.logger.InfoContext(ctx, "outbound multiplex packet connection to ", destination)
}
return h.multiplexDialer.DialContext(ctx, network, destination)
}
}
2024-11-02 00:39:02 +08:00
func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
2023-04-24 09:55:46 +08:00
if h.multiplexDialer == nil {
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
return (*vlessDialer)(h).ListenPacket(ctx, destination)
} else {
h.logger.InfoContext(ctx, "outbound multiplex packet connection to ", destination)
return h.multiplexDialer.ListenPacket(ctx, destination)
}
}
2026-07-25 13:05:31 +08:00
func (h *Outbound) MultiplexEnabled() bool {
return h.multiplexDialer != nil
}
2024-11-02 00:39:02 +08:00
func (h *Outbound) InterfaceUpdated() {
2024-07-07 15:45:50 +08:00
if h.transport != nil {
h.transport.Close()
}
2023-04-24 09:55:46 +08:00
if h.multiplexDialer != nil {
h.multiplexDialer.Reset()
}
}
2024-11-02 00:39:02 +08:00
func (h *Outbound) Close() error {
2023-04-24 09:55:46 +08:00
return common.Close(common.PtrOrNil(h.multiplexDialer), h.transport)
}
2024-11-02 00:39:02 +08:00
type vlessDialer Outbound
2023-04-24 09:55:46 +08:00
func (h *vlessDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
2024-10-27 07:45:15 +08:00
ctx, metadata := adapter.ExtendContext(ctx)
2024-11-02 00:39:02 +08:00
metadata.Outbound = h.Tag()
2022-09-12 21:59:27 +08:00
metadata.Destination = destination
var conn net.Conn
2026-02-26 18:03:59 +03:00
var baseConn net.Conn
var hookOnce sync.Once
if h.vision {
ctx = vision.WithHook(ctx, func(tlsConn net.Conn) {
if tlsConn == nil || !isVisionTLSConn(tlsConn) {
return
}
hookOnce.Do(func() {
baseConn = tlsConn
})
})
}
2022-09-12 21:59:27 +08:00
var err error
if h.transport != nil {
conn, err = h.transport.DialContext(ctx)
2026-02-26 18:03:59 +03:00
if err == nil && h.vision {
if baseConn == nil {
if isVisionTLSConn(conn) {
h.logger.Warn("Vision enabled but hook was not called by transport, using fallback")
baseConn = conn
}
}
}
2025-08-11 21:18:34 +08:00
} else if h.tlsDialer != nil {
conn, err = h.tlsDialer.DialTLSContext(ctx, h.serverAddr)
2026-03-10 04:50:32 +03:00
if err == nil && h.vision && baseConn == nil {
baseConn = conn
}
2022-09-12 21:59:27 +08:00
} else {
conn, err = h.dialer.DialContext(ctx, N.NetworkTCP, h.serverAddr)
}
if err != nil {
return nil, err
}
2026-02-26 18:03:59 +03:00
if h.encryption != nil {
conn, err = h.encryption.Handshake(conn)
if err != nil {
return nil, E.Cause(err, "encryption handshake")
}
}
var visionBaseConn net.Conn
2026-02-26 18:03:59 +03:00
var visionCanSplice bool
if h.vision {
conn, visionBaseConn, visionCanSplice, err = h.setupVision(conn, baseConn)
if err != nil {
return nil, err
2026-02-26 18:03:59 +03:00
}
}
2022-09-12 21:59:27 +08:00
switch N.NetworkName(network) {
case N.NetworkTCP:
h.logger.InfoContext(ctx, "outbound connection to ", destination)
2026-02-26 18:03:59 +03:00
if h.vision && visionBaseConn != nil {
return h.client.DialEarlyConnWithOptions(conn, visionBaseConn, destination, visionCanSplice)
}
return h.client.DialEarlyConn(conn, destination)
2022-09-12 21:59:27 +08:00
case N.NetworkUDP:
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
2023-03-03 16:31:07 +08:00
if h.xudp {
return h.client.DialEarlyXUDPPacketConn(conn, destination)
} else if h.packetAddr {
if destination.IsDomain() {
2023-04-02 12:01:19 +08:00
return nil, E.New("packetaddr: domain destination is not supported")
}
2023-03-03 16:31:07 +08:00
packetConn, err := h.client.DialEarlyPacketConn(conn, M.Socksaddr{Fqdn: packetaddr.SeqPacketMagicAddress})
if err != nil {
return nil, err
}
2023-04-14 19:19:27 +08:00
return bufio.NewBindPacketConn(packetaddr.NewConn(packetConn, destination), destination), nil
2023-03-03 16:31:07 +08:00
} else {
return h.client.DialEarlyPacketConn(conn, destination)
}
2022-09-12 21:59:27 +08:00
default:
return nil, E.Extend(N.ErrUnknownNetwork, network)
}
}
func (h *vlessDialer) setupVision(conn net.Conn, baseConn net.Conn) (net.Conn, net.Conn, bool, error) {
isRAWTransport := h.transport == nil
if baseConn != nil && !isVisionTLSConn(baseConn) {
baseConn = nil
}
if baseConn != nil {
return newVisionConnWrapper(conn, baseConn), baseConn, isRAWTransport, nil
}
if h.encryption != nil {
encConn := findEncryptionLayer(conn)
if encConn == nil {
return nil, nil, false, E.New("Vision: failed to find encryption layer")
}
canSplice := isRAWTransport && !h.encryption.IsFullRandomXorMode()
return newVisionConnWrapper(conn, encConn), encConn, canSplice, nil
}
return nil, nil, false, E.New("Vision requires either TLS/Reality or Encryption")
}
2023-04-24 09:55:46 +08:00
func (h *vlessDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
2022-09-12 21:59:27 +08:00
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
2024-10-27 07:45:15 +08:00
ctx, metadata := adapter.ExtendContext(ctx)
2024-11-02 00:39:02 +08:00
metadata.Outbound = h.Tag()
2022-09-12 21:59:27 +08:00
metadata.Destination = destination
var conn net.Conn
var err error
if h.transport != nil {
conn, err = h.transport.DialContext(ctx)
2025-08-11 21:18:34 +08:00
} else if h.tlsDialer != nil {
conn, err = h.tlsDialer.DialTLSContext(ctx, h.serverAddr)
2022-09-12 21:59:27 +08:00
} else {
conn, err = h.dialer.DialContext(ctx, N.NetworkTCP, h.serverAddr)
}
if err != nil {
2023-03-03 16:46:49 +08:00
common.Close(conn)
2022-09-12 21:59:27 +08:00
return nil, err
}
2026-02-26 18:03:59 +03:00
if h.encryption != nil {
conn, err = h.encryption.Handshake(conn)
if err != nil {
common.Close(conn)
return nil, E.Cause(err, "encryption handshake")
}
}
2022-09-12 21:59:27 +08:00
if h.xudp {
return h.client.DialEarlyXUDPPacketConn(conn, destination)
2022-09-12 21:59:27 +08:00
} else if h.packetAddr {
if destination.IsDomain() {
2023-04-02 12:01:19 +08:00
return nil, E.New("packetaddr: domain destination is not supported")
}
conn, err := h.client.DialEarlyPacketConn(conn, M.Socksaddr{Fqdn: packetaddr.SeqPacketMagicAddress})
if err != nil {
return nil, err
}
2023-04-02 12:01:19 +08:00
return packetaddr.NewConn(conn, destination), nil
2022-09-12 21:59:27 +08:00
} else {
return h.client.DialEarlyPacketConn(conn, destination)
2022-09-12 21:59:27 +08:00
}
}
2026-02-26 18:03:59 +03:00
type visionConnWrapper struct {
net.Conn
upstream net.Conn
}
var (
_ N.ReaderWithUpstream = (*visionConnWrapper)(nil)
_ N.WriterWithUpstream = (*visionConnWrapper)(nil)
_ common.WithUpstream = (*visionConnWrapper)(nil)
)
func newVisionConnWrapper(conn net.Conn, upstream net.Conn) net.Conn {
if upstream == nil || conn == nil || conn == upstream {
return conn
}
return &visionConnWrapper{
Conn: conn,
upstream: upstream,
}
}
func (c *visionConnWrapper) Upstream() any {
return c.upstream
}
func (c *visionConnWrapper) ReaderReplaceable() bool {
if replacer, ok := c.Conn.(N.ReaderWithUpstream); ok {
return replacer.ReaderReplaceable()
}
return true
}
func (c *visionConnWrapper) WriterReplaceable() bool {
if replacer, ok := c.Conn.(N.WriterWithUpstream); ok {
return replacer.WriterReplaceable()
}
return true
}
func isVisionTLSConn(conn net.Conn) bool {
if conn == nil {
return false
}
if _, ok := conn.(interface{ ConnectionState() stdtls.ConnectionState }); ok {
return true
}
if _, ok := conn.(interface{ Handshake() error }); ok {
return true
}
return false
}
func findEncryptionLayer(conn net.Conn) net.Conn {
for conn != nil {
if enc, ok := conn.(encryption.EncryptionConn); ok && enc.IsEncryptionLayer() {
return conn
}
if upstream, ok := conn.(common.WithUpstream); ok {
if next := upstream.Upstream(); next != nil {
if nextConn, ok := next.(net.Conn); ok {
conn = nextConn
continue
}
}
}
break
}
return nil
}
type clientEncryptionConfig struct {
keys [][]byte
xorMode uint32
seconds uint32
padding string
}
func parseClientEncryption(raw string) (clientEncryptionConfig, error) {
var cfg clientEncryptionConfig
raw = strings.TrimSpace(raw)
if raw == "" {
return cfg, E.New("empty encryption string")
}
parts := strings.Split(raw, ".")
if len(parts) < 4 {
return cfg, E.New("invalid encryption string: missing components")
}
if parts[0] != "mlkem768x25519plus" {
return cfg, E.New("unsupported encryption prefix: ", parts[0])
}
switch parts[1] {
case "native":
cfg.xorMode = 0
case "xorpub":
cfg.xorMode = 1
case "random":
cfg.xorMode = 2
default:
return cfg, E.New("unknown encryption mode: ", parts[1])
}
switch parts[2] {
case "0rtt":
cfg.seconds = 1
case "1rtt":
cfg.seconds = 0
default:
return cfg, E.New("unsupported encryption RTT value: ", parts[2])
}
paddingPhase := true
var paddingParts []string
for _, segment := range parts[3:] {
segment = strings.TrimSpace(segment)
if segment == "" {
return cfg, E.New("invalid empty segment in encryption string")
}
if data, err := base64.RawURLEncoding.DecodeString(segment); err == nil {
if len(data) == 32 || len(data) == 1184 {
cfg.keys = append(cfg.keys, data)
paddingPhase = false
continue
}
return cfg, E.New("invalid encryption key length: ", len(data))
}
if paddingPhase {
paddingParts = append(paddingParts, segment)
continue
}
return cfg, E.New("invalid encryption key: ", segment)
}
if len(cfg.keys) == 0 {
return cfg, E.New("no valid encryption keys found in encryption string")
}
if len(paddingParts) > 0 {
cfg.padding = strings.Join(paddingParts, ".")
}
return cfg, nil
}