Improve ECH support

This commit is contained in:
世界 2023-08-29 13:43:42 +08:00
parent be61ca64d4
commit 5b343d4c72
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
34 changed files with 434 additions and 84 deletions

View file

@ -10,6 +10,7 @@ import (
"github.com/sagernet/sing-tun" "github.com/sagernet/sing-tun"
"github.com/sagernet/sing/common/control" "github.com/sagernet/sing/common/control"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/service"
mdns "github.com/miekg/dns" mdns "github.com/miekg/dns"
) )
@ -56,18 +57,12 @@ type Router interface {
ResetNetwork() error ResetNetwork() error
} }
type routerContextKey struct{}
func ContextWithRouter(ctx context.Context, router Router) context.Context { func ContextWithRouter(ctx context.Context, router Router) context.Context {
return context.WithValue(ctx, (*routerContextKey)(nil), router) return service.ContextWith(ctx, router)
} }
func RouterFromContext(ctx context.Context) Router { func RouterFromContext(ctx context.Context) Router {
metadata := ctx.Value((*routerContextKey)(nil)) return service.FromContext[Router](ctx)
if metadata == nil {
return nil
}
return metadata.(Router)
} }
type Rule interface { type Rule interface {

2
box.go
View file

@ -19,6 +19,7 @@ import (
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format" F "github.com/sagernet/sing/common/format"
"github.com/sagernet/sing/service"
"github.com/sagernet/sing/service/pause" "github.com/sagernet/sing/service/pause"
) )
@ -47,6 +48,7 @@ func New(options Options) (*Box, error) {
if ctx == nil { if ctx == nil {
ctx = context.Background() ctx = context.Background()
} }
ctx = service.ContextWithDefaultRegistry(ctx)
ctx = pause.ContextWithDefaultManager(ctx) ctx = pause.ContextWithDefaultManager(ctx)
createdAt := time.Now() createdAt := time.Now()
experimentalOptions := common.PtrValueOrDefault(options.Experimental) experimentalOptions := common.PtrValueOrDefault(options.Experimental)

View file

@ -13,29 +13,29 @@ import (
aTLS "github.com/sagernet/sing/common/tls" aTLS "github.com/sagernet/sing/common/tls"
) )
func NewDialerFromOptions(router adapter.Router, dialer N.Dialer, serverAddress string, options option.OutboundTLSOptions) (N.Dialer, error) { func NewDialerFromOptions(ctx context.Context, router adapter.Router, dialer N.Dialer, serverAddress string, options option.OutboundTLSOptions) (N.Dialer, error) {
if !options.Enabled { if !options.Enabled {
return dialer, nil return dialer, nil
} }
config, err := NewClient(router, serverAddress, options) config, err := NewClient(ctx, serverAddress, options)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return NewDialer(dialer, config), nil return NewDialer(dialer, config), nil
} }
func NewClient(router adapter.Router, serverAddress string, options option.OutboundTLSOptions) (Config, error) { func NewClient(ctx context.Context, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
if !options.Enabled { if !options.Enabled {
return nil, nil return nil, nil
} }
if options.ECH != nil && options.ECH.Enabled { if options.ECH != nil && options.ECH.Enabled {
return NewECHClient(router, serverAddress, options) return NewECHClient(ctx, serverAddress, options)
} else if options.Reality != nil && options.Reality.Enabled { } else if options.Reality != nil && options.Reality.Enabled {
return NewRealityClient(router, serverAddress, options) return NewRealityClient(ctx, serverAddress, options)
} else if options.UTLS != nil && options.UTLS.Enabled { } else if options.UTLS != nil && options.UTLS.Enabled {
return NewUTLSClient(router, serverAddress, options) return NewUTLSClient(ctx, serverAddress, options)
} else { } else {
return NewSTDClient(router, serverAddress, options) return NewSTDClient(ctx, serverAddress, options)
} }
} }

View file

@ -7,15 +7,18 @@ import (
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"encoding/base64" "encoding/base64"
"encoding/pem"
"net" "net"
"net/netip" "net/netip"
"os" "os"
"strings"
cftls "github.com/sagernet/cloudflare-tls" cftls "github.com/sagernet/cloudflare-tls"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-dns" "github.com/sagernet/sing-dns"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/ntp"
mDNS "github.com/miekg/dns" mDNS "github.com/miekg/dns"
) )
@ -80,7 +83,7 @@ func (c *echConnWrapper) Upstream() any {
return c.Conn return c.Conn
} }
func NewECHClient(router adapter.Router, serverAddress string, options option.OutboundTLSOptions) (Config, error) { func NewECHClient(ctx context.Context, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
var serverName string var serverName string
if options.ServerName != "" { if options.ServerName != "" {
serverName = options.ServerName serverName = options.ServerName
@ -94,7 +97,7 @@ func NewECHClient(router adapter.Router, serverAddress string, options option.Ou
} }
var tlsConfig cftls.Config var tlsConfig cftls.Config
tlsConfig.Time = router.TimeFunc() tlsConfig.Time = ntp.TimeFuncFromContext(ctx)
if options.DisableSNI { if options.DisableSNI {
tlsConfig.ServerName = "127.0.0.1" tlsConfig.ServerName = "127.0.0.1"
} else { } else {
@ -168,24 +171,24 @@ func NewECHClient(router adapter.Router, serverAddress string, options option.Ou
tlsConfig.ECHEnabled = true tlsConfig.ECHEnabled = true
tlsConfig.PQSignatureSchemesEnabled = options.ECH.PQSignatureSchemesEnabled tlsConfig.PQSignatureSchemesEnabled = options.ECH.PQSignatureSchemesEnabled
tlsConfig.DynamicRecordSizingDisabled = options.ECH.DynamicRecordSizingDisabled tlsConfig.DynamicRecordSizingDisabled = options.ECH.DynamicRecordSizingDisabled
if options.ECH.Config != "" { if len(options.ECH.Config) > 0 {
clientConfigContent, err := base64.StdEncoding.DecodeString(options.ECH.Config) block, rest := pem.Decode([]byte(strings.Join(options.ECH.Config, "\n")))
if err != nil { if block == nil || block.Type != "ECH CONFIGS" || len(rest) > 0 {
return nil, err return nil, E.New("invalid ECH configs pem")
} }
clientConfig, err := cftls.UnmarshalECHConfigs(clientConfigContent) echConfigs, err := cftls.UnmarshalECHConfigs(block.Bytes)
if err != nil { if err != nil {
return nil, err return nil, E.Cause(err, "parse ECH configs")
} }
tlsConfig.ClientECHConfigs = clientConfig tlsConfig.ClientECHConfigs = echConfigs
} else { } else {
tlsConfig.GetClientECHConfigs = fetchECHClientConfig(router) tlsConfig.GetClientECHConfigs = fetchECHClientConfig(ctx)
} }
return &ECHClientConfig{&tlsConfig}, nil return &ECHClientConfig{&tlsConfig}, nil
} }
func fetchECHClientConfig(router adapter.Router) func(ctx context.Context, serverName string) ([]cftls.ECHConfig, error) { func fetchECHClientConfig(ctx context.Context) func(_ context.Context, serverName string) ([]cftls.ECHConfig, error) {
return func(ctx context.Context, serverName string) ([]cftls.ECHConfig, error) { return func(_ context.Context, serverName string) ([]cftls.ECHConfig, error) {
message := &mDNS.Msg{ message := &mDNS.Msg{
MsgHdr: mDNS.MsgHdr{ MsgHdr: mDNS.MsgHdr{
RecursionDesired: true, RecursionDesired: true,
@ -198,7 +201,7 @@ func fetchECHClientConfig(router adapter.Router) func(ctx context.Context, serve
}, },
}, },
} }
response, err := router.Exchange(ctx, message) response, err := adapter.RouterFromContext(ctx).Exchange(ctx, message)
if err != nil { if err != nil {
return nil, err return nil, err
} }

330
common/tls/ech_server.go Normal file
View file

@ -0,0 +1,330 @@
//go:build with_ech
package tls
import (
"context"
"crypto/tls"
"encoding/pem"
"net"
"os"
"strings"
cftls "github.com/sagernet/cloudflare-tls"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/ntp"
"github.com/fsnotify/fsnotify"
)
type echServerConfig struct {
config *cftls.Config
logger log.Logger
certificate []byte
key []byte
certificatePath string
keyPath string
watcher *fsnotify.Watcher
echKeyPath string
echWatcher *fsnotify.Watcher
}
func (c *echServerConfig) ServerName() string {
return c.config.ServerName
}
func (c *echServerConfig) SetServerName(serverName string) {
c.config.ServerName = serverName
}
func (c *echServerConfig) NextProtos() []string {
return c.config.NextProtos
}
func (c *echServerConfig) SetNextProtos(nextProto []string) {
c.config.NextProtos = nextProto
}
func (c *echServerConfig) Config() (*STDConfig, error) {
return nil, E.New("unsupported usage for ECH")
}
func (c *echServerConfig) Client(conn net.Conn) (Conn, error) {
return &echConnWrapper{cftls.Client(conn, c.config)}, nil
}
func (c *echServerConfig) Server(conn net.Conn) (Conn, error) {
return &echConnWrapper{cftls.Server(conn, c.config)}, nil
}
func (c *echServerConfig) Clone() Config {
return &echServerConfig{
config: c.config.Clone(),
}
}
func (c *echServerConfig) Start() error {
if c.certificatePath != "" && c.keyPath != "" {
err := c.startWatcher()
if err != nil {
c.logger.Warn("create fsnotify watcher: ", err)
}
}
if c.echKeyPath != "" {
err := c.startECHWatcher()
if err != nil {
c.logger.Warn("create fsnotify watcher: ", err)
}
}
return nil
}
func (c *echServerConfig) startWatcher() error {
watcher, err := fsnotify.NewWatcher()
if err != nil {
return err
}
if c.certificatePath != "" {
err = watcher.Add(c.certificatePath)
if err != nil {
return err
}
}
if c.keyPath != "" {
err = watcher.Add(c.keyPath)
if err != nil {
return err
}
}
c.watcher = watcher
go c.loopUpdate()
return nil
}
func (c *echServerConfig) loopUpdate() {
for {
select {
case event, ok := <-c.watcher.Events:
if !ok {
return
}
if event.Op&fsnotify.Write != fsnotify.Write {
continue
}
err := c.reloadKeyPair()
if err != nil {
c.logger.Error(E.Cause(err, "reload TLS key pair"))
}
case err, ok := <-c.watcher.Errors:
if !ok {
return
}
c.logger.Error(E.Cause(err, "fsnotify error"))
}
}
}
func (c *echServerConfig) reloadKeyPair() error {
if c.certificatePath != "" {
certificate, err := os.ReadFile(c.certificatePath)
if err != nil {
return E.Cause(err, "reload certificate from ", c.certificatePath)
}
c.certificate = certificate
}
if c.keyPath != "" {
key, err := os.ReadFile(c.keyPath)
if err != nil {
return E.Cause(err, "reload key from ", c.keyPath)
}
c.key = key
}
keyPair, err := cftls.X509KeyPair(c.certificate, c.key)
if err != nil {
return E.Cause(err, "reload key pair")
}
c.config.Certificates = []cftls.Certificate{keyPair}
c.logger.Info("reloaded TLS certificate")
return nil
}
func (c *echServerConfig) startECHWatcher() error {
watcher, err := fsnotify.NewWatcher()
if err != nil {
return err
}
err = watcher.Add(c.echKeyPath)
if err != nil {
return err
}
c.watcher = watcher
go c.loopECHUpdate()
return nil
}
func (c *echServerConfig) loopECHUpdate() {
for {
select {
case event, ok := <-c.echWatcher.Events:
if !ok {
return
}
if event.Op&fsnotify.Write != fsnotify.Write {
continue
}
err := c.reloadECHKey()
if err != nil {
c.logger.Error(E.Cause(err, "reload ECH key"))
}
case err, ok := <-c.watcher.Errors:
if !ok {
return
}
c.logger.Error(E.Cause(err, "fsnotify error"))
}
}
}
func (c *echServerConfig) reloadECHKey() error {
echKeyContent, err := os.ReadFile(c.echKeyPath)
if err != nil {
return err
}
block, rest := pem.Decode(echKeyContent)
if block == nil || block.Type != "ECH KEYS" || len(rest) > 0 {
return E.New("invalid ECH keys pem")
}
echKeys, err := cftls.EXP_UnmarshalECHKeys(block.Bytes)
if err != nil {
return E.Cause(err, "parse ECH keys")
}
echKeySet, err := cftls.EXP_NewECHKeySet(echKeys)
if err != nil {
return E.Cause(err, "create ECH key set")
}
c.config.ServerECHProvider = echKeySet
c.logger.Info("reloaded ECH keys")
return nil
}
func (c *echServerConfig) Close() error {
var err error
if c.watcher != nil {
err = E.Append(err, c.watcher.Close(), func(err error) error {
return E.Cause(err, "close certificate watcher")
})
}
if c.echWatcher != nil {
err = E.Append(err, c.echWatcher.Close(), func(err error) error {
return E.Cause(err, "close ECH key watcher")
})
}
return err
}
func NewECHServer(ctx context.Context, logger log.Logger, options option.InboundTLSOptions) (ServerConfig, error) {
if !options.Enabled {
return nil, nil
}
var tlsConfig cftls.Config
if options.ACME != nil && len(options.ACME.Domain) > 0 {
return nil, E.New("acme is unavailable in ech")
}
tlsConfig.Time = ntp.TimeFuncFromContext(ctx)
if options.ServerName != "" {
tlsConfig.ServerName = options.ServerName
}
if len(options.ALPN) > 0 {
tlsConfig.NextProtos = append(options.ALPN, tlsConfig.NextProtos...)
}
if options.MinVersion != "" {
minVersion, err := ParseTLSVersion(options.MinVersion)
if err != nil {
return nil, E.Cause(err, "parse min_version")
}
tlsConfig.MinVersion = minVersion
}
if options.MaxVersion != "" {
maxVersion, err := ParseTLSVersion(options.MaxVersion)
if err != nil {
return nil, E.Cause(err, "parse max_version")
}
tlsConfig.MaxVersion = maxVersion
}
if options.CipherSuites != nil {
find:
for _, cipherSuite := range options.CipherSuites {
for _, tlsCipherSuite := range tls.CipherSuites() {
if cipherSuite == tlsCipherSuite.Name {
tlsConfig.CipherSuites = append(tlsConfig.CipherSuites, tlsCipherSuite.ID)
continue find
}
}
return nil, E.New("unknown cipher_suite: ", cipherSuite)
}
}
var certificate []byte
var key []byte
if len(options.Certificate) > 0 {
certificate = []byte(strings.Join(options.Certificate, "\n"))
} else if options.CertificatePath != "" {
content, err := os.ReadFile(options.CertificatePath)
if err != nil {
return nil, E.Cause(err, "read certificate")
}
certificate = content
}
if len(options.Key) > 0 {
key = []byte(strings.Join(options.Key, ""))
} else if options.KeyPath != "" {
content, err := os.ReadFile(options.KeyPath)
if err != nil {
return nil, E.Cause(err, "read key")
}
key = content
}
if certificate == nil {
return nil, E.New("missing certificate")
} else if key == nil {
return nil, E.New("missing key")
}
keyPair, err := cftls.X509KeyPair(certificate, key)
if err != nil {
return nil, E.Cause(err, "parse x509 key pair")
}
tlsConfig.Certificates = []cftls.Certificate{keyPair}
block, rest := pem.Decode([]byte(strings.Join(options.ECH.Key, "\n")))
if block == nil || block.Type != "ECH KEYS" || len(rest) > 0 {
return nil, E.New("invalid ECH keys pem")
}
echKeys, err := cftls.EXP_UnmarshalECHKeys(block.Bytes)
if err != nil {
return nil, E.Cause(err, "parse ECH keys")
}
echKeySet, err := cftls.EXP_NewECHKeySet(echKeys)
if err != nil {
return nil, E.Cause(err, "create ECH key set")
}
tlsConfig.ECHEnabled = true
tlsConfig.PQSignatureSchemesEnabled = options.ECH.PQSignatureSchemesEnabled
tlsConfig.DynamicRecordSizingDisabled = options.ECH.DynamicRecordSizingDisabled
tlsConfig.ServerECHProvider = echKeySet
return &echServerConfig{
config: &tlsConfig,
logger: logger,
certificate: certificate,
key: key,
certificatePath: options.CertificatePath,
keyPath: options.KeyPath,
echKeyPath: options.ECH.KeyPath,
}, nil
}

View file

@ -3,11 +3,17 @@
package tls package tls
import ( import (
"github.com/sagernet/sing-box/adapter" "context"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
) )
func NewECHClient(router adapter.Router, serverAddress string, options option.OutboundTLSOptions) (Config, error) { func NewECHServer(ctx context.Context, logger log.Logger, options option.InboundTLSOptions) (ServerConfig, error) {
return nil, E.New(`ECH is not included in this build, rebuild with -tags with_ech`)
}
func NewECHClient(ctx context.Context, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
return nil, E.New(`ECH is not included in this build, rebuild with -tags with_ech`) return nil, E.New(`ECH is not included in this build, rebuild with -tags with_ech`)
} }

View file

@ -26,7 +26,6 @@ import (
"time" "time"
"unsafe" "unsafe"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common/debug" "github.com/sagernet/sing/common/debug"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
@ -45,12 +44,12 @@ type RealityClientConfig struct {
shortID [8]byte shortID [8]byte
} }
func NewRealityClient(router adapter.Router, serverAddress string, options option.OutboundTLSOptions) (*RealityClientConfig, error) { func NewRealityClient(ctx context.Context, serverAddress string, options option.OutboundTLSOptions) (*RealityClientConfig, error) {
if options.UTLS == nil || !options.UTLS.Enabled { if options.UTLS == nil || !options.UTLS.Enabled {
return nil, E.New("uTLS is required by reality client") return nil, E.New("uTLS is required by reality client")
} }
uClient, err := NewUTLSClient(router, serverAddress, options) uClient, err := NewUTLSClient(ctx, serverAddress, options)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -19,6 +19,7 @@ import (
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/common/ntp"
) )
var _ ServerConfigCompat = (*RealityServerConfig)(nil) var _ ServerConfigCompat = (*RealityServerConfig)(nil)
@ -27,13 +28,13 @@ type RealityServerConfig struct {
config *reality.Config config *reality.Config
} }
func NewRealityServer(ctx context.Context, router adapter.Router, logger log.Logger, options option.InboundTLSOptions) (*RealityServerConfig, error) { func NewRealityServer(ctx context.Context, logger log.Logger, options option.InboundTLSOptions) (*RealityServerConfig, error) {
var tlsConfig reality.Config var tlsConfig reality.Config
if options.ACME != nil && len(options.ACME.Domain) > 0 { if options.ACME != nil && len(options.ACME.Domain) > 0 {
return nil, E.New("acme is unavailable in reality") return nil, E.New("acme is unavailable in reality")
} }
tlsConfig.Time = router.TimeFunc() tlsConfig.Time = ntp.TimeFuncFromContext(ctx)
if options.ServerName != "" { if options.ServerName != "" {
tlsConfig.ServerName = options.ServerName tlsConfig.ServerName = options.ServerName
} }
@ -101,7 +102,7 @@ func NewRealityServer(ctx context.Context, router adapter.Router, logger log.Log
tlsConfig.ShortIds[shortID] = true tlsConfig.ShortIds[shortID] = true
} }
handshakeDialer, err := dialer.New(router, options.Reality.Handshake.DialerOptions) handshakeDialer, err := dialer.New(adapter.RouterFromContext(ctx), options.Reality.Handshake.DialerOptions)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -5,12 +5,11 @@ package tls
import ( import (
"context" "context"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
) )
func NewRealityServer(ctx context.Context, router adapter.Router, logger log.Logger, options option.InboundTLSOptions) (ServerConfig, error) { func NewRealityServer(ctx context.Context, logger log.Logger, options option.InboundTLSOptions) (ServerConfig, error) {
return nil, E.New(`reality server is not included in this build, rebuild with -tags with_reality_server`) return nil, E.New(`reality server is not included in this build, rebuild with -tags with_reality_server`)
} }

View file

@ -4,21 +4,22 @@ import (
"context" "context"
"net" "net"
"github.com/sagernet/sing-box/adapter"
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
aTLS "github.com/sagernet/sing/common/tls" aTLS "github.com/sagernet/sing/common/tls"
) )
func NewServer(ctx context.Context, router adapter.Router, logger log.Logger, options option.InboundTLSOptions) (ServerConfig, error) { func NewServer(ctx context.Context, logger log.Logger, options option.InboundTLSOptions) (ServerConfig, error) {
if !options.Enabled { if !options.Enabled {
return nil, nil return nil, nil
} }
if options.Reality != nil && options.Reality.Enabled { if options.ECH != nil && options.ECH.Enabled {
return NewRealityServer(ctx, router, logger, options) return NewECHServer(ctx, logger, options)
} else if options.Reality != nil && options.Reality.Enabled {
return NewRealityServer(ctx, logger, options)
} else { } else {
return NewSTDServer(ctx, router, logger, options) return NewSTDServer(ctx, logger, options)
} }
} }

View file

@ -1,15 +1,16 @@
package tls package tls
import ( import (
"context"
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"net" "net"
"net/netip" "net/netip"
"os" "os"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/ntp"
) )
type STDClientConfig struct { type STDClientConfig struct {
@ -44,7 +45,7 @@ func (s *STDClientConfig) Clone() Config {
return &STDClientConfig{s.config.Clone()} return &STDClientConfig{s.config.Clone()}
} }
func NewSTDClient(router adapter.Router, serverAddress string, options option.OutboundTLSOptions) (Config, error) { func NewSTDClient(ctx context.Context, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
var serverName string var serverName string
if options.ServerName != "" { if options.ServerName != "" {
serverName = options.ServerName serverName = options.ServerName
@ -58,7 +59,7 @@ func NewSTDClient(router adapter.Router, serverAddress string, options option.Ou
} }
var tlsConfig tls.Config var tlsConfig tls.Config
tlsConfig.Time = router.TimeFunc() tlsConfig.Time = ntp.TimeFuncFromContext(ctx)
if options.DisableSNI { if options.DisableSNI {
tlsConfig.ServerName = "127.0.0.1" tlsConfig.ServerName = "127.0.0.1"
} else { } else {

View file

@ -11,6 +11,7 @@ import (
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/ntp"
"github.com/fsnotify/fsnotify" "github.com/fsnotify/fsnotify"
) )
@ -156,7 +157,7 @@ func (c *STDServerConfig) Close() error {
return nil return nil
} }
func NewSTDServer(ctx context.Context, router adapter.Router, logger log.Logger, options option.InboundTLSOptions) (ServerConfig, error) { func NewSTDServer(ctx context.Context, logger log.Logger, options option.InboundTLSOptions) (ServerConfig, error) {
if !options.Enabled { if !options.Enabled {
return nil, nil return nil, nil
} }
@ -175,7 +176,7 @@ func NewSTDServer(ctx context.Context, router adapter.Router, logger log.Logger,
} else { } else {
tlsConfig = &tls.Config{} tlsConfig = &tls.Config{}
} }
tlsConfig.Time = router.TimeFunc() tlsConfig.Time = ntp.TimeFuncFromContext(ctx)
if options.ServerName != "" { if options.ServerName != "" {
tlsConfig.ServerName = options.ServerName tlsConfig.ServerName = options.ServerName
} }
@ -231,7 +232,7 @@ func NewSTDServer(ctx context.Context, router adapter.Router, logger log.Logger,
} }
if certificate == nil && key == nil && options.Insecure { if certificate == nil && key == nil && options.Insecure {
tlsConfig.GetCertificate = func(info *tls.ClientHelloInfo) (*tls.Certificate, error) { tlsConfig.GetCertificate = func(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
return GenerateKeyPair(router.TimeFunc(), info.ServerName) return GenerateKeyPair(ntp.TimeFuncFromContext(ctx), info.ServerName)
} }
} else { } else {
if certificate == nil { if certificate == nil {

View file

@ -11,9 +11,9 @@ import (
"net/netip" "net/netip"
"os" "os"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/ntp"
utls "github.com/sagernet/utls" utls "github.com/sagernet/utls"
"golang.org/x/net/http2" "golang.org/x/net/http2"
@ -113,7 +113,7 @@ func (c *utlsALPNWrapper) HandshakeContext(ctx context.Context) error {
return c.UConn.HandshakeContext(ctx) return c.UConn.HandshakeContext(ctx)
} }
func NewUTLSClient(router adapter.Router, serverAddress string, options option.OutboundTLSOptions) (*UTLSClientConfig, error) { func NewUTLSClient(ctx context.Context, serverAddress string, options option.OutboundTLSOptions) (*UTLSClientConfig, error) {
var serverName string var serverName string
if options.ServerName != "" { if options.ServerName != "" {
serverName = options.ServerName serverName = options.ServerName
@ -127,7 +127,7 @@ func NewUTLSClient(router adapter.Router, serverAddress string, options option.O
} }
var tlsConfig utls.Config var tlsConfig utls.Config
tlsConfig.Time = router.TimeFunc() tlsConfig.Time = ntp.TimeFuncFromContext(ctx)
if options.DisableSNI { if options.DisableSNI {
tlsConfig.ServerName = "127.0.0.1" tlsConfig.ServerName = "127.0.0.1"
} else { } else {

View file

@ -3,15 +3,16 @@
package tls package tls
import ( import (
"github.com/sagernet/sing-box/adapter" "context"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
) )
func NewUTLSClient(router adapter.Router, serverAddress string, options option.OutboundTLSOptions) (Config, error) { func NewUTLSClient(ctx context.Context, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
return nil, E.New(`uTLS is not included in this build, rebuild with -tags with_utls`) return nil, E.New(`uTLS is not included in this build, rebuild with -tags with_utls`)
} }
func NewRealityClient(router adapter.Router, serverAddress string, options option.OutboundTLSOptions) (Config, error) { func NewRealityClient(ctx context.Context, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
return nil, E.New(`uTLS, which is required by reality client is not included in this build, rebuild with -tags with_utls`) return nil, E.New(`uTLS, which is required by reality client is not included in this build, rebuild with -tags with_utls`)
} }

View file

@ -44,7 +44,7 @@ func NewHTTP(ctx context.Context, router adapter.Router, logger log.ContextLogge
authenticator: auth.NewAuthenticator(options.Users), authenticator: auth.NewAuthenticator(options.Users),
} }
if options.TLS != nil { if options.TLS != nil {
tlsConfig, err := tls.NewServer(ctx, router, logger, common.PtrValueOrDefault(options.TLS)) tlsConfig, err := tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS))
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -126,7 +126,7 @@ func NewHysteria(ctx context.Context, router adapter.Router, logger log.ContextL
if len(options.TLS.ALPN) == 0 { if len(options.TLS.ALPN) == 0 {
options.TLS.ALPN = []string{hysteria.DefaultALPN} options.TLS.ALPN = []string{hysteria.DefaultALPN}
} }
tlsConfig, err := tls.NewServer(ctx, router, logger, common.PtrValueOrDefault(options.TLS)) tlsConfig, err := tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS))
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -60,7 +60,7 @@ func NewNaive(ctx context.Context, router adapter.Router, logger log.ContextLogg
return nil, E.New("missing users") return nil, E.New("missing users")
} }
if options.TLS != nil { if options.TLS != nil {
tlsConfig, err := tls.NewServer(ctx, router, logger, common.PtrValueOrDefault(options.TLS)) tlsConfig, err := tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS))
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -49,7 +49,7 @@ func NewTrojan(ctx context.Context, router adapter.Router, logger log.ContextLog
users: options.Users, users: options.Users,
} }
if options.TLS != nil { if options.TLS != nil {
tlsConfig, err := tls.NewServer(ctx, router, logger, common.PtrValueOrDefault(options.TLS)) tlsConfig, err := tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS))
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -34,7 +34,7 @@ func NewTUIC(ctx context.Context, router adapter.Router, logger log.ContextLogge
if options.TLS == nil || !options.TLS.Enabled { if options.TLS == nil || !options.TLS.Enabled {
return nil, C.ErrTLSRequired return nil, C.ErrTLSRequired
} }
tlsConfig, err := tls.NewServer(ctx, router, logger, common.PtrValueOrDefault(options.TLS)) tlsConfig, err := tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -115,6 +115,7 @@ func (h *TUIC) Start() error {
func (h *TUIC) Close() error { func (h *TUIC) Close() error {
return common.Close( return common.Close(
&h.myInboundAdapter, &h.myInboundAdapter,
h.tlsConfig,
common.PtrOrNil(h.server), common.PtrOrNil(h.server),
) )
} }

View file

@ -61,7 +61,7 @@ func NewVLESS(ctx context.Context, router adapter.Router, logger log.ContextLogg
inbound.service = service inbound.service = service
var err error var err error
if options.TLS != nil { if options.TLS != nil {
inbound.tlsConfig, err = tls.NewServer(ctx, router, logger, common.PtrValueOrDefault(options.TLS)) inbound.tlsConfig, err = tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS))
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -69,7 +69,7 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg
return nil, err return nil, err
} }
if options.TLS != nil { if options.TLS != nil {
inbound.tlsConfig, err = tls.NewServer(ctx, router, logger, common.PtrValueOrDefault(options.TLS)) inbound.tlsConfig, err = tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS))
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -8,11 +8,12 @@ type InboundTLSOptions struct {
MinVersion string `json:"min_version,omitempty"` MinVersion string `json:"min_version,omitempty"`
MaxVersion string `json:"max_version,omitempty"` MaxVersion string `json:"max_version,omitempty"`
CipherSuites Listable[string] `json:"cipher_suites,omitempty"` CipherSuites Listable[string] `json:"cipher_suites,omitempty"`
Certificate string `json:"certificate,omitempty"` Certificate Listable[string] `json:"certificate,omitempty"`
CertificatePath string `json:"certificate_path,omitempty"` CertificatePath string `json:"certificate_path,omitempty"`
Key string `json:"key,omitempty"` Key Listable[string] `json:"key,omitempty"`
KeyPath string `json:"key_path,omitempty"` KeyPath string `json:"key_path,omitempty"`
ACME *InboundACMEOptions `json:"acme,omitempty"` ACME *InboundACMEOptions `json:"acme,omitempty"`
ECH *InboundECHOptions `json:"ech,omitempty"`
Reality *InboundRealityOptions `json:"reality,omitempty"` Reality *InboundRealityOptions `json:"reality,omitempty"`
} }
@ -45,11 +46,20 @@ type InboundRealityHandshakeOptions struct {
DialerOptions DialerOptions
} }
type InboundECHOptions struct {
Enabled bool `json:"enabled,omitempty"`
PQSignatureSchemesEnabled bool `json:"pq_signature_schemes_enabled,omitempty"`
DynamicRecordSizingDisabled bool `json:"dynamic_record_sizing_disabled,omitempty"`
Key Listable[string] `json:"ech_keys,omitempty"`
KeyPath string `json:"ech_keys_path,omitempty"`
}
type OutboundECHOptions struct { type OutboundECHOptions struct {
Enabled bool `json:"enabled,omitempty"` Enabled bool `json:"enabled,omitempty"`
PQSignatureSchemesEnabled bool `json:"pq_signature_schemes_enabled,omitempty"` PQSignatureSchemesEnabled bool `json:"pq_signature_schemes_enabled,omitempty"`
DynamicRecordSizingDisabled bool `json:"dynamic_record_sizing_disabled,omitempty"` DynamicRecordSizingDisabled bool `json:"dynamic_record_sizing_disabled,omitempty"`
Config string `json:"config,omitempty"` Config Listable[string] `json:"config,omitempty"`
ConfigPath string `json:"config_path,omitempty"`
} }
type OutboundUTLSOptions struct { type OutboundUTLSOptions struct {

View file

@ -30,7 +30,7 @@ func New(ctx context.Context, router adapter.Router, logger log.ContextLogger, t
case C.TypeSOCKS: case C.TypeSOCKS:
return NewSocks(router, logger, tag, options.SocksOptions) return NewSocks(router, logger, tag, options.SocksOptions)
case C.TypeHTTP: case C.TypeHTTP:
return NewHTTP(router, logger, tag, options.HTTPOptions) return NewHTTP(ctx, router, logger, tag, options.HTTPOptions)
case C.TypeShadowsocks: case C.TypeShadowsocks:
return NewShadowsocks(ctx, router, logger, tag, options.ShadowsocksOptions) return NewShadowsocks(ctx, router, logger, tag, options.ShadowsocksOptions)
case C.TypeVMess: case C.TypeVMess:

View file

@ -25,12 +25,12 @@ type HTTP struct {
client *sHTTP.Client client *sHTTP.Client
} }
func NewHTTP(router adapter.Router, logger log.ContextLogger, tag string, options option.HTTPOutboundOptions) (*HTTP, error) { func NewHTTP(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.HTTPOutboundOptions) (*HTTP, error) {
outboundDialer, err := dialer.New(router, options.DialerOptions) outboundDialer, err := dialer.New(router, options.DialerOptions)
if err != nil { if err != nil {
return nil, err return nil, err
} }
detour, err := tls.NewDialerFromOptions(router, outboundDialer, options.Server, common.PtrValueOrDefault(options.TLS)) detour, err := tls.NewDialerFromOptions(ctx, router, outboundDialer, options.Server, common.PtrValueOrDefault(options.TLS))
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -52,7 +52,7 @@ func NewHysteria(ctx context.Context, router adapter.Router, logger log.ContextL
if options.TLS == nil || !options.TLS.Enabled { if options.TLS == nil || !options.TLS.Enabled {
return nil, C.ErrTLSRequired return nil, C.ErrTLSRequired
} }
abstractTLSConfig, err := tls.NewClient(router, options.Server, common.PtrValueOrDefault(options.TLS)) abstractTLSConfig, err := tls.NewClient(ctx, options.Server, common.PtrValueOrDefault(options.TLS))
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -57,7 +57,7 @@ func NewShadowsocks(ctx context.Context, router adapter.Router, logger log.Conte
serverAddr: options.ServerOptions.Build(), serverAddr: options.ServerOptions.Build(),
} }
if options.Plugin != "" { if options.Plugin != "" {
outbound.plugin, err = sip003.CreatePlugin(options.Plugin, options.PluginOptions, router, outbound.dialer, outbound.serverAddr) outbound.plugin, err = sip003.CreatePlugin(ctx, options.Plugin, options.PluginOptions, router, outbound.dialer, outbound.serverAddr)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -47,7 +47,7 @@ func NewShadowTLS(ctx context.Context, router adapter.Router, logger log.Context
options.TLS.MinVersion = "1.2" options.TLS.MinVersion = "1.2"
options.TLS.MaxVersion = "1.2" options.TLS.MaxVersion = "1.2"
} }
tlsConfig, err := tls.NewClient(router, options.Server, common.PtrValueOrDefault(options.TLS)) tlsConfig, err := tls.NewClient(ctx, options.Server, common.PtrValueOrDefault(options.TLS))
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -51,7 +51,7 @@ func NewTrojan(ctx context.Context, router adapter.Router, logger log.ContextLog
key: trojan.Key(options.Password), key: trojan.Key(options.Password),
} }
if options.TLS != nil { if options.TLS != nil {
outbound.tlsConfig, err = tls.NewClient(router, options.Server, common.PtrValueOrDefault(options.TLS)) outbound.tlsConfig, err = tls.NewClient(ctx, options.Server, common.PtrValueOrDefault(options.TLS))
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -41,7 +41,7 @@ func NewTUIC(ctx context.Context, router adapter.Router, logger log.ContextLogge
if options.TLS == nil || !options.TLS.Enabled { if options.TLS == nil || !options.TLS.Enabled {
return nil, C.ErrTLSRequired return nil, C.ErrTLSRequired
} }
abstractTLSConfig, err := tls.NewClient(router, options.Server, common.PtrValueOrDefault(options.TLS)) abstractTLSConfig, err := tls.NewClient(ctx, options.Server, common.PtrValueOrDefault(options.TLS))
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -53,7 +53,7 @@ func NewVLESS(ctx context.Context, router adapter.Router, logger log.ContextLogg
serverAddr: options.ServerOptions.Build(), serverAddr: options.ServerOptions.Build(),
} }
if options.TLS != nil { if options.TLS != nil {
outbound.tlsConfig, err = tls.NewClient(router, options.Server, common.PtrValueOrDefault(options.TLS)) outbound.tlsConfig, err = tls.NewClient(ctx, options.Server, common.PtrValueOrDefault(options.TLS))
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -52,7 +52,7 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg
serverAddr: options.ServerOptions.Build(), serverAddr: options.ServerOptions.Build(),
} }
if options.TLS != nil { if options.TLS != nil {
outbound.tlsConfig, err = tls.NewClient(router, options.Server, common.PtrValueOrDefault(options.TLS)) outbound.tlsConfig, err = tls.NewClient(ctx, options.Server, common.PtrValueOrDefault(options.TLS))
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -18,7 +18,7 @@ func init() {
RegisterPlugin("obfs-local", newObfsLocal) RegisterPlugin("obfs-local", newObfsLocal)
} }
func newObfsLocal(pluginOpts Args, router adapter.Router, dialer N.Dialer, serverAddr M.Socksaddr) (Plugin, error) { func newObfsLocal(ctx context.Context, pluginOpts Args, router adapter.Router, dialer N.Dialer, serverAddr M.Socksaddr) (Plugin, error) {
plugin := &ObfsLocal{ plugin := &ObfsLocal{
dialer: dialer, dialer: dialer,
serverAddr: serverAddr, serverAddr: serverAddr,

View file

@ -10,7 +10,7 @@ import (
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
) )
type PluginConstructor func(pluginArgs Args, router adapter.Router, dialer N.Dialer, serverAddr M.Socksaddr) (Plugin, error) type PluginConstructor func(ctx context.Context, pluginArgs Args, router adapter.Router, dialer N.Dialer, serverAddr M.Socksaddr) (Plugin, error)
type Plugin interface { type Plugin interface {
DialContext(ctx context.Context) (net.Conn, error) DialContext(ctx context.Context) (net.Conn, error)
@ -25,7 +25,7 @@ func RegisterPlugin(name string, constructor PluginConstructor) {
plugins[name] = constructor plugins[name] = constructor
} }
func CreatePlugin(name string, pluginArgs string, router adapter.Router, dialer N.Dialer, serverAddr M.Socksaddr) (Plugin, error) { func CreatePlugin(ctx context.Context, name string, pluginArgs string, router adapter.Router, dialer N.Dialer, serverAddr M.Socksaddr) (Plugin, error) {
pluginOptions, err := ParsePluginOptions(pluginArgs) pluginOptions, err := ParsePluginOptions(pluginArgs)
if err != nil { if err != nil {
return nil, E.Cause(err, "parse plugin_opts") return nil, E.Cause(err, "parse plugin_opts")
@ -34,5 +34,5 @@ func CreatePlugin(name string, pluginArgs string, router adapter.Router, dialer
if !loaded { if !loaded {
return nil, E.New("plugin not found: ", name) return nil, E.New("plugin not found: ", name)
} }
return constructor(pluginOptions, router, dialer, serverAddr) return constructor(ctx, pluginOptions, router, dialer, serverAddr)
} }

View file

@ -20,7 +20,7 @@ func init() {
RegisterPlugin("v2ray-plugin", newV2RayPlugin) RegisterPlugin("v2ray-plugin", newV2RayPlugin)
} }
func newV2RayPlugin(pluginOpts Args, router adapter.Router, dialer N.Dialer, serverAddr M.Socksaddr) (Plugin, error) { func newV2RayPlugin(ctx context.Context, pluginOpts Args, router adapter.Router, dialer N.Dialer, serverAddr M.Socksaddr) (Plugin, error) {
var tlsOptions option.OutboundTLSOptions var tlsOptions option.OutboundTLSOptions
if _, loaded := pluginOpts.Get("tls"); loaded { if _, loaded := pluginOpts.Get("tls"); loaded {
tlsOptions.Enabled = true tlsOptions.Enabled = true
@ -54,7 +54,7 @@ func newV2RayPlugin(pluginOpts Args, router adapter.Router, dialer N.Dialer, ser
var tlsClient tls.Config var tlsClient tls.Config
var err error var err error
if tlsOptions.Enabled { if tlsOptions.Enabled {
tlsClient, err = tls.NewClient(router, serverAddr.AddrString(), tlsOptions) tlsClient, err = tls.NewClient(ctx, serverAddr.AddrString(), tlsOptions)
if err != nil { if err != nil {
return nil, err return nil, err
} }