mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-04-12 01:57:18 +10:00
Compare commits
4 Commits
v1.1-beta3
...
v1.0.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a37cab48d2 | ||
|
|
c586c8f361 | ||
|
|
e68fa3e12d | ||
|
|
7f5b9e0e3b |
@@ -7,10 +7,6 @@ linters:
|
||||
- staticcheck
|
||||
- paralleltest
|
||||
|
||||
run:
|
||||
skip-dirs:
|
||||
- transport/cloudflaretls
|
||||
|
||||
linters-settings:
|
||||
# gci:
|
||||
# sections:
|
||||
|
||||
2
Makefile
2
Makefile
@@ -64,7 +64,7 @@ test:
|
||||
@go test -v . && \
|
||||
pushd test && \
|
||||
go mod tidy && \
|
||||
go test -v -tags with_quic,with_wireguard,with_grpc,with_ech,with_utls . && \
|
||||
go test -v -tags with_quic,with_wireguard,with_grpc . && \
|
||||
popd
|
||||
|
||||
clean:
|
||||
|
||||
@@ -9,22 +9,18 @@ import (
|
||||
|
||||
type ClashServer interface {
|
||||
Service
|
||||
Mode() string
|
||||
StoreSelected() bool
|
||||
CacheFile() ClashCacheFile
|
||||
RoutedConnection(ctx context.Context, conn net.Conn, metadata InboundContext, matchedRule Rule) (net.Conn, Tracker)
|
||||
RoutedPacketConnection(ctx context.Context, conn N.PacketConn, metadata InboundContext, matchedRule Rule) (N.PacketConn, Tracker)
|
||||
}
|
||||
|
||||
type ClashCacheFile interface {
|
||||
LoadSelected(group string) string
|
||||
StoreSelected(group string, selected string) error
|
||||
TrafficController
|
||||
}
|
||||
|
||||
type Tracker interface {
|
||||
Leave()
|
||||
}
|
||||
|
||||
type TrafficController interface {
|
||||
RoutedConnection(ctx context.Context, conn net.Conn, metadata InboundContext, matchedRule Rule) (net.Conn, Tracker)
|
||||
RoutedPacketConnection(ctx context.Context, conn N.PacketConn, metadata InboundContext, matchedRule Rule) (N.PacketConn, Tracker)
|
||||
}
|
||||
|
||||
type OutboundGroup interface {
|
||||
Now() string
|
||||
All() []string
|
||||
|
||||
@@ -39,9 +39,7 @@ type Router interface {
|
||||
InterfaceMonitor() tun.DefaultInterfaceMonitor
|
||||
PackageManager() tun.PackageManager
|
||||
Rules() []Rule
|
||||
|
||||
ClashServer() ClashServer
|
||||
SetClashServer(controller ClashServer)
|
||||
SetTrafficController(controller TrafficController)
|
||||
}
|
||||
|
||||
type Rule interface {
|
||||
|
||||
2
box.go
2
box.go
@@ -154,7 +154,7 @@ func New(ctx context.Context, options option.Options) (*Box, error) {
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "create clash api server")
|
||||
}
|
||||
router.SetClashServer(clashServer)
|
||||
router.SetTrafficController(clashServer)
|
||||
}
|
||||
return &Box{
|
||||
router: router,
|
||||
|
||||
@@ -112,10 +112,6 @@ func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDia
|
||||
if options.TCPFastOpen {
|
||||
warnTFOOnUnsupportedPlatform.Check()
|
||||
}
|
||||
if !options.UDPFragment {
|
||||
dialer.Control = control.Append(dialer.Control, control.DisableUDPFragment())
|
||||
listener.Control = control.Append(listener.Control, control.DisableUDPFragment())
|
||||
}
|
||||
var bindUDPAddr string
|
||||
udpDialer := dialer
|
||||
var bindAddress netip.Addr
|
||||
|
||||
@@ -1,26 +1,34 @@
|
||||
package tls
|
||||
package dialer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
type stdClientConfig struct {
|
||||
type TLSDialer struct {
|
||||
dialer N.Dialer
|
||||
config *tls.Config
|
||||
}
|
||||
|
||||
func newStdClient(serverAddress string, options option.OutboundTLSOptions) (Config, error) {
|
||||
func TLSConfig(serverAddress string, options option.OutboundTLSOptions) (*tls.Config, error) {
|
||||
if !options.Enabled {
|
||||
return nil, nil
|
||||
}
|
||||
var serverName string
|
||||
if options.ServerName != "" {
|
||||
serverName = options.ServerName
|
||||
} else if serverAddress != "" {
|
||||
if _, err := netip.ParseAddr(serverName); err != nil {
|
||||
if _, err := netip.ParseAddr(serverName); err == nil {
|
||||
serverName = serverAddress
|
||||
}
|
||||
}
|
||||
@@ -54,14 +62,14 @@ func newStdClient(serverAddress string, options option.OutboundTLSOptions) (Conf
|
||||
tlsConfig.NextProtos = options.ALPN
|
||||
}
|
||||
if options.MinVersion != "" {
|
||||
minVersion, err := ParseTLSVersion(options.MinVersion)
|
||||
minVersion, err := option.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)
|
||||
maxVersion, err := option.ParseTLSVersion(options.MaxVersion)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse max_version")
|
||||
}
|
||||
@@ -96,21 +104,42 @@ func newStdClient(serverAddress string, options option.OutboundTLSOptions) (Conf
|
||||
}
|
||||
tlsConfig.RootCAs = certPool
|
||||
}
|
||||
return &stdClientConfig{&tlsConfig}, nil
|
||||
return &tlsConfig, nil
|
||||
}
|
||||
|
||||
func (s *stdClientConfig) NextProtos() []string {
|
||||
return s.config.NextProtos
|
||||
func NewTLS(dialer N.Dialer, serverAddress string, options option.OutboundTLSOptions) (N.Dialer, error) {
|
||||
if !options.Enabled {
|
||||
return dialer, nil
|
||||
}
|
||||
tlsConfig, err := TLSConfig(serverAddress, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &TLSDialer{
|
||||
dialer: dialer,
|
||||
config: tlsConfig,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *stdClientConfig) SetNextProtos(nextProto []string) {
|
||||
s.config.NextProtos = nextProto
|
||||
func (d *TLSDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||
if network != N.NetworkTCP {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
conn, err := d.dialer.DialContext(ctx, network, destination)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return TLSClient(ctx, conn, d.config)
|
||||
}
|
||||
|
||||
func (s *stdClientConfig) Config() (*STDConfig, error) {
|
||||
return s.config, nil
|
||||
func (d *TLSDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
|
||||
func (s *stdClientConfig) Client(conn net.Conn) Conn {
|
||||
return tls.Client(conn, s.config)
|
||||
func TLSClient(ctx context.Context, conn net.Conn, tlsConfig *tls.Config) (*tls.Conn, error) {
|
||||
tlsConn := tls.Client(conn, tlsConfig)
|
||||
ctx, cancel := context.WithTimeout(ctx, C.TCPTimeout)
|
||||
defer cancel()
|
||||
err := tlsConn.HandshakeContext(ctx)
|
||||
return tlsConn, err
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
package json
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
)
|
||||
|
||||
// kanged from v2ray
|
||||
|
||||
type commentFilterState = byte
|
||||
|
||||
const (
|
||||
commentFilterStateContent commentFilterState = iota
|
||||
commentFilterStateEscape
|
||||
commentFilterStateDoubleQuote
|
||||
commentFilterStateDoubleQuoteEscape
|
||||
commentFilterStateSingleQuote
|
||||
commentFilterStateSingleQuoteEscape
|
||||
commentFilterStateComment
|
||||
commentFilterStateSlash
|
||||
commentFilterStateMultilineComment
|
||||
commentFilterStateMultilineCommentStar
|
||||
)
|
||||
|
||||
type CommentFilter struct {
|
||||
br *bufio.Reader
|
||||
state commentFilterState
|
||||
}
|
||||
|
||||
func NewCommentFilter(reader io.Reader) io.Reader {
|
||||
return &CommentFilter{br: bufio.NewReader(reader)}
|
||||
}
|
||||
|
||||
func (v *CommentFilter) Read(b []byte) (int, error) {
|
||||
p := b[:0]
|
||||
for len(p) < len(b)-2 {
|
||||
x, err := v.br.ReadByte()
|
||||
if err != nil {
|
||||
if len(p) == 0 {
|
||||
return 0, err
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
||||
switch v.state {
|
||||
case commentFilterStateContent:
|
||||
switch x {
|
||||
case '"':
|
||||
v.state = commentFilterStateDoubleQuote
|
||||
p = append(p, x)
|
||||
case '\'':
|
||||
v.state = commentFilterStateSingleQuote
|
||||
p = append(p, x)
|
||||
case '\\':
|
||||
v.state = commentFilterStateEscape
|
||||
case '#':
|
||||
v.state = commentFilterStateComment
|
||||
case '/':
|
||||
v.state = commentFilterStateSlash
|
||||
default:
|
||||
p = append(p, x)
|
||||
}
|
||||
case commentFilterStateEscape:
|
||||
p = append(p, '\\', x)
|
||||
v.state = commentFilterStateContent
|
||||
case commentFilterStateDoubleQuote:
|
||||
switch x {
|
||||
case '"':
|
||||
v.state = commentFilterStateContent
|
||||
p = append(p, x)
|
||||
case '\\':
|
||||
v.state = commentFilterStateDoubleQuoteEscape
|
||||
default:
|
||||
p = append(p, x)
|
||||
}
|
||||
case commentFilterStateDoubleQuoteEscape:
|
||||
p = append(p, '\\', x)
|
||||
v.state = commentFilterStateDoubleQuote
|
||||
case commentFilterStateSingleQuote:
|
||||
switch x {
|
||||
case '\'':
|
||||
v.state = commentFilterStateContent
|
||||
p = append(p, x)
|
||||
case '\\':
|
||||
v.state = commentFilterStateSingleQuoteEscape
|
||||
default:
|
||||
p = append(p, x)
|
||||
}
|
||||
case commentFilterStateSingleQuoteEscape:
|
||||
p = append(p, '\\', x)
|
||||
v.state = commentFilterStateSingleQuote
|
||||
case commentFilterStateComment:
|
||||
if x == '\n' {
|
||||
v.state = commentFilterStateContent
|
||||
p = append(p, '\n')
|
||||
}
|
||||
case commentFilterStateSlash:
|
||||
switch x {
|
||||
case '/':
|
||||
v.state = commentFilterStateComment
|
||||
case '*':
|
||||
v.state = commentFilterStateMultilineComment
|
||||
default:
|
||||
p = append(p, '/', x)
|
||||
}
|
||||
case commentFilterStateMultilineComment:
|
||||
switch x {
|
||||
case '*':
|
||||
v.state = commentFilterStateMultilineCommentStar
|
||||
case '\n':
|
||||
p = append(p, '\n')
|
||||
}
|
||||
case commentFilterStateMultilineCommentStar:
|
||||
switch x {
|
||||
case '/':
|
||||
v.state = commentFilterStateContent
|
||||
case '*':
|
||||
// Stay
|
||||
case '\n':
|
||||
p = append(p, '\n')
|
||||
default:
|
||||
v.state = commentFilterStateMultilineComment
|
||||
}
|
||||
default:
|
||||
panic("Unknown state.")
|
||||
}
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
|
||||
type Listener struct {
|
||||
net.Listener
|
||||
AcceptNoHeader bool
|
||||
}
|
||||
|
||||
func (l *Listener) Accept() (net.Conn, error) {
|
||||
@@ -23,7 +22,7 @@ func (l *Listener) Accept() (net.Conn, error) {
|
||||
}
|
||||
bufReader := std_bufio.NewReader(conn)
|
||||
header, err := proxyproto.Read(bufReader)
|
||||
if err != nil && !(l.AcceptNoHeader && err == proxyproto.ErrNoProxyProtocol) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if bufReader.Buffered() > 0 {
|
||||
@@ -34,11 +33,8 @@ func (l *Listener) Accept() (net.Conn, error) {
|
||||
}
|
||||
conn = bufio.NewCachedConn(conn, cache)
|
||||
}
|
||||
if header != nil {
|
||||
return &bufio.AddrConn{Conn: conn, Metadata: M.Metadata{
|
||||
Source: M.SocksaddrFromNet(header.SourceAddr),
|
||||
Destination: M.SocksaddrFromNet(header.DestinationAddr),
|
||||
}}, nil
|
||||
}
|
||||
return conn, nil
|
||||
return &bufio.AddrConn{Conn: conn, Metadata: M.Metadata{
|
||||
Source: M.SocksaddrFromNet(header.SourceAddr),
|
||||
Destination: M.SocksaddrFromNet(header.DestinationAddr),
|
||||
}}, nil
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ type systemProxy struct {
|
||||
isMixed bool
|
||||
}
|
||||
|
||||
func (p *systemProxy) update(event int) error {
|
||||
func (p *systemProxy) update() error {
|
||||
newInterfaceName := p.monitor.DefaultInterfaceName(netip.IPv4Unspecified())
|
||||
if p.interfaceName == newInterfaceName {
|
||||
return nil
|
||||
@@ -88,7 +88,7 @@ func SetSystemProxy(router adapter.Router, port uint16, isMixed bool) (func() er
|
||||
port: port,
|
||||
isMixed: isMixed,
|
||||
}
|
||||
err := proxy.update(tun.EventInterfaceUpdate)
|
||||
err := proxy.update()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
package tls
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
func NewDialerFromOptions(router adapter.Router, dialer N.Dialer, serverAddress string, options option.OutboundTLSOptions) (N.Dialer, error) {
|
||||
config, err := NewClient(router, serverAddress, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewDialer(dialer, config), nil
|
||||
}
|
||||
|
||||
func NewClient(router adapter.Router, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
|
||||
if options.ECH != nil && options.ECH.Enabled {
|
||||
return newECHClient(router, serverAddress, options)
|
||||
} else if options.UTLS != nil && options.UTLS.Enabled {
|
||||
return newUTLSClient(router, serverAddress, options)
|
||||
} else {
|
||||
return newStdClient(serverAddress, options)
|
||||
}
|
||||
}
|
||||
|
||||
func ClientHandshake(ctx context.Context, conn net.Conn, config Config) (Conn, error) {
|
||||
tlsConn := config.Client(conn)
|
||||
ctx, cancel := context.WithTimeout(ctx, C.TCPTimeout)
|
||||
defer cancel()
|
||||
err := tlsConn.HandshakeContext(ctx)
|
||||
return tlsConn, err
|
||||
}
|
||||
|
||||
type Dialer struct {
|
||||
dialer N.Dialer
|
||||
config Config
|
||||
}
|
||||
|
||||
func NewDialer(dialer N.Dialer, config Config) N.Dialer {
|
||||
return &Dialer{dialer, config}
|
||||
}
|
||||
|
||||
func (d *Dialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||
if network != N.NetworkTCP {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
conn, err := d.dialer.DialContext(ctx, network, destination)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ClientHandshake(ctx, conn, d.config)
|
||||
}
|
||||
|
||||
func (d *Dialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package tls
|
||||
|
||||
const (
|
||||
VersionTLS10 = 0x0301
|
||||
VersionTLS11 = 0x0302
|
||||
VersionTLS12 = 0x0303
|
||||
VersionTLS13 = 0x0304
|
||||
|
||||
// Deprecated: SSLv3 is cryptographically broken, and is no longer
|
||||
// supported by this package. See golang.org/issue/32716.
|
||||
VersionSSL30 = 0x0300
|
||||
)
|
||||
@@ -1,49 +0,0 @@
|
||||
package tls
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
|
||||
type (
|
||||
STDConfig = tls.Config
|
||||
STDConn = tls.Conn
|
||||
)
|
||||
|
||||
type Config interface {
|
||||
NextProtos() []string
|
||||
SetNextProtos(nextProto []string)
|
||||
Config() (*STDConfig, error)
|
||||
Client(conn net.Conn) Conn
|
||||
}
|
||||
|
||||
type ServerConfig interface {
|
||||
Config
|
||||
adapter.Service
|
||||
Server(conn net.Conn) Conn
|
||||
}
|
||||
|
||||
type Conn interface {
|
||||
net.Conn
|
||||
HandshakeContext(ctx context.Context) error
|
||||
ConnectionState() tls.ConnectionState
|
||||
}
|
||||
|
||||
func ParseTLSVersion(version string) (uint16, error) {
|
||||
switch version {
|
||||
case "1.0":
|
||||
return tls.VersionTLS10, nil
|
||||
case "1.1":
|
||||
return tls.VersionTLS11, nil
|
||||
case "1.2":
|
||||
return tls.VersionTLS12, nil
|
||||
case "1.3":
|
||||
return tls.VersionTLS13, nil
|
||||
default:
|
||||
return 0, E.New("unknown tls version:", version)
|
||||
}
|
||||
}
|
||||
@@ -1,219 +0,0 @@
|
||||
//go:build with_ech
|
||||
|
||||
package tls
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
cftls "github.com/sagernet/sing-box/transport/cloudflaretls"
|
||||
"github.com/sagernet/sing-dns"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
|
||||
mDNS "github.com/miekg/dns"
|
||||
"golang.org/x/net/dns/dnsmessage"
|
||||
)
|
||||
|
||||
type echClientConfig struct {
|
||||
config *cftls.Config
|
||||
}
|
||||
|
||||
func (e *echClientConfig) NextProtos() []string {
|
||||
return e.config.NextProtos
|
||||
}
|
||||
|
||||
func (e *echClientConfig) SetNextProtos(nextProto []string) {
|
||||
e.config.NextProtos = nextProto
|
||||
}
|
||||
|
||||
func (e *echClientConfig) Config() (*STDConfig, error) {
|
||||
return nil, E.New("unsupported usage for ECH")
|
||||
}
|
||||
|
||||
func (e *echClientConfig) Client(conn net.Conn) Conn {
|
||||
return &echConnWrapper{cftls.Client(conn, e.config)}
|
||||
}
|
||||
|
||||
type echConnWrapper struct {
|
||||
*cftls.Conn
|
||||
}
|
||||
|
||||
func (c *echConnWrapper) ConnectionState() tls.ConnectionState {
|
||||
state := c.Conn.ConnectionState()
|
||||
return tls.ConnectionState{
|
||||
Version: state.Version,
|
||||
HandshakeComplete: state.HandshakeComplete,
|
||||
DidResume: state.DidResume,
|
||||
CipherSuite: state.CipherSuite,
|
||||
NegotiatedProtocol: state.NegotiatedProtocol,
|
||||
NegotiatedProtocolIsMutual: state.NegotiatedProtocolIsMutual,
|
||||
ServerName: state.ServerName,
|
||||
PeerCertificates: state.PeerCertificates,
|
||||
VerifiedChains: state.VerifiedChains,
|
||||
SignedCertificateTimestamps: state.SignedCertificateTimestamps,
|
||||
OCSPResponse: state.OCSPResponse,
|
||||
TLSUnique: state.TLSUnique,
|
||||
}
|
||||
}
|
||||
|
||||
func newECHClient(router adapter.Router, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
|
||||
var serverName string
|
||||
if options.ServerName != "" {
|
||||
serverName = options.ServerName
|
||||
} else if serverAddress != "" {
|
||||
if _, err := netip.ParseAddr(serverName); err != nil {
|
||||
serverName = serverAddress
|
||||
}
|
||||
}
|
||||
if serverName == "" && !options.Insecure {
|
||||
return nil, E.New("missing server_name or insecure=true")
|
||||
}
|
||||
|
||||
var tlsConfig cftls.Config
|
||||
if options.DisableSNI {
|
||||
tlsConfig.ServerName = "127.0.0.1"
|
||||
} else {
|
||||
tlsConfig.ServerName = serverName
|
||||
}
|
||||
if options.Insecure {
|
||||
tlsConfig.InsecureSkipVerify = options.Insecure
|
||||
} else if options.DisableSNI {
|
||||
tlsConfig.InsecureSkipVerify = true
|
||||
tlsConfig.VerifyConnection = func(state cftls.ConnectionState) error {
|
||||
verifyOptions := x509.VerifyOptions{
|
||||
DNSName: serverName,
|
||||
Intermediates: x509.NewCertPool(),
|
||||
}
|
||||
for _, cert := range state.PeerCertificates[1:] {
|
||||
verifyOptions.Intermediates.AddCert(cert)
|
||||
}
|
||||
_, err := state.PeerCertificates[0].Verify(verifyOptions)
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(options.ALPN) > 0 {
|
||||
tlsConfig.NextProtos = options.ALPN
|
||||
}
|
||||
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 cftls.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
|
||||
if options.Certificate != "" {
|
||||
certificate = []byte(options.Certificate)
|
||||
} else if options.CertificatePath != "" {
|
||||
content, err := os.ReadFile(options.CertificatePath)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "read certificate")
|
||||
}
|
||||
certificate = content
|
||||
}
|
||||
if len(certificate) > 0 {
|
||||
certPool := x509.NewCertPool()
|
||||
if !certPool.AppendCertsFromPEM(certificate) {
|
||||
return nil, E.New("failed to parse certificate:\n\n", certificate)
|
||||
}
|
||||
tlsConfig.RootCAs = certPool
|
||||
}
|
||||
|
||||
// ECH Config
|
||||
|
||||
tlsConfig.ECHEnabled = true
|
||||
tlsConfig.PQSignatureSchemesEnabled = options.ECH.PQSignatureSchemesEnabled
|
||||
tlsConfig.DynamicRecordSizingDisabled = options.ECH.DynamicRecordSizingDisabled
|
||||
if options.ECH.Config != "" {
|
||||
clientConfigContent, err := base64.StdEncoding.DecodeString(options.ECH.Config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
clientConfig, err := cftls.UnmarshalECHConfigs(clientConfigContent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tlsConfig.ClientECHConfigs = clientConfig
|
||||
} else {
|
||||
tlsConfig.GetClientECHConfigs = fetchECHClientConfig(router)
|
||||
}
|
||||
return &echClientConfig{&tlsConfig}, nil
|
||||
}
|
||||
|
||||
const typeHTTPS = 65
|
||||
|
||||
func fetchECHClientConfig(router adapter.Router) func(ctx context.Context, serverName string) ([]cftls.ECHConfig, error) {
|
||||
return func(ctx context.Context, serverName string) ([]cftls.ECHConfig, error) {
|
||||
message := &dnsmessage.Message{
|
||||
Header: dnsmessage.Header{
|
||||
RecursionDesired: true,
|
||||
},
|
||||
Questions: []dnsmessage.Question{
|
||||
{
|
||||
Name: dnsmessage.MustNewName(serverName + "."),
|
||||
Type: typeHTTPS,
|
||||
Class: dnsmessage.ClassINET,
|
||||
},
|
||||
},
|
||||
}
|
||||
response, err := router.Exchange(ctx, message)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if response.RCode != dnsmessage.RCodeSuccess {
|
||||
return nil, dns.RCodeError(response.RCode)
|
||||
}
|
||||
content, err := response.Pack()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var mMsg mDNS.Msg
|
||||
err = mMsg.Unpack(content)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, rr := range mMsg.Answer {
|
||||
switch resource := rr.(type) {
|
||||
case *mDNS.HTTPS:
|
||||
for _, value := range resource.Value {
|
||||
if value.Key().String() == "ech" {
|
||||
echConfig, err := base64.StdEncoding.DecodeString(value.String())
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "decode ECH config")
|
||||
}
|
||||
return cftls.UnmarshalECHConfigs(echConfig)
|
||||
}
|
||||
}
|
||||
default:
|
||||
return nil, E.New("unknown resource record type: ", resource.Header().Rrtype)
|
||||
}
|
||||
}
|
||||
return nil, E.New("no ECH config found")
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
//go:build !with_ech
|
||||
|
||||
package tls
|
||||
|
||||
import (
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
|
||||
func newECHClient(router adapter.Router, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
|
||||
return nil, E.New(`ECH is not included in this build, rebuild with -tags with_ech`)
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package tls
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
)
|
||||
|
||||
func NewServer(ctx context.Context, logger log.Logger, options option.InboundTLSOptions) (ServerConfig, error) {
|
||||
return newSTDServer(ctx, logger, options)
|
||||
}
|
||||
@@ -1,151 +0,0 @@
|
||||
//go:build with_utls
|
||||
|
||||
package tls
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
|
||||
utls "github.com/refraction-networking/utls"
|
||||
)
|
||||
|
||||
type utlsClientConfig struct {
|
||||
config *utls.Config
|
||||
id utls.ClientHelloID
|
||||
}
|
||||
|
||||
func (e *utlsClientConfig) NextProtos() []string {
|
||||
return e.config.NextProtos
|
||||
}
|
||||
|
||||
func (e *utlsClientConfig) SetNextProtos(nextProto []string) {
|
||||
e.config.NextProtos = nextProto
|
||||
}
|
||||
|
||||
func (e *utlsClientConfig) Config() (*STDConfig, error) {
|
||||
return nil, E.New("unsupported usage for uTLS")
|
||||
}
|
||||
|
||||
func (e *utlsClientConfig) Client(conn net.Conn) Conn {
|
||||
return &utlsConnWrapper{utls.UClient(conn, e.config, e.id)}
|
||||
}
|
||||
|
||||
type utlsConnWrapper struct {
|
||||
*utls.UConn
|
||||
}
|
||||
|
||||
func (c *utlsConnWrapper) HandshakeContext(ctx context.Context) error {
|
||||
return c.Conn.Handshake()
|
||||
}
|
||||
|
||||
func (c *utlsConnWrapper) ConnectionState() tls.ConnectionState {
|
||||
state := c.Conn.ConnectionState()
|
||||
return tls.ConnectionState{
|
||||
Version: state.Version,
|
||||
HandshakeComplete: state.HandshakeComplete,
|
||||
DidResume: state.DidResume,
|
||||
CipherSuite: state.CipherSuite,
|
||||
NegotiatedProtocol: state.NegotiatedProtocol,
|
||||
NegotiatedProtocolIsMutual: state.NegotiatedProtocolIsMutual,
|
||||
ServerName: state.ServerName,
|
||||
PeerCertificates: state.PeerCertificates,
|
||||
VerifiedChains: state.VerifiedChains,
|
||||
SignedCertificateTimestamps: state.SignedCertificateTimestamps,
|
||||
OCSPResponse: state.OCSPResponse,
|
||||
TLSUnique: state.TLSUnique,
|
||||
}
|
||||
}
|
||||
|
||||
func newUTLSClient(router adapter.Router, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
|
||||
var serverName string
|
||||
if options.ServerName != "" {
|
||||
serverName = options.ServerName
|
||||
} else if serverAddress != "" {
|
||||
if _, err := netip.ParseAddr(serverName); err != nil {
|
||||
serverName = serverAddress
|
||||
}
|
||||
}
|
||||
if serverName == "" && !options.Insecure {
|
||||
return nil, E.New("missing server_name or insecure=true")
|
||||
}
|
||||
|
||||
var tlsConfig utls.Config
|
||||
if options.DisableSNI {
|
||||
tlsConfig.ServerName = "127.0.0.1"
|
||||
} else {
|
||||
tlsConfig.ServerName = serverName
|
||||
}
|
||||
if options.Insecure {
|
||||
tlsConfig.InsecureSkipVerify = options.Insecure
|
||||
} else if options.DisableSNI {
|
||||
return nil, E.New("disable_sni is unsupported in uTLS")
|
||||
}
|
||||
if len(options.ALPN) > 0 {
|
||||
tlsConfig.NextProtos = options.ALPN
|
||||
}
|
||||
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
|
||||
if options.Certificate != "" {
|
||||
certificate = []byte(options.Certificate)
|
||||
} else if options.CertificatePath != "" {
|
||||
content, err := os.ReadFile(options.CertificatePath)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "read certificate")
|
||||
}
|
||||
certificate = content
|
||||
}
|
||||
if len(certificate) > 0 {
|
||||
certPool := x509.NewCertPool()
|
||||
if !certPool.AppendCertsFromPEM(certificate) {
|
||||
return nil, E.New("failed to parse certificate:\n\n", certificate)
|
||||
}
|
||||
tlsConfig.RootCAs = certPool
|
||||
}
|
||||
var id utls.ClientHelloID
|
||||
switch options.UTLS.Fingerprint {
|
||||
case "chrome", "":
|
||||
id = utls.HelloChrome_Auto
|
||||
case "firefox":
|
||||
id = utls.HelloFirefox_Auto
|
||||
case "ios":
|
||||
id = utls.HelloIOS_Auto
|
||||
case "android":
|
||||
id = utls.HelloAndroid_11_OkHttp
|
||||
case "random":
|
||||
id = utls.HelloRandomized
|
||||
}
|
||||
return &utlsClientConfig{&tlsConfig, id}, nil
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
//go:build !with_utls
|
||||
|
||||
package tls
|
||||
|
||||
import (
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
|
||||
func newUTLSClient(router adapter.Router, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
|
||||
return nil, E.New(`uTLS is not included in this build, rebuild with -tags with_utls`)
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package constant
|
||||
|
||||
var (
|
||||
Version = "1.1-beta3"
|
||||
Version = "1.0.2"
|
||||
Commit = ""
|
||||
)
|
||||
|
||||
@@ -1,76 +1,3 @@
|
||||
#### 1.1-beta3
|
||||
|
||||
* Fix using custom TLS client in http2 client
|
||||
* Fix bugs in 1.1-beta2
|
||||
|
||||
#### 1.1-beta2
|
||||
|
||||
* Add Clash mode and persistence support **1**
|
||||
* Add TLS ECH and uTLS support for outbound TLS options **2**
|
||||
* Fix socks4 request
|
||||
* Fix processing empty dns result
|
||||
|
||||
*1*:
|
||||
|
||||
Switching modes using the Clash API, and `store-selected` are now supported,
|
||||
see [Experimental](/configuration/experimental).
|
||||
|
||||
*2*:
|
||||
|
||||
ECH (Encrypted Client Hello) is a TLS extension that allows a client to encrypt the first part of its ClientHello
|
||||
message, see [TLS#ECH](/configuration/shared/tls#ech).
|
||||
|
||||
uTLS is a fork of "crypto/tls", which provides ClientHello fingerprinting resistance,
|
||||
see [TLS#uTLS](/configuration/shared/tls#utls).
|
||||
|
||||
#### 1.0.2
|
||||
|
||||
* Fix socks4 request
|
||||
* Fix processing empty dns result
|
||||
|
||||
#### 1.1-beta1
|
||||
|
||||
* Add support for use with android VPNService **1**
|
||||
* Add tun support for WireGuard outbound **2**
|
||||
* Add system tun stack **3**
|
||||
* Add comment filter for config **4**
|
||||
* Add option for allow optional proxy protocol header
|
||||
* Add half close for smux
|
||||
* Set UDP DF by default **5**
|
||||
* Set default tun mtu to 9000
|
||||
* Update gVisor to 20220905.0
|
||||
|
||||
*1*:
|
||||
|
||||
In previous versions, Android VPN would not work with tun enabled.
|
||||
|
||||
The usage of tun over VPN and VPN over tun is now supported, see [Tun Inbound](/configuration/inbound/tun#auto_route).
|
||||
|
||||
*2*:
|
||||
|
||||
In previous releases, WireGuard outbound support was backed by the lower performance gVisor virtual interface.
|
||||
|
||||
It achieves the same performance as wireguard-go by providing automatic system interface support.
|
||||
|
||||
*3*:
|
||||
|
||||
It does not depend on gVisor and has better performance in some cases.
|
||||
|
||||
It is less compatible and may not be available in some environments.
|
||||
|
||||
*4*:
|
||||
|
||||
Annotated json configuration files are now supported.
|
||||
|
||||
*5*:
|
||||
|
||||
UDP fragmentation is now blocked by default.
|
||||
|
||||
Including shadowsocks-libev, shadowsocks-rust and quic-go all disable segmentation by default.
|
||||
|
||||
See [Dial Fields](/configuration/shared/dial#udp_fragment)
|
||||
and [Listen Fields](/configuration/shared/listen#udp_fragment).
|
||||
|
||||
#### 1.0.1
|
||||
|
||||
* Fix match 4in6 address in ip_cidr
|
||||
|
||||
@@ -73,7 +73,6 @@
|
||||
"user_id": [
|
||||
1000
|
||||
],
|
||||
"clash_mode": "direct",
|
||||
"invert": false,
|
||||
"outbound": [
|
||||
"direct"
|
||||
@@ -209,10 +208,6 @@ Match user name.
|
||||
|
||||
Match user id.
|
||||
|
||||
#### clash_mode
|
||||
|
||||
Match Clash mode.
|
||||
|
||||
#### invert
|
||||
|
||||
Invert match result.
|
||||
|
||||
@@ -72,7 +72,6 @@
|
||||
"user_id": [
|
||||
1000
|
||||
],
|
||||
"clash_mode": "direct",
|
||||
"invert": false,
|
||||
"outbound": [
|
||||
"direct"
|
||||
@@ -208,10 +207,6 @@
|
||||
|
||||
匹配用户 ID。
|
||||
|
||||
#### clash_mode
|
||||
|
||||
匹配 Clash 模式。
|
||||
|
||||
#### invert
|
||||
|
||||
反选匹配结果。
|
||||
|
||||
@@ -8,10 +8,7 @@
|
||||
"clash_api": {
|
||||
"external_controller": "127.0.0.1:9090",
|
||||
"external_ui": "folder",
|
||||
"secret": "",
|
||||
"default_mode": "rule",
|
||||
"store_selected": false,
|
||||
"cache_file": "cache.db"
|
||||
"secret": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,7 +26,7 @@
|
||||
|
||||
#### external_controller
|
||||
|
||||
RESTful web API listening address. Clash API will be disabled if empty.
|
||||
RESTful web API listening address. Disabled if empty.
|
||||
|
||||
#### external_ui
|
||||
|
||||
@@ -41,22 +38,4 @@ serve it at `http://{{external-controller}}/ui`.
|
||||
|
||||
Secret for the RESTful API (optional)
|
||||
Authenticate by spedifying HTTP header `Authorization: Bearer ${secret}`
|
||||
ALWAYS set a secret if RESTful API is listening on 0.0.0.0
|
||||
|
||||
#### default_mode
|
||||
|
||||
Default mode in clash, `rule` will be used if empty.
|
||||
|
||||
This setting has no direct effect, but can be used in routing and DNS rules via the `clash_mode` rule item.
|
||||
|
||||
#### store_selected
|
||||
|
||||
!!! note ""
|
||||
|
||||
The tag must be set for target outbounds.
|
||||
|
||||
Store selected outbound for the `Selector` outbound in cache file.
|
||||
|
||||
#### cache_file
|
||||
|
||||
Cache file path, `cache.db` will be used if empty.
|
||||
ALWAYS set a secret if RESTful API is listening on 0.0.0.0
|
||||
@@ -8,10 +8,7 @@
|
||||
"clash_api": {
|
||||
"external_controller": "127.0.0.1:9090",
|
||||
"external_ui": "folder",
|
||||
"secret": "",
|
||||
"default_mode": "rule",
|
||||
"store_selected": false,
|
||||
"cache_file": "cache.db"
|
||||
"secret": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,7 +26,7 @@
|
||||
|
||||
#### external_controller
|
||||
|
||||
RESTful web API 监听地址。如果为空,则禁用 Clash API。
|
||||
RESTful web API 监听地址。
|
||||
|
||||
#### external_ui
|
||||
|
||||
@@ -39,22 +36,4 @@ RESTful web API 监听地址。如果为空,则禁用 Clash API。
|
||||
|
||||
RESTful API 的密钥(可选)
|
||||
通过指定 HTTP 标头 `Authorization: Bearer ${secret}` 进行身份验证
|
||||
如果 RESTful API 正在监听 0.0.0.0,请始终设置一个密钥。
|
||||
|
||||
#### default_mode
|
||||
|
||||
Clash 中的默认模式,默认使用 `rule`。
|
||||
|
||||
此设置没有直接影响,但可以通过 `clash_mode` 规则项在路由和 DNS 规则中使用。
|
||||
|
||||
#### store_selected
|
||||
|
||||
!!! note ""
|
||||
|
||||
必须为目标出站设置标签。
|
||||
|
||||
将 `Selector` 中出站的选定的目标出站存储在缓存文件中。
|
||||
|
||||
#### cache_file
|
||||
|
||||
缓存文件路径,默认使用`cache.db`。
|
||||
如果 RESTful API 正在监听 0.0.0.0,请始终设置一个密钥。
|
||||
@@ -11,8 +11,8 @@
|
||||
|
||||
"interface_name": "tun0",
|
||||
"inet4_address": "172.19.0.1/30",
|
||||
"inet6_address": "fdfe:dcba:9876::1/126",
|
||||
"mtu": 9000,
|
||||
"inet6_address": "fdfe:dcba:9876::1/128",
|
||||
"mtu": 1500,
|
||||
"auto_route": true,
|
||||
"strict_route": true,
|
||||
"endpoint_independent_nat": false,
|
||||
@@ -80,10 +80,6 @@ Set the default route to the Tun.
|
||||
|
||||
To avoid traffic loopback, set `route.auto_detect_interface` or `route.default_interface` or `outbound.bind_interface`
|
||||
|
||||
!!! note "Use with Android VPN"
|
||||
|
||||
By default, VPN takes precedence over tun. To make tun go through VPN, enable `route.override_android_vpn`.
|
||||
|
||||
#### strict_route
|
||||
|
||||
Enforce strict routing rules in Linux when `auto_route` is enabled:
|
||||
@@ -96,10 +92,6 @@ not be accessible by others.
|
||||
|
||||
#### endpoint_independent_nat
|
||||
|
||||
!!! info ""
|
||||
|
||||
This item is only available on the gvisor stack, other stacks are endpoint-independent NAT by default.
|
||||
|
||||
Enable endpoint-independent NAT.
|
||||
|
||||
Performance may degrade slightly, so it is not recommended to enable on when it is not needed.
|
||||
@@ -112,11 +104,10 @@ UDP NAT expiration time in seconds, default is 300 (5 minutes).
|
||||
|
||||
TCP/IP stack.
|
||||
|
||||
| Stack | Description | Status |
|
||||
|------------------|--------------------------------------------------------------------------------|-------------------|
|
||||
| gVisor (default) | Based on [google/gvisor](https://github.com/google/gvisor) | recommended |
|
||||
| system | Less compatibility and sometimes better performance. | recommended |
|
||||
| LWIP | Based on [eycorsican/go-tun2socks](https://github.com/eycorsican/go-tun2socks) | upstream archived |
|
||||
| Stack | Upstream | Status |
|
||||
|------------------|-----------------------------------------------------------------------|-------------------|
|
||||
| gVisor (default) | [google/gvisor](https://github.com/google/gvisor) | recommended |
|
||||
| LWIP | [eycorsican/go-tun2socks](https://github.com/eycorsican/go-tun2socks) | upstream archived |
|
||||
|
||||
!!! warning ""
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
|
||||
"interface_name": "tun0",
|
||||
"inet4_address": "172.19.0.1/30",
|
||||
"inet6_address": "fdfe:dcba:9876::1/126",
|
||||
"mtu": 9000,
|
||||
"inet6_address": "fdfe:dcba:9876::1/128",
|
||||
"mtu": 1500,
|
||||
"auto_route": true,
|
||||
"strict_route": true,
|
||||
"endpoint_independent_nat": false,
|
||||
@@ -80,10 +80,6 @@ tun 接口的 IPv6 前缀。
|
||||
|
||||
为避免流量环回,请设置 `route.auto_detect_interface` 或 `route.default_interface` 或 `outbound.bind_interface`。
|
||||
|
||||
!!! note "与 Android VPN 一起使用"
|
||||
|
||||
VPN 默认优先于 tun。要使 tun 经过 VPN,启用 `route.override_android_vpn`。
|
||||
|
||||
#### strict_route
|
||||
|
||||
在 Linux 中启用 `auto_route` 时执行严格的路由规则。
|
||||
@@ -107,11 +103,10 @@ UDP NAT 过期时间,以秒为单位,默认为 300(5 分钟)。
|
||||
|
||||
TCP/IP 栈。
|
||||
|
||||
| 栈 | 描述 | 状态 |
|
||||
|------------------|--------------------------------------------------------------------------|-------|
|
||||
| gVisor (default) | 基于 [google/gvisor](https://github.com/google/gvisor) | 推荐 |
|
||||
| system | 兼容性较差,有时性能更好。 | 推荐 |
|
||||
| LWIP | 基于 [eycorsican/go-tun2socks](https://github.com/eycorsican/go-tun2socks) | 上游已存档 |
|
||||
| 栈 | 上游 | 状态 |
|
||||
|------------------|-----------------------------------------------------------------------|-------|
|
||||
| gVisor (default) | [google/gvisor](https://github.com/google/gvisor) | 推荐 |
|
||||
| LWIP | [eycorsican/go-tun2socks](https://github.com/eycorsican/go-tun2socks) | 上游已存档 |
|
||||
|
||||
!!! warning ""
|
||||
|
||||
|
||||
@@ -7,9 +7,8 @@
|
||||
|
||||
"server": "127.0.0.1",
|
||||
"server_port": 1080,
|
||||
"system_interface": false,
|
||||
"interface_name": "wg0",
|
||||
"local_address": [
|
||||
"10.0.0.1",
|
||||
"10.0.0.2/32"
|
||||
],
|
||||
"private_key": "YNXtAzepDqRv9H52osJVDQnznT5AM11eCK3ESpwSt04=",
|
||||
@@ -40,21 +39,11 @@ The server address.
|
||||
|
||||
The server port.
|
||||
|
||||
#### system_interface
|
||||
|
||||
Use system tun support.
|
||||
|
||||
Requires privileges and cannot conflict with system interfaces.
|
||||
|
||||
#### interface_name
|
||||
|
||||
Custom device name when `system_interface` enabled.
|
||||
|
||||
#### local_address
|
||||
|
||||
==Required==
|
||||
|
||||
List of IP (v4 or v6) address prefixes to be assigned to the interface.
|
||||
List of IP (v4 or v6) addresses (optionally with CIDR masks) to be assigned to the interface.
|
||||
|
||||
#### private_key
|
||||
|
||||
|
||||
@@ -7,9 +7,8 @@
|
||||
|
||||
"server": "127.0.0.1",
|
||||
"server_port": 1080,
|
||||
"system_interface": false,
|
||||
"interface_name": "wg0",
|
||||
"local_address": [
|
||||
"10.0.0.1",
|
||||
"10.0.0.2/32"
|
||||
],
|
||||
"private_key": "YNXtAzepDqRv9H52osJVDQnznT5AM11eCK3ESpwSt04=",
|
||||
@@ -40,23 +39,13 @@
|
||||
|
||||
服务器端口。
|
||||
|
||||
#### system_interface
|
||||
|
||||
使用系统 tun 支持。
|
||||
|
||||
需要特权且不能与系统接口冲突。
|
||||
|
||||
#### interface_name
|
||||
|
||||
启用 `system_interface` 时的自定义设备名称。
|
||||
|
||||
#### local_address
|
||||
|
||||
==必填==
|
||||
|
||||
接口的 IPv4/IPv6 地址或地址段的列表您。
|
||||
|
||||
要分配给接口的 IP(v4 或 v6)地址段列表。
|
||||
要分配给接口的 IP(v4 或 v6)地址列表(可以选择带有 CIDR 掩码)。
|
||||
|
||||
#### private_key
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
"rules": [],
|
||||
"final": "",
|
||||
"auto_detect_interface": false,
|
||||
"override_android_vpn": false,
|
||||
"default_interface": "en0",
|
||||
"default_mark": 233
|
||||
}
|
||||
@@ -35,25 +34,17 @@ Default outbound tag. the first outbound will be used if empty.
|
||||
|
||||
Only supported on Linux, Windows and macOS.
|
||||
|
||||
Bind outbound connections to the default NIC by default to prevent routing loops under tun.
|
||||
Bind outbound connections to the default NIC by default to prevent routing loops under Tun.
|
||||
|
||||
Takes no effect if `outbound.bind_interface` is set.
|
||||
|
||||
#### override_android_vpn
|
||||
|
||||
!!! error ""
|
||||
|
||||
Only supported on Android.
|
||||
|
||||
Accept Android VPN as upstream NIC when `auto_detect_interface` enabled.
|
||||
|
||||
#### default_interface
|
||||
|
||||
!!! error ""
|
||||
|
||||
Only supported on Linux, Windows and macOS.
|
||||
|
||||
Bind outbound connections to the specified NIC by default to prevent routing loops under tun.
|
||||
Bind outbound connections to the specified NIC by default to prevent routing loops under Tun.
|
||||
|
||||
Takes no effect if `auto_detect_interface` is set.
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
"rules": [],
|
||||
"final": "",
|
||||
"auto_detect_interface": false,
|
||||
"override_android_vpn": false,
|
||||
"default_interface": "en0",
|
||||
"default_mark": 233
|
||||
}
|
||||
@@ -35,25 +34,17 @@
|
||||
|
||||
仅支持 Linux、Windows 和 macOS。
|
||||
|
||||
默认将出站连接绑定到默认网卡,以防止在 tun 下出现路由环路。
|
||||
默认将出站连接绑定到默认网卡,以防止在 Tun 下出现路由环路。
|
||||
|
||||
如果设置了 `outbound.bind_interface` 设置,则不生效。
|
||||
|
||||
#### override_android_vpn
|
||||
|
||||
!!! error ""
|
||||
|
||||
仅支持 Android。
|
||||
|
||||
启用 `auto_detect_interface` 时接受 Android VPN 作为上游网卡。
|
||||
|
||||
#### default_interface
|
||||
|
||||
!!! error ""
|
||||
|
||||
仅支持 Linux、Windows 和 macOS。
|
||||
|
||||
默认将出站连接绑定到指定网卡,以防止在 tun 下出现路由环路。
|
||||
默认将出站连接绑定到指定网卡,以防止在 Tun 下出现路由环路。
|
||||
|
||||
如果设置了 `auto_detect_interface` 设置,则不生效。
|
||||
|
||||
|
||||
@@ -80,7 +80,6 @@
|
||||
"user_id": [
|
||||
1000
|
||||
],
|
||||
"clash_mode": "direct",
|
||||
"invert": false,
|
||||
"outbound": "direct"
|
||||
},
|
||||
@@ -220,10 +219,6 @@ Match user name.
|
||||
|
||||
Match user id.
|
||||
|
||||
#### clash_mode
|
||||
|
||||
Match Clash mode.
|
||||
|
||||
#### invert
|
||||
|
||||
Invert match result.
|
||||
|
||||
@@ -78,7 +78,6 @@
|
||||
"user_id": [
|
||||
1000
|
||||
],
|
||||
"clash_mode": "direct",
|
||||
"invert": false,
|
||||
"outbound": "direct"
|
||||
},
|
||||
@@ -218,10 +217,6 @@
|
||||
|
||||
匹配用户 ID。
|
||||
|
||||
#### clash_mode
|
||||
|
||||
匹配 Clash 模式。
|
||||
|
||||
#### invert
|
||||
|
||||
反选匹配结果。
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
"reuse_addr": false,
|
||||
"connect_timeout": "5s",
|
||||
"tcp_fast_open": false,
|
||||
"udp_fragment": false,
|
||||
"domain_strategy": "prefer_ipv6",
|
||||
"fallback_delay": "300ms"
|
||||
}
|
||||
@@ -17,9 +16,9 @@
|
||||
|
||||
### Fields
|
||||
|
||||
| Field | Available Context |
|
||||
|---------------------------------------------------------------------------------------------------------------------|-------------------|
|
||||
| `bind_interface` /`bind_address` /`routing_mark` /`reuse_addr` / `tcp_fast_open`/ `udp_fragment` /`connect_timeout` | `detour` not set |
|
||||
| Field | Available Context |
|
||||
|-----------------------------------------------------------------------------------|-------------------|
|
||||
| `bind_interface` /`bind_address` /`routing_mark` /`reuse_addr` /`connect_timeout` | `detour` not set |
|
||||
|
||||
#### detour
|
||||
|
||||
@@ -45,14 +44,6 @@ Set netfilter routing mark.
|
||||
|
||||
Reuse listener address.
|
||||
|
||||
#### tcp_fast_open
|
||||
|
||||
Enable TCP Fast Open.
|
||||
|
||||
#### udp_fragment
|
||||
|
||||
Enable UDP fragmentation.
|
||||
|
||||
#### connect_timeout
|
||||
|
||||
Connect timeout, in golang's Duration format.
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
"reuse_addr": false,
|
||||
"connect_timeout": "5s",
|
||||
"tcp_fast_open": false,
|
||||
"udp_fragment": false,
|
||||
"domain_strategy": "prefer_ipv6",
|
||||
"fallback_delay": "300ms"
|
||||
}
|
||||
@@ -17,11 +16,6 @@
|
||||
|
||||
### 字段
|
||||
|
||||
| 字段 | 可用上下文 |
|
||||
|---------------------------------------------------------------------------------------------------------------------|--------------|
|
||||
| `bind_interface` /`bind_address` /`routing_mark` /`reuse_addr` / `tcp_fast_open`/ `udp_fragment` /`connect_timeout` | `detour` 未设置 |
|
||||
|
||||
|
||||
#### detour
|
||||
|
||||
上游出站的标签。
|
||||
@@ -48,14 +42,6 @@
|
||||
|
||||
重用监听地址。
|
||||
|
||||
#### tcp_fast_open
|
||||
|
||||
启用 TCP Fast Open。
|
||||
|
||||
#### udp_fragment
|
||||
|
||||
启用 UDP 分段。
|
||||
|
||||
#### connect_timeout
|
||||
|
||||
连接超时,采用 golang 的 Duration 格式。
|
||||
|
||||
@@ -5,27 +5,24 @@
|
||||
"listen": "::",
|
||||
"listen_port": 5353,
|
||||
"tcp_fast_open": false,
|
||||
"udp_fragment": false,
|
||||
"sniff": false,
|
||||
"sniff_override_destination": false,
|
||||
"domain_strategy": "prefer_ipv6",
|
||||
"udp_timeout": 300,
|
||||
"proxy_protocol": false,
|
||||
"proxy_protocol_accept_no_header": false,
|
||||
"detour": "another-in"
|
||||
}
|
||||
```
|
||||
|
||||
### Fields
|
||||
|
||||
| Field | Available Context |
|
||||
|-----------------------------------|-------------------------------------------------------------------|
|
||||
| `listen` | Needs to listen on TCP or UDP. |
|
||||
| `listen_port` | Needs to listen on TCP or UDP. |
|
||||
| `tcp_fast_open` | Needs to listen on TCP. |
|
||||
| `udp_timeout` | Needs to assemble UDP connections, currently Tun and Shadowsocks. |
|
||||
| `proxy_protocol` | Needs to listen on TCP. |
|
||||
| `proxy_protocol_accept_no_header` | When `proxy_protocol` enabled |
|
||||
| Field | Available Context |
|
||||
|------------------|-------------------------------------------------------------------|
|
||||
| `listen` | Needs to listen on TCP or UDP. |
|
||||
| `listen_port` | Needs to listen on TCP or UDP. |
|
||||
| `tcp_fast_open` | Needs to listen on TCP. |
|
||||
| `udp_timeout` | Needs to assemble UDP connections, currently Tun and Shadowsocks. |
|
||||
| `proxy_protocol` | Needs to listen on TCP. |
|
||||
|
||||
#### listen
|
||||
|
||||
@@ -39,11 +36,7 @@ Listen port.
|
||||
|
||||
#### tcp_fast_open
|
||||
|
||||
Enable TCP Fast Open.
|
||||
|
||||
#### udp_fragment
|
||||
|
||||
Enable UDP fragmentation.
|
||||
Enable tcp fast open for listener.
|
||||
|
||||
#### sniff
|
||||
|
||||
@@ -73,10 +66,6 @@ UDP NAT expiration time in seconds, default is 300 (5 minutes).
|
||||
|
||||
Parse [Proxy Protocol](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) in the connection header.
|
||||
|
||||
#### proxy_protocol_accept_no_header
|
||||
|
||||
Accept connections without Proxy Protocol header.
|
||||
|
||||
#### detour
|
||||
|
||||
If set, connections will be forwarded to the specified inbound.
|
||||
|
||||
@@ -5,26 +5,21 @@
|
||||
"listen": "::",
|
||||
"listen_port": 5353,
|
||||
"tcp_fast_open": false,
|
||||
"udp_fragment": false,
|
||||
"sniff": false,
|
||||
"sniff_override_destination": false,
|
||||
"domain_strategy": "prefer_ipv6",
|
||||
"udp_timeout": 300,
|
||||
"proxy_protocol": false,
|
||||
"proxy_protocol_accept_no_header": false,
|
||||
"detour": "another-in"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
| 字段 | 可用上下文 |
|
||||
|-----------------------------------|-------------------------------------|
|
||||
| `listen` | 需要监听 TCP 或 UDP。 |
|
||||
| `listen_port` | 需要监听 TCP 或 UDP。 |
|
||||
| `tcp_fast_open` | 需要监听 TCP。 |
|
||||
| `udp_timeout` | 需要组装 UDP 连接, 当前为 Tun 和 Shadowsocks。 |
|
||||
| `proxy_protocol` | 需要监听 TCP。 |
|
||||
| `proxy_protocol_accept_no_header` | `proxy_protocol` 启用时 |
|
||||
| 字段 | 可用上下文 |
|
||||
|------------------|-------------------------------------|
|
||||
| `listen` | 需要监听 TCP 或 UDP。 |
|
||||
| `listen_port` | 需要监听 TCP 或 UDP。 |
|
||||
| `tcp_fast_open` | 需要监听 TCP。 |
|
||||
| `udp_timeout` | 需要组装 UDP 连接, 当前为 Tun 和 Shadowsocks。 |
|
||||
| `proxy_protocol` | 需要监听 TCP。 |
|
||||
|
||||
### 字段
|
||||
|
||||
@@ -40,11 +35,7 @@
|
||||
|
||||
#### tcp_fast_open
|
||||
|
||||
启用 TCP Fast Open。
|
||||
|
||||
#### udp_fragment
|
||||
|
||||
启用 UDP 分段。
|
||||
为监听器启用 TCP 快速打开。
|
||||
|
||||
#### sniff
|
||||
|
||||
@@ -74,10 +65,6 @@ UDP NAT 过期时间,以秒为单位,默认为 300(5 分钟)。
|
||||
|
||||
解析连接头中的 [代理协议](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt)。
|
||||
|
||||
#### proxy_protocol_accept_no_header
|
||||
|
||||
接受没有代理协议标头的连接。
|
||||
|
||||
#### detour
|
||||
|
||||
如果设置,连接将被转发到指定的入站。
|
||||
|
||||
@@ -30,6 +30,10 @@
|
||||
}
|
||||
```
|
||||
|
||||
!!! warning ""
|
||||
|
||||
ACME is not included by default, see [Installation](/#installation).
|
||||
|
||||
### Outbound
|
||||
|
||||
```json
|
||||
@@ -43,17 +47,7 @@
|
||||
"max_version": "",
|
||||
"cipher_suites": [],
|
||||
"certificate": "",
|
||||
"certificate_path": "",
|
||||
"ech": {
|
||||
"enabled": false,
|
||||
"pq_signature_schemes_enabled": false,
|
||||
"dynamic_record_sizing_disabled": false,
|
||||
"config": ""
|
||||
},
|
||||
"utls": {
|
||||
"enabled": false,
|
||||
"fingerprint": ""
|
||||
}
|
||||
"certificate_path": ""
|
||||
}
|
||||
```
|
||||
|
||||
@@ -161,48 +155,8 @@ The server private key, in PEM format.
|
||||
|
||||
The path to the server private key, in PEM format.
|
||||
|
||||
#### ech
|
||||
|
||||
==Client only==
|
||||
|
||||
!!! warning ""
|
||||
|
||||
ECH is not included by default, see [Installation](/#installation).
|
||||
|
||||
ECH (Encrypted Client Hello) is a TLS extension that allows a client to encrypt the first part of its ClientHello
|
||||
message.
|
||||
|
||||
If you don't know how to fill in the other configuration, just set `enabled`.
|
||||
|
||||
#### utls
|
||||
|
||||
==Client only==
|
||||
|
||||
!!! warning ""
|
||||
|
||||
uTLS is not included by default, see [Installation](/#installation).
|
||||
|
||||
!!! note ""
|
||||
|
||||
uTLS is poorly maintained and the effect may be unproven, use at your own risk.
|
||||
|
||||
uTLS is a fork of "crypto/tls", which provides ClientHello fingerprinting resistance.
|
||||
|
||||
Available fingerprint values:
|
||||
|
||||
* chrome
|
||||
* firefox
|
||||
* ios
|
||||
* android
|
||||
* random
|
||||
|
||||
|
||||
### ACME Fields
|
||||
|
||||
!!! warning ""
|
||||
|
||||
ACME is not included by default, see [Installation](/#installation).
|
||||
|
||||
#### domain
|
||||
|
||||
List of domain.
|
||||
@@ -251,6 +205,10 @@ listener for the HTTP challenge.
|
||||
The alternate port to use for the ACME TLS-ALPN challenge; the system must forward 443 to this port for challenge to
|
||||
succeed.
|
||||
|
||||
### Reload
|
||||
|
||||
For server configuration, certificate and key will be automatically reloaded if modified.
|
||||
|
||||
#### external_account
|
||||
|
||||
EAB (External Account Binding) contains information necessary to bind or map an ACME account to some other account known
|
||||
@@ -268,8 +226,4 @@ The key identifier.
|
||||
|
||||
#### external_account.mac_key
|
||||
|
||||
The MAC key.
|
||||
|
||||
### Reload
|
||||
|
||||
For server configuration, certificate and key will be automatically reloaded if modified.
|
||||
The MAC key.
|
||||
@@ -30,6 +30,10 @@
|
||||
}
|
||||
```
|
||||
|
||||
!!! warning ""
|
||||
|
||||
默认安装不包含 ACME,参阅 [安装](/zh/#_2)。
|
||||
|
||||
### 出站
|
||||
|
||||
```json
|
||||
@@ -43,17 +47,7 @@
|
||||
"max_version": "",
|
||||
"cipher_suites": [],
|
||||
"certificate": "",
|
||||
"certificate_path": "",
|
||||
"ech": {
|
||||
"enabled": false,
|
||||
"pq_signature_schemes_enabled": false,
|
||||
"dynamic_record_sizing_disabled": false,
|
||||
"config": ""
|
||||
},
|
||||
"utls": {
|
||||
"enabled": false,
|
||||
"fingerprint": ""
|
||||
}
|
||||
"certificate_path": ""
|
||||
}
|
||||
```
|
||||
|
||||
@@ -161,47 +155,8 @@ TLS 版本值:
|
||||
|
||||
服务器 PEM 私钥路径。
|
||||
|
||||
#### ech
|
||||
|
||||
==仅客户端==
|
||||
|
||||
!!! warning ""
|
||||
|
||||
默认安装不包含 ECH, 参阅 [安装](/zh/#_2)。
|
||||
|
||||
ECH (Encrypted Client Hello) 是一个 TLS 扩展,它允许客户端加密其 ClientHello 的第一部分
|
||||
信息。
|
||||
|
||||
如果您不知道如何填写其他配置,只需设置 `enabled` 即可。
|
||||
|
||||
#### utls
|
||||
|
||||
==仅客户端==
|
||||
|
||||
!!! warning ""
|
||||
|
||||
默认安装不包含 uTLS, 参阅 [安装](/zh/#_2)。
|
||||
|
||||
!!! note ""
|
||||
|
||||
uTLS 维护不善且其效果可能未经证实,使用风险自负。
|
||||
|
||||
uTLS 是 "crypto/tls" 的一个分支,它提供了 ClientHello 指纹识别阻力。
|
||||
|
||||
可用的指纹值:
|
||||
|
||||
* chrome
|
||||
* firefox
|
||||
* ios
|
||||
* android
|
||||
* random
|
||||
|
||||
### ACME 字段
|
||||
|
||||
!!! warning ""
|
||||
|
||||
默认安装不包含 ACME,参阅 [安装](/zh/#_2)。
|
||||
|
||||
#### domain
|
||||
|
||||
一组域名。
|
||||
@@ -248,6 +203,10 @@ ACME 数据目录。
|
||||
|
||||
用于 ACME TLS-ALPN 质询的备用端口; 系统必须将 443 转发到此端口以使质询成功。
|
||||
|
||||
### Reload
|
||||
|
||||
对于服务器配置,如果修改,证书和密钥将自动重新加载。
|
||||
|
||||
#### external_account
|
||||
|
||||
EAB(外部帐户绑定)包含将 ACME 帐户绑定或映射到其他已知帐户所需的信息由 CA。
|
||||
@@ -263,8 +222,4 @@ EAB(外部帐户绑定)包含将 ACME 帐户绑定或映射到其他已知
|
||||
|
||||
#### external_account.mac_key
|
||||
|
||||
MAC 密钥。
|
||||
|
||||
### 重载
|
||||
|
||||
对于服务器配置,如果修改,证书和密钥将自动重新加载。
|
||||
MAC 密钥。
|
||||
@@ -27,8 +27,6 @@ go install -v -tags with_clash_api github.com/sagernet/sing-box/cmd/sing-box@lat
|
||||
| `with_quic` | Build with QUIC support, see [QUIC and HTTP3 dns transports](./configuration/dns/server), [Naive inbound](./configuration/inbound/naive), [Hysteria Inbound](./configuration/inbound/hysteria), [Hysteria Outbound](./configuration/outbound/hysteria) and [V2Ray Transport#QUIC](./configuration/shared/v2ray-transport#quic). |
|
||||
| `with_grpc` | Build with standard gRPC support, see [V2Ray Transport#gRPC](./configuration/shared/v2ray-transport#grpc). |
|
||||
| `with_wireguard` | Build with WireGuard support, see [WireGuard outbound](./configuration/outbound/wireguard). |
|
||||
| `with_ech` | Build with TLS ECH extension support for TLS outbound, see [TLS](./configuration/shared/tls#ech). |
|
||||
| `with_utls` | Build with [uTLS](https://github.com/refraction-networking/utls) support for TLS outbound, see [TLS](./configuration/shared/tls#utls). |
|
||||
| `with_acme` | Build with ACME TLS certificate issuer support, see [TLS](./configuration/shared/tls). |
|
||||
| `with_clash_api` | Build with Clash API support, see [Experimental](./configuration/experimental#clash-api-fields). |
|
||||
| `no_gvisor` | Build without gVisor Tun stack support, see [Tun inbound](./configuration/inbound/tun#stack). |
|
||||
|
||||
@@ -27,8 +27,6 @@ go install -v -tags with_clash_api github.com/sagernet/sing-box/cmd/sing-box@lat
|
||||
| `with_quic` | 启用 QUIC 支持,参阅 [QUIC 和 HTTP3 DNS 传输层](./configuration/dns/server),[Naive 入站](./configuration/inbound/naive),[Hysteria 入站](./configuration/inbound/hysteria),[Hysteria 出站](./configuration/outbound/hysteria) 和 [V2Ray 传输层#QUIC](./configuration/shared/v2ray-transport#quic)。 |
|
||||
| `with_grpc` | 启用标准 gRPC 支持,参阅 [V2Ray 传输层#gRPC](./configuration/shared/v2ray-transport#grpc)。 |
|
||||
| `with_wireguard` | 启用 WireGuard 支持,参阅 [WireGuard 出站](./configuration/outbound/wireguard)。 |
|
||||
| `with_ech` | 启用 TLS ECH 扩展支持,参阅 [TLS](./configuration/shared/tls#ech)。 |
|
||||
| `with_utls` | 启用 [uTLS](https://github.com/refraction-networking/utls) 支持, 参阅 [TLS](./configuration/shared/tls#utls)。 |
|
||||
| `with_acme` | 启用 ACME TLS 证书签发支持,参阅 [TLS](./configuration/shared/tls)。 |
|
||||
| `with_clash_api` | 启用 Clash api 支持,参阅 [实验性](./configuration/experimental#clash-api-fields)。 |
|
||||
| `no_gvisor` | 禁用 gVisor Tun 栈支持,参阅 [Tun 入站](./configuration/inbound/tun#stack)。 |
|
||||
|
||||
@@ -10,5 +10,5 @@ import (
|
||||
)
|
||||
|
||||
func NewClashServer(router adapter.Router, logFactory log.ObservableFactory, options option.ClashAPIOptions) (adapter.ClashServer, error) {
|
||||
return clashapi.NewServer(router, logFactory, options)
|
||||
return clashapi.NewServer(router, logFactory, options), nil
|
||||
}
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
package cachefile
|
||||
|
||||
import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
|
||||
"go.etcd.io/bbolt"
|
||||
)
|
||||
|
||||
var bucketSelected = []byte("selected")
|
||||
|
||||
var _ adapter.ClashCacheFile = (*CacheFile)(nil)
|
||||
|
||||
type CacheFile struct {
|
||||
DB *bbolt.DB
|
||||
}
|
||||
|
||||
func Open(path string) (*CacheFile, error) {
|
||||
const fileMode = 0o666
|
||||
options := bbolt.Options{Timeout: time.Second}
|
||||
db, err := bbolt.Open(path, fileMode, &options)
|
||||
switch err {
|
||||
case bbolt.ErrInvalid, bbolt.ErrChecksum, bbolt.ErrVersionMismatch:
|
||||
if err = os.Remove(path); err != nil {
|
||||
break
|
||||
}
|
||||
db, err = bbolt.Open(path, 0o666, &options)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &CacheFile{db}, nil
|
||||
}
|
||||
|
||||
func (c *CacheFile) LoadSelected(group string) string {
|
||||
var selected string
|
||||
c.DB.View(func(t *bbolt.Tx) error {
|
||||
bucket := t.Bucket(bucketSelected)
|
||||
if bucket == nil {
|
||||
return nil
|
||||
}
|
||||
selectedBytes := bucket.Get([]byte(group))
|
||||
if len(selectedBytes) > 0 {
|
||||
selected = string(selectedBytes)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return selected
|
||||
}
|
||||
|
||||
func (c *CacheFile) StoreSelected(group, selected string) error {
|
||||
return c.DB.Batch(func(t *bbolt.Tx) error {
|
||||
bucket, err := t.CreateBucketIfNotExists(bucketSelected)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return bucket.Put([]byte(group), []byte(selected))
|
||||
})
|
||||
}
|
||||
@@ -2,7 +2,6 @@ package clashapi
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing-box/log"
|
||||
|
||||
@@ -10,11 +9,11 @@ import (
|
||||
"github.com/go-chi/render"
|
||||
)
|
||||
|
||||
func configRouter(server *Server, logFactory log.Factory, logger log.Logger) http.Handler {
|
||||
func configRouter(logFactory log.Factory) http.Handler {
|
||||
r := chi.NewRouter()
|
||||
r.Get("/", getConfigs(server, logFactory))
|
||||
r.Get("/", getConfigs(logFactory))
|
||||
r.Put("/", updateConfigs)
|
||||
r.Patch("/", patchConfigs(server, logger))
|
||||
r.Patch("/", patchConfigs)
|
||||
return r
|
||||
}
|
||||
|
||||
@@ -32,7 +31,7 @@ type configSchema struct {
|
||||
Tun map[string]any `json:"tun"`
|
||||
}
|
||||
|
||||
func getConfigs(server *Server, logFactory log.Factory) func(w http.ResponseWriter, r *http.Request) {
|
||||
func getConfigs(logFactory log.Factory) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
logLevel := logFactory.Level()
|
||||
if logLevel == log.LevelTrace {
|
||||
@@ -41,31 +40,15 @@ func getConfigs(server *Server, logFactory log.Factory) func(w http.ResponseWrit
|
||||
logLevel = log.LevelError
|
||||
}
|
||||
render.JSON(w, r, &configSchema{
|
||||
Mode: server.mode,
|
||||
Mode: "rule",
|
||||
BindAddress: "*",
|
||||
LogLevel: log.FormatLevel(logLevel),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func patchConfigs(server *Server, logger log.Logger) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var newConfig configSchema
|
||||
err := render.DecodeJSON(r.Body, &newConfig)
|
||||
if err != nil {
|
||||
render.Status(r, http.StatusBadRequest)
|
||||
render.JSON(w, r, ErrBadRequest)
|
||||
return
|
||||
}
|
||||
if newConfig.Mode != "" {
|
||||
mode := strings.ToLower(newConfig.Mode)
|
||||
if server.mode != mode {
|
||||
server.mode = mode
|
||||
logger.Info("updated mode: ", mode)
|
||||
}
|
||||
}
|
||||
render.NoContent(w, r)
|
||||
}
|
||||
func patchConfigs(w http.ResponseWriter, r *http.Request) {
|
||||
render.NoContent(w, r)
|
||||
}
|
||||
|
||||
func updateConfigs(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
@@ -14,7 +14,6 @@ import (
|
||||
"github.com/sagernet/sing-box/common/json"
|
||||
"github.com/sagernet/sing-box/common/urltest"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/experimental/clashapi/cachefile"
|
||||
"github.com/sagernet/sing-box/experimental/clashapi/trafficontrol"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
@@ -38,12 +37,9 @@ type Server struct {
|
||||
trafficManager *trafficontrol.Manager
|
||||
urlTestHistory *urltest.HistoryStorage
|
||||
tcpListener net.Listener
|
||||
mode string
|
||||
storeSelected bool
|
||||
cacheFile adapter.ClashCacheFile
|
||||
}
|
||||
|
||||
func NewServer(router adapter.Router, logFactory log.ObservableFactory, options option.ClashAPIOptions) (*Server, error) {
|
||||
func NewServer(router adapter.Router, logFactory log.ObservableFactory, options option.ClashAPIOptions) *Server {
|
||||
trafficManager := trafficontrol.NewManager()
|
||||
chiRouter := chi.NewRouter()
|
||||
server := &Server{
|
||||
@@ -55,21 +51,6 @@ func NewServer(router adapter.Router, logFactory log.ObservableFactory, options
|
||||
},
|
||||
trafficManager: trafficManager,
|
||||
urlTestHistory: urltest.NewHistoryStorage(),
|
||||
mode: strings.ToLower(options.DefaultMode),
|
||||
}
|
||||
if server.mode == "" {
|
||||
server.mode = "rule"
|
||||
}
|
||||
if options.StoreSelected {
|
||||
cachePath := os.ExpandEnv(options.CacheFile)
|
||||
if cachePath == "" {
|
||||
cachePath = "cache.db"
|
||||
}
|
||||
cacheFile, err := cachefile.Open(cachePath)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "open cache file")
|
||||
}
|
||||
server.cacheFile = cacheFile
|
||||
}
|
||||
cors := cors.New(cors.Options{
|
||||
AllowedOrigins: []string{"*"},
|
||||
@@ -84,7 +65,7 @@ func NewServer(router adapter.Router, logFactory log.ObservableFactory, options
|
||||
r.Get("/logs", getLogs(logFactory))
|
||||
r.Get("/traffic", traffic(trafficManager))
|
||||
r.Get("/version", version)
|
||||
r.Mount("/configs", configRouter(server, logFactory, server.logger))
|
||||
r.Mount("/configs", configRouter(logFactory))
|
||||
r.Mount("/proxies", proxyRouter(server, router))
|
||||
r.Mount("/rules", ruleRouter(router))
|
||||
r.Mount("/connections", connectionRouter(trafficManager))
|
||||
@@ -103,7 +84,7 @@ func NewServer(router adapter.Router, logFactory log.ObservableFactory, options
|
||||
})
|
||||
})
|
||||
}
|
||||
return server, nil
|
||||
return server
|
||||
}
|
||||
|
||||
func (s *Server) Start() error {
|
||||
@@ -127,22 +108,9 @@ func (s *Server) Close() error {
|
||||
common.PtrOrNil(s.httpServer),
|
||||
s.tcpListener,
|
||||
s.trafficManager,
|
||||
s.cacheFile,
|
||||
)
|
||||
}
|
||||
|
||||
func (s *Server) Mode() string {
|
||||
return s.mode
|
||||
}
|
||||
|
||||
func (s *Server) StoreSelected() bool {
|
||||
return s.storeSelected
|
||||
}
|
||||
|
||||
func (s *Server) CacheFile() adapter.ClashCacheFile {
|
||||
return s.cacheFile
|
||||
}
|
||||
|
||||
func (s *Server) RoutedConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, matchedRule adapter.Rule) (net.Conn, adapter.Tracker) {
|
||||
tracker := trafficontrol.NewTCPTracker(conn, s.trafficManager, castMetadata(metadata), s.router, matchedRule)
|
||||
return tracker, tracker
|
||||
|
||||
20
go.mod
20
go.mod
@@ -4,7 +4,6 @@ go 1.18
|
||||
|
||||
require (
|
||||
berty.tech/go-libtor v1.0.385
|
||||
github.com/cloudflare/circl v1.2.1-0.20220831060716-4cf0150356fc
|
||||
github.com/cretz/bine v0.2.0
|
||||
github.com/database64128/tfo-go v1.1.2
|
||||
github.com/dustin/go-humanize v1.0.0
|
||||
@@ -12,49 +11,44 @@ require (
|
||||
github.com/go-chi/chi/v5 v5.0.7
|
||||
github.com/go-chi/cors v1.2.1
|
||||
github.com/go-chi/render v1.0.2
|
||||
github.com/gofrs/uuid v4.3.0+incompatible
|
||||
github.com/gofrs/uuid v4.2.0+incompatible
|
||||
github.com/gorilla/websocket v1.5.0
|
||||
github.com/hashicorp/yamux v0.1.1
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible
|
||||
github.com/mholt/acmez v1.0.4
|
||||
github.com/miekg/dns v1.1.50
|
||||
github.com/oschwald/maxminddb-golang v1.10.0
|
||||
github.com/pires/go-proxyproto v0.6.2
|
||||
github.com/refraction-networking/utls v1.1.2
|
||||
github.com/sagernet/certmagic v0.0.0-20220819042630-4a57f8b6853a
|
||||
github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb
|
||||
github.com/sagernet/sing v0.0.0-20220910144724-62c4ebdbcb3f
|
||||
github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666
|
||||
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6
|
||||
github.com/sagernet/sing-tun v0.0.0-20220911034209-c7dd5d457e24
|
||||
github.com/sagernet/sing-tun v0.0.0-20220828031750-185b6c880a83
|
||||
github.com/sagernet/sing-vmess v0.0.0-20220907073918-72d7fdf6825f
|
||||
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195
|
||||
github.com/spf13/cobra v1.5.0
|
||||
github.com/stretchr/testify v1.8.0
|
||||
go.etcd.io/bbolt v1.3.6
|
||||
go.uber.org/atomic v1.10.0
|
||||
go4.org/netipx v0.0.0-20220812043211-3cc044ffd68d
|
||||
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90
|
||||
golang.org/x/net v0.0.0-20220909164309-bea034e7d591
|
||||
golang.org/x/sys v0.0.0-20220909162455-aba9fc2a8ff2
|
||||
golang.zx2c4.com/wireguard v0.0.0-20220904105730-b51010ba13f0
|
||||
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b
|
||||
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261
|
||||
golang.zx2c4.com/wireguard v0.0.0-20220829161405-d1d08426b27b
|
||||
google.golang.org/grpc v1.49.0
|
||||
google.golang.org/protobuf v1.28.1
|
||||
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c
|
||||
gvisor.dev/gvisor v0.0.0-20220819163037-ba6e795b139a
|
||||
)
|
||||
|
||||
//replace github.com/sagernet/sing => ../sing
|
||||
|
||||
require (
|
||||
github.com/ajg/form v1.5.1 // indirect
|
||||
github.com/andybalholm/brotli v1.0.4 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
||||
github.com/golang/mock v1.6.0 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/btree v1.0.1 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
github.com/klauspost/compress v1.13.6 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.1.0 // indirect
|
||||
github.com/libdns/libdns v0.2.1 // indirect
|
||||
github.com/marten-seemann/qpack v0.2.1 // indirect
|
||||
@@ -65,7 +59,7 @@ require (
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e // indirect
|
||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect
|
||||
github.com/sagernet/netlink v0.0.0-20220826133217-3fb4ff92ea17 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
|
||||
47
go.sum
47
go.sum
@@ -5,15 +5,11 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
|
||||
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudflare/circl v1.2.1-0.20220831060716-4cf0150356fc h1:307gdRLiZ08dwOIKwc5lAQ19DRFaQQvdhHalyB4Asx8=
|
||||
github.com/cloudflare/circl v1.2.1-0.20220831060716-4cf0150356fc/go.mod h1:+CauBF6R70Jqcyl8N2hC8pAXYbWkGIezuSbuGLtRhnw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
@@ -47,8 +43,8 @@ github.com/go-chi/render v1.0.2 h1:4ER/udB0+fMWB2Jlf15RV3F4A2FDuYi/9f+lFttR/Lg=
|
||||
github.com/go-chi/render v1.0.2/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/gofrs/uuid v4.3.0+incompatible h1:CaSVZxm5B+7o45rtab4jC2G37WGYX1zQfuU2i6DSvnc=
|
||||
github.com/gofrs/uuid v4.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0=
|
||||
github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||
@@ -85,8 +81,6 @@ github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbg
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
|
||||
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.1.0 h1:eyi1Ad2aNJMW95zcSbmGg7Cg6cq3ADwLpMAP96d8rF0=
|
||||
github.com/klauspost/cpuid/v2 v2.1.0/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||
@@ -107,8 +101,6 @@ github.com/marten-seemann/qtls-go1-19 v0.1.0 h1:rLFKD/9mp/uq1SYGYuVZhm83wkmU95pK
|
||||
github.com/marten-seemann/qtls-go1-19 v0.1.0/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI=
|
||||
github.com/mholt/acmez v1.0.4 h1:N3cE4Pek+dSolbsofIkAYz6H1d3pE+2G0os7QHslf80=
|
||||
github.com/mholt/acmez v1.0.4/go.mod h1:qFGLZ4u+ehWINeJZjzPlsnjJBCPAADWTcIqE/7DAYQY=
|
||||
github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
|
||||
github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
@@ -129,8 +121,6 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/refraction-networking/utls v1.1.2 h1:a7GQauRt72VG+wtNm0lnrAaCGlyX47gEi1++dSsDBpw=
|
||||
github.com/refraction-networking/utls v1.1.2/go.mod h1:+D89TUtA8+NKVFj1IXWr0p3tSdX1+SqUB7rL0QnGqyg=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e h1:5CFRo8FJbCuf5s/eTBdZpmMbn8Fe2eSMLNAYfKanA34=
|
||||
@@ -139,8 +129,8 @@ github.com/sagernet/certmagic v0.0.0-20220819042630-4a57f8b6853a h1:SE3Xn4GOQ+kx
|
||||
github.com/sagernet/certmagic v0.0.0-20220819042630-4a57f8b6853a/go.mod h1:Q+ZXyesnkjV5B70B1ixk65ecKrlJ2jz0atv3fPKsVVo=
|
||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA=
|
||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h1:QUQ4RRHD6hGGHdFMEtR8T2P6GS6R3D/CXKdaYHKKXms=
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
||||
github.com/sagernet/netlink v0.0.0-20220826133217-3fb4ff92ea17 h1:zvm6IrIgo4rLizJCHkH+SWUBhm+jyjjozX031QdAlj8=
|
||||
github.com/sagernet/netlink v0.0.0-20220826133217-3fb4ff92ea17/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
||||
github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb h1:wc0yQ+SBn4TaTYRwpwvEm3nc4eRdxk6vtRbouLVZAzk=
|
||||
github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb/go.mod h1:MIccjRKnPTjWwAOpl+AUGWOkzyTd9tERytudxu+1ra4=
|
||||
github.com/sagernet/sing v0.0.0-20220812082120-05f9836bff8f/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
|
||||
@@ -151,8 +141,8 @@ github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666 h1:XUTocA/Ek0dFx
|
||||
github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666/go.mod h1:eDyH7AJmqBGjZQdQmpZIzlbTREudZuWDExMuGKgjRVM=
|
||||
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6 h1:JJfDeYYhWunvtxsU/mOVNTmFQmnzGx9dY034qG6G3g4=
|
||||
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6/go.mod h1:EX3RbZvrwAkPI2nuGa78T2iQXmrkT+/VQtskjou42xM=
|
||||
github.com/sagernet/sing-tun v0.0.0-20220911034209-c7dd5d457e24 h1:LsmPeFvj4GhiV5Y7Rm8I845XysdxVN4MQmfZ36P5bmw=
|
||||
github.com/sagernet/sing-tun v0.0.0-20220911034209-c7dd5d457e24/go.mod h1:5AhPUv9jWDQ3pv3Mj78SL/1TSjhoaj6WNASxRKLqXqM=
|
||||
github.com/sagernet/sing-tun v0.0.0-20220828031750-185b6c880a83 h1:SoWiHYuOCVedqA7T/CJSZUUrcPGKQb2wFKEq8DphiAI=
|
||||
github.com/sagernet/sing-tun v0.0.0-20220828031750-185b6c880a83/go.mod h1:76r07HS1WRcEI4mE9pFsohfTBUt1j/G9Avz6DaOP3VU=
|
||||
github.com/sagernet/sing-vmess v0.0.0-20220907073918-72d7fdf6825f h1:6l9aXZqAl1JqXJWi89KHpWnM/moQUPGG+XiwMc+yD0A=
|
||||
github.com/sagernet/sing-vmess v0.0.0-20220907073918-72d7fdf6825f/go.mod h1:u66Vv7NHXJWfeAmhh7JuJp/cwxmuQlM56QoZ7B7Mmd0=
|
||||
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195 h1:5VBIbVw9q7aKbrFdT83mjkyvQ+VaRsQ6yflTepfln38=
|
||||
@@ -173,8 +163,6 @@ github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695AP
|
||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
|
||||
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
|
||||
@@ -193,7 +181,6 @@ golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaE
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM=
|
||||
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
@@ -224,11 +211,9 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211111160137-58aab5ef257a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220630215102-69896b714898/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20220909164309-bea034e7d591 h1:D0B/7al0LLrVC8aWF4+oxpv/m8bc7ViFfVS8/gXGdqI=
|
||||
golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b h1:ZmngSVLe/wycRns9MKikG9OWIEjGcGAkacif7oYQaUY=
|
||||
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -236,7 +221,6 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -249,7 +233,6 @@ golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -257,13 +240,12 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220909162455-aba9fc2a8ff2 h1:wM1k/lXfpc5HdkJJyW9GELpd8ERGdnh8sMGL6Gzq3Ho=
|
||||
golang.org/x/sys v0.0.0-20220909162455-aba9fc2a8ff2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 h1:v6hYoSR9T5oet+pMXwUWkbiVqx/63mlHjefrHmxwfeY=
|
||||
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
@@ -286,7 +268,6 @@ golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapK
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.11-0.20220513221640-090b14e8501f h1:OKYpQQVE3DKSc3r3zHVzq46vq5YH7x8xpR3/k9ixmUg=
|
||||
golang.org/x/tools v0.1.11-0.20220513221640-090b14e8501f/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -295,8 +276,8 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 h1:Ug9qvr1myri/zFN6xL17LSCBGFDnphBBhzmILHsM5TY=
|
||||
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20220904105730-b51010ba13f0 h1:5ZkdpbduT/g+9OtbSDvbF3KvfQG45CtH/ppO8FUmvCQ=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20220904105730-b51010ba13f0/go.mod h1:enML0deDxY1ux+B6ANGiwtg0yAJi1rctkTpcHNAVPyg=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20220829161405-d1d08426b27b h1:qgrKnOfe1zyURRNdmDlGbN32i38Zjmw0B1+TMdHcOvg=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20220829161405-d1d08426b27b/go.mod h1:6y4CqPAy54NwiN4nC8K+R1eMpQDB1P2d25qmunh2RSA=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
@@ -345,8 +326,8 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c h1:m5lcgWnL3OElQNVyp3qcncItJ2c0sQlSGjYK2+nJTA4=
|
||||
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM=
|
||||
gvisor.dev/gvisor v0.0.0-20220819163037-ba6e795b139a h1:W1h3JsEzYWg7eD4908iHv49p7AOx7JPKsoh/fsxgylM=
|
||||
gvisor.dev/gvisor v0.0.0-20220819163037-ba6e795b139a/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0=
|
||||
|
||||
@@ -29,7 +29,7 @@ func (a *myInboundAdapter) ListenTCP() (net.Listener, error) {
|
||||
}
|
||||
if a.listenOptions.ProxyProtocol {
|
||||
a.logger.Debug("proxy protocol enabled")
|
||||
tcpListener = &proxyproto.Listener{Listener: tcpListener, AcceptNoHeader: a.listenOptions.ProxyProtocolAcceptNoHeader}
|
||||
tcpListener = &proxyproto.Listener{Listener: tcpListener}
|
||||
}
|
||||
a.tcpListener = tcpListener
|
||||
return tcpListener, err
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"github.com/sagernet/sing-dns"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/control"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
@@ -18,15 +17,11 @@ import (
|
||||
|
||||
func (a *myInboundAdapter) ListenUDP() (net.PacketConn, error) {
|
||||
bindAddr := M.SocksaddrFrom(netip.Addr(a.listenOptions.Listen), a.listenOptions.ListenPort)
|
||||
var lc net.ListenConfig
|
||||
if !a.listenOptions.UDPFragment {
|
||||
lc.Control = control.Append(lc.Control, control.DisableUDPFragment())
|
||||
}
|
||||
udpConn, err := lc.ListenPacket(a.ctx, M.NetworkFromNetAddr(N.NetworkUDP, bindAddr.Addr), bindAddr.String())
|
||||
udpConn, err := net.ListenUDP(M.NetworkFromNetAddr(N.NetworkUDP, bindAddr.Addr), bindAddr.UDPAddr())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
a.udpConn = udpConn.(*net.UDPConn)
|
||||
a.udpConn = udpConn
|
||||
a.udpAddr = bindAddr
|
||||
a.logger.Info("udp server started at ", udpConn.LocalAddr())
|
||||
return udpConn, err
|
||||
|
||||
@@ -3,11 +3,11 @@ package inbound
|
||||
import (
|
||||
std_bufio "bufio"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
@@ -26,7 +26,7 @@ var (
|
||||
type HTTP struct {
|
||||
myInboundAdapter
|
||||
authenticator auth.Authenticator
|
||||
tlsConfig tls.ServerConfig
|
||||
tlsConfig *TLSConfig
|
||||
}
|
||||
|
||||
func NewHTTP(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.HTTPMixedInboundOptions) (*HTTP, error) {
|
||||
@@ -44,7 +44,7 @@ func NewHTTP(ctx context.Context, router adapter.Router, logger log.ContextLogge
|
||||
authenticator: auth.NewAuthenticator(options.Users),
|
||||
}
|
||||
if options.TLS != nil {
|
||||
tlsConfig, err := tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS))
|
||||
tlsConfig, err := NewTLSConfig(ctx, logger, common.PtrValueOrDefault(options.TLS))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -67,13 +67,13 @@ func (h *HTTP) Start() error {
|
||||
func (h *HTTP) Close() error {
|
||||
return common.Close(
|
||||
&h.myInboundAdapter,
|
||||
h.tlsConfig,
|
||||
common.PtrOrNil(h.tlsConfig),
|
||||
)
|
||||
}
|
||||
|
||||
func (h *HTTP) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
if h.tlsConfig != nil {
|
||||
conn = h.tlsConfig.Server(conn)
|
||||
conn = tls.Server(conn, h.tlsConfig.Config())
|
||||
}
|
||||
return http.HandleConnection(ctx, conn, std_bufio.NewReader(conn), h.authenticator, h.upstreamUserHandler(metadata), adapter.UpstreamMetadata(metadata))
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"github.com/sagernet/quic-go"
|
||||
"github.com/sagernet/quic-go/congestion"
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
@@ -27,7 +26,7 @@ var _ adapter.Inbound = (*Hysteria)(nil)
|
||||
type Hysteria struct {
|
||||
myInboundAdapter
|
||||
quicConfig *quic.Config
|
||||
tlsConfig tls.ServerConfig
|
||||
tlsConfig *TLSConfig
|
||||
authKey []byte
|
||||
xplusKey []byte
|
||||
sendBPS uint64
|
||||
@@ -117,7 +116,7 @@ func NewHysteria(ctx context.Context, router adapter.Router, logger log.ContextL
|
||||
if len(options.TLS.ALPN) == 0 {
|
||||
options.TLS.ALPN = []string{hysteria.DefaultALPN}
|
||||
}
|
||||
tlsConfig, err := tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS))
|
||||
tlsConfig, err := NewTLSConfig(ctx, logger, common.PtrValueOrDefault(options.TLS))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -138,11 +137,7 @@ func (h *Hysteria) Start() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rawConfig, err := h.tlsConfig.Config()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
listener, err := quic.Listen(packetConn, rawConfig, h.quicConfig)
|
||||
listener, err := quic.Listen(packetConn, h.tlsConfig.Config(), h.quicConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -306,6 +301,6 @@ func (h *Hysteria) Close() error {
|
||||
return common.Close(
|
||||
&h.myInboundAdapter,
|
||||
h.listener,
|
||||
h.tlsConfig,
|
||||
common.PtrOrNil(h.tlsConfig),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package inbound
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
@@ -13,7 +14,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
@@ -32,7 +32,7 @@ var _ adapter.Inbound = (*Naive)(nil)
|
||||
type Naive struct {
|
||||
myInboundAdapter
|
||||
authenticator auth.Authenticator
|
||||
tlsConfig tls.ServerConfig
|
||||
tlsConfig *TLSConfig
|
||||
httpServer *http.Server
|
||||
h3Server any
|
||||
}
|
||||
@@ -59,7 +59,7 @@ func NewNaive(ctx context.Context, router adapter.Router, logger log.ContextLogg
|
||||
return nil, E.New("missing users")
|
||||
}
|
||||
if options.TLS != nil {
|
||||
tlsConfig, err := tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS))
|
||||
tlsConfig, err := NewTLSConfig(ctx, logger, common.PtrValueOrDefault(options.TLS))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -69,16 +69,13 @@ func NewNaive(ctx context.Context, router adapter.Router, logger log.ContextLogg
|
||||
}
|
||||
|
||||
func (n *Naive) Start() error {
|
||||
var tlsConfig *tls.STDConfig
|
||||
var tlsConfig *tls.Config
|
||||
if n.tlsConfig != nil {
|
||||
err := n.tlsConfig.Start()
|
||||
if err != nil {
|
||||
return E.Cause(err, "create TLS config")
|
||||
}
|
||||
tlsConfig, err = n.tlsConfig.Config()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tlsConfig = n.tlsConfig.Config()
|
||||
}
|
||||
|
||||
if common.Contains(n.network, N.NetworkTCP) {
|
||||
@@ -120,7 +117,7 @@ func (n *Naive) Close() error {
|
||||
&n.myInboundAdapter,
|
||||
common.PtrOrNil(n.httpServer),
|
||||
n.h3Server,
|
||||
n.tlsConfig,
|
||||
common.PtrOrNil(n.tlsConfig),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -8,13 +8,9 @@ import (
|
||||
)
|
||||
|
||||
func (n *Naive) configureHTTP3Listener() error {
|
||||
tlsConfig, err := n.tlsConfig.Config()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h3Server := &http3.Server{
|
||||
Port: int(n.listenOptions.ListenPort),
|
||||
TLSConfig: tlsConfig,
|
||||
TLSConfig: n.tlsConfig.Config(),
|
||||
Handler: n,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
package tls
|
||||
package inbound
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
@@ -15,7 +14,9 @@ import (
|
||||
"github.com/fsnotify/fsnotify"
|
||||
)
|
||||
|
||||
type STDServerConfig struct {
|
||||
var _ adapter.Service = (*TLSConfig)(nil)
|
||||
|
||||
type TLSConfig struct {
|
||||
config *tls.Config
|
||||
logger log.Logger
|
||||
acmeService adapter.Service
|
||||
@@ -26,15 +27,105 @@ type STDServerConfig struct {
|
||||
watcher *fsnotify.Watcher
|
||||
}
|
||||
|
||||
func (c *STDServerConfig) NextProtos() []string {
|
||||
return c.config.NextProtos
|
||||
func (c *TLSConfig) Config() *tls.Config {
|
||||
return c.config
|
||||
}
|
||||
|
||||
func (c *STDServerConfig) SetNextProtos(nextProto []string) {
|
||||
c.config.NextProtos = nextProto
|
||||
func (c *TLSConfig) Start() error {
|
||||
if c.acmeService != nil {
|
||||
return c.acmeService.Start()
|
||||
} else {
|
||||
if c.certificatePath == "" && c.keyPath == "" {
|
||||
return nil
|
||||
}
|
||||
err := c.startWatcher()
|
||||
if err != nil {
|
||||
c.logger.Warn("create fsnotify watcher: ", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func newSTDServer(ctx context.Context, logger log.Logger, options option.InboundTLSOptions) (ServerConfig, error) {
|
||||
func (c *TLSConfig) 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 *TLSConfig) 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 *TLSConfig) 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 := tls.X509KeyPair(c.certificate, c.key)
|
||||
if err != nil {
|
||||
return E.Cause(err, "reload key pair")
|
||||
}
|
||||
c.config.Certificates = []tls.Certificate{keyPair}
|
||||
c.logger.Info("reloaded TLS certificate")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *TLSConfig) Close() error {
|
||||
if c.acmeService != nil {
|
||||
return c.acmeService.Close()
|
||||
}
|
||||
if c.watcher != nil {
|
||||
return c.watcher.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewTLSConfig(ctx context.Context, logger log.Logger, options option.InboundTLSOptions) (*TLSConfig, error) {
|
||||
if !options.Enabled {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -56,14 +147,14 @@ func newSTDServer(ctx context.Context, logger log.Logger, options option.Inbound
|
||||
tlsConfig.NextProtos = append(tlsConfig.NextProtos, options.ALPN...)
|
||||
}
|
||||
if options.MinVersion != "" {
|
||||
minVersion, err := ParseTLSVersion(options.MinVersion)
|
||||
minVersion, err := option.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)
|
||||
maxVersion, err := option.ParseTLSVersion(options.MaxVersion)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse max_version")
|
||||
}
|
||||
@@ -114,7 +205,7 @@ func newSTDServer(ctx context.Context, logger log.Logger, options option.Inbound
|
||||
}
|
||||
tlsConfig.Certificates = []tls.Certificate{keyPair}
|
||||
}
|
||||
return &STDServerConfig{
|
||||
return &TLSConfig{
|
||||
config: tlsConfig,
|
||||
logger: logger,
|
||||
acmeService: acmeService,
|
||||
@@ -124,109 +215,3 @@ func newSTDServer(ctx context.Context, logger log.Logger, options option.Inbound
|
||||
keyPath: options.KeyPath,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *STDServerConfig) Config() (*STDConfig, error) {
|
||||
return c.config, nil
|
||||
}
|
||||
|
||||
func (c *STDServerConfig) Client(conn net.Conn) Conn {
|
||||
return tls.Client(conn, c.config)
|
||||
}
|
||||
|
||||
func (c *STDServerConfig) Server(conn net.Conn) Conn {
|
||||
return tls.Server(conn, c.config)
|
||||
}
|
||||
|
||||
func (c *STDServerConfig) Start() error {
|
||||
if c.acmeService != nil {
|
||||
return c.acmeService.Start()
|
||||
} else {
|
||||
if c.certificatePath == "" && c.keyPath == "" {
|
||||
return nil
|
||||
}
|
||||
err := c.startWatcher()
|
||||
if err != nil {
|
||||
c.logger.Warn("create fsnotify watcher: ", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *STDServerConfig) 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 *STDServerConfig) 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 *STDServerConfig) 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 := tls.X509KeyPair(c.certificate, c.key)
|
||||
if err != nil {
|
||||
return E.Cause(err, "reload key pair")
|
||||
}
|
||||
c.config.Certificates = []tls.Certificate{keyPair}
|
||||
c.logger.Info("reloaded TLS certificate")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *STDServerConfig) Close() error {
|
||||
if c.acmeService != nil {
|
||||
return c.acmeService.Close()
|
||||
}
|
||||
if c.watcher != nil {
|
||||
return c.watcher.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
//go:build with_acme
|
||||
|
||||
package tls
|
||||
package inbound
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -1,6 +1,6 @@
|
||||
//go:build !with_acme
|
||||
|
||||
package tls
|
||||
package inbound
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -2,11 +2,11 @@ package inbound
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
@@ -29,7 +29,7 @@ type Trojan struct {
|
||||
myInboundAdapter
|
||||
service *trojan.Service[int]
|
||||
users []option.TrojanUser
|
||||
tlsConfig tls.ServerConfig
|
||||
tlsConfig *TLSConfig
|
||||
fallbackAddr M.Socksaddr
|
||||
fallbackAddrTLSNextProto map[string]M.Socksaddr
|
||||
transport adapter.V2RayServerTransport
|
||||
@@ -49,7 +49,7 @@ func NewTrojan(ctx context.Context, router adapter.Router, logger log.ContextLog
|
||||
users: options.Users,
|
||||
}
|
||||
if options.TLS != nil {
|
||||
tlsConfig, err := tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS))
|
||||
tlsConfig, err := NewTLSConfig(ctx, logger, common.PtrValueOrDefault(options.TLS))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -89,7 +89,11 @@ func NewTrojan(ctx context.Context, router adapter.Router, logger log.ContextLog
|
||||
return nil, err
|
||||
}
|
||||
if options.Transport != nil {
|
||||
inbound.transport, err = v2ray.NewServerTransport(ctx, common.PtrValueOrDefault(options.Transport), inbound.tlsConfig, adapter.NewUpstreamHandler(adapter.InboundContext{}, inbound.newTransportConnection, nil, nil), inbound)
|
||||
var tlsConfig *tls.Config
|
||||
if inbound.tlsConfig != nil {
|
||||
tlsConfig = inbound.tlsConfig.Config()
|
||||
}
|
||||
inbound.transport, err = v2ray.NewServerTransport(ctx, common.PtrValueOrDefault(options.Transport), tlsConfig, adapter.NewUpstreamHandler(adapter.InboundContext{}, inbound.newTransportConnection, nil, nil), inbound)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "create server transport: ", options.Transport.Type)
|
||||
}
|
||||
@@ -139,7 +143,7 @@ func (h *Trojan) Start() error {
|
||||
func (h *Trojan) Close() error {
|
||||
return common.Close(
|
||||
&h.myInboundAdapter,
|
||||
h.tlsConfig,
|
||||
common.PtrOrNil(h.tlsConfig),
|
||||
h.transport,
|
||||
)
|
||||
}
|
||||
@@ -151,7 +155,7 @@ func (h *Trojan) newTransportConnection(ctx context.Context, conn net.Conn, meta
|
||||
|
||||
func (h *Trojan) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
if h.tlsConfig != nil && h.transport == nil {
|
||||
conn = h.tlsConfig.Server(conn)
|
||||
conn = tls.Server(conn, h.tlsConfig.Config())
|
||||
}
|
||||
return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata))
|
||||
}
|
||||
@@ -178,7 +182,7 @@ func (h *Trojan) newConnection(ctx context.Context, conn net.Conn, metadata adap
|
||||
func (h *Trojan) fallbackConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
var fallbackAddr M.Socksaddr
|
||||
if len(h.fallbackAddrTLSNextProto) > 0 {
|
||||
if tlsConn, loaded := common.Cast[*tls.STDConn](conn); loaded {
|
||||
if tlsConn, loaded := common.Cast[*tls.Conn](conn); loaded {
|
||||
connectionState := tlsConn.ConnectionState()
|
||||
if connectionState.NegotiatedProtocol != "" {
|
||||
if fallbackAddr, loaded = h.fallbackAddrTLSNextProto[connectionState.NegotiatedProtocol]; !loaded {
|
||||
|
||||
@@ -40,11 +40,11 @@ type Tun struct {
|
||||
func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TunInboundOptions) (*Tun, error) {
|
||||
tunName := options.InterfaceName
|
||||
if tunName == "" {
|
||||
tunName = tun.CalculateInterfaceName("")
|
||||
tunName = tun.DefaultInterfaceName()
|
||||
}
|
||||
tunMTU := options.MTU
|
||||
if tunMTU == 0 {
|
||||
tunMTU = 9000
|
||||
tunMTU = 1500
|
||||
}
|
||||
var udpTimeout int64
|
||||
if options.UDPTimeout != 0 {
|
||||
@@ -77,8 +77,8 @@ func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger
|
||||
tunOptions: tun.Options{
|
||||
Name: tunName,
|
||||
MTU: tunMTU,
|
||||
Inet4Address: common.Map(options.Inet4Address, option.ListenPrefix.Build),
|
||||
Inet6Address: common.Map(options.Inet6Address, option.ListenPrefix.Build),
|
||||
Inet4Address: options.Inet4Address.Build(),
|
||||
Inet6Address: options.Inet6Address.Build(),
|
||||
AutoRoute: options.AutoRoute,
|
||||
StrictRoute: options.StrictRoute,
|
||||
IncludeUID: includeUID,
|
||||
@@ -86,8 +86,6 @@ func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger
|
||||
IncludeAndroidUser: options.IncludeAndroidUser,
|
||||
IncludePackage: options.IncludePackage,
|
||||
ExcludePackage: options.ExcludePackage,
|
||||
InterfaceMonitor: router.InterfaceMonitor(),
|
||||
TableIndex: 2022,
|
||||
},
|
||||
endpointIndependentNat: options.EndpointIndependentNat,
|
||||
udpTimeout: udpTimeout,
|
||||
@@ -144,18 +142,7 @@ func (t *Tun) Start() error {
|
||||
return E.Cause(err, "configure tun interface")
|
||||
}
|
||||
t.tunIf = tunIf
|
||||
t.tunStack, err = tun.NewStack(t.stack, tun.StackOptions{
|
||||
Context: t.ctx,
|
||||
Tun: tunIf,
|
||||
MTU: t.tunOptions.MTU,
|
||||
Name: t.tunOptions.Name,
|
||||
Inet4Address: t.tunOptions.Inet4Address,
|
||||
Inet6Address: t.tunOptions.Inet6Address,
|
||||
EndpointIndependentNat: t.endpointIndependentNat,
|
||||
UDPTimeout: t.udpTimeout,
|
||||
Handler: t,
|
||||
Logger: t.logger,
|
||||
})
|
||||
t.tunStack, err = tun.NewStack(t.ctx, t.stack, tunIf, t.tunOptions.MTU, t.endpointIndependentNat, t.udpTimeout, t)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -2,11 +2,11 @@ package inbound
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
@@ -31,7 +31,7 @@ type VMess struct {
|
||||
ctx context.Context
|
||||
service *vmess.Service[int]
|
||||
users []option.VMessUser
|
||||
tlsConfig tls.ServerConfig
|
||||
tlsConfig *TLSConfig
|
||||
transport adapter.V2RayServerTransport
|
||||
}
|
||||
|
||||
@@ -62,13 +62,17 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg
|
||||
return nil, err
|
||||
}
|
||||
if options.TLS != nil {
|
||||
inbound.tlsConfig, err = tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS))
|
||||
inbound.tlsConfig, err = NewTLSConfig(ctx, logger, common.PtrValueOrDefault(options.TLS))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if options.Transport != nil {
|
||||
inbound.transport, err = v2ray.NewServerTransport(ctx, common.PtrValueOrDefault(options.Transport), inbound.tlsConfig, adapter.NewUpstreamHandler(adapter.InboundContext{}, inbound.newTransportConnection, nil, nil), inbound)
|
||||
var tlsConfig *tls.Config
|
||||
if inbound.tlsConfig != nil {
|
||||
tlsConfig = inbound.tlsConfig.Config()
|
||||
}
|
||||
inbound.transport, err = v2ray.NewServerTransport(ctx, common.PtrValueOrDefault(options.Transport), tlsConfig, adapter.NewUpstreamHandler(adapter.InboundContext{}, inbound.newTransportConnection, nil, nil), inbound)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "create server transport: ", options.Transport.Type)
|
||||
}
|
||||
@@ -80,7 +84,7 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg
|
||||
func (h *VMess) Start() error {
|
||||
err := common.Start(
|
||||
h.service,
|
||||
h.tlsConfig,
|
||||
common.PtrOrNil(h.tlsConfig),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -119,7 +123,7 @@ func (h *VMess) Close() error {
|
||||
return common.Close(
|
||||
h.service,
|
||||
&h.myInboundAdapter,
|
||||
h.tlsConfig,
|
||||
common.PtrOrNil(h.tlsConfig),
|
||||
h.transport,
|
||||
)
|
||||
}
|
||||
@@ -131,7 +135,7 @@ func (h *VMess) newTransportConnection(ctx context.Context, conn net.Conn, metad
|
||||
|
||||
func (h *VMess) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
if h.tlsConfig != nil && h.transport == nil {
|
||||
conn = h.tlsConfig.Server(conn)
|
||||
conn = tls.Server(conn, h.tlsConfig.Config())
|
||||
}
|
||||
return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata))
|
||||
}
|
||||
|
||||
@@ -4,10 +4,6 @@ type ClashAPIOptions struct {
|
||||
ExternalController string `json:"external_controller,omitempty"`
|
||||
ExternalUI string `json:"external_ui,omitempty"`
|
||||
Secret string `json:"secret,omitempty"`
|
||||
|
||||
DefaultMode string `json:"default_mode,omitempty"`
|
||||
StoreSelected bool `json:"store_selected,omitempty"`
|
||||
CacheFile string `json:"cache_file,omitempty"`
|
||||
}
|
||||
|
||||
type SelectorOutboundOptions struct {
|
||||
|
||||
@@ -20,7 +20,7 @@ type _Options struct {
|
||||
type Options _Options
|
||||
|
||||
func (o *Options) UnmarshalJSON(content []byte) error {
|
||||
decoder := json.NewDecoder(json.NewCommentFilter(bytes.NewReader(content)))
|
||||
decoder := json.NewDecoder(bytes.NewReader(content))
|
||||
decoder.DisallowUnknownFields()
|
||||
err := decoder.Decode((*_Options)(o))
|
||||
if err == nil {
|
||||
|
||||
@@ -99,7 +99,6 @@ type DefaultDNSRule struct {
|
||||
User Listable[string] `json:"user,omitempty"`
|
||||
UserID Listable[int32] `json:"user_id,omitempty"`
|
||||
Outbound Listable[string] `json:"outbound,omitempty"`
|
||||
ClashMode string `json:"clash_mode,omitempty"`
|
||||
Invert bool `json:"invert,omitempty"`
|
||||
Server string `json:"server,omitempty"`
|
||||
DisableCache bool `json:"disable_cache,omitempty"`
|
||||
|
||||
@@ -111,13 +111,11 @@ type InboundOptions struct {
|
||||
}
|
||||
|
||||
type ListenOptions struct {
|
||||
Listen ListenAddress `json:"listen"`
|
||||
ListenPort uint16 `json:"listen_port,omitempty"`
|
||||
TCPFastOpen bool `json:"tcp_fast_open,omitempty"`
|
||||
UDPFragment bool `json:"udp_fragment,omitempty"`
|
||||
UDPTimeout int64 `json:"udp_timeout,omitempty"`
|
||||
ProxyProtocol bool `json:"proxy_protocol,omitempty"`
|
||||
ProxyProtocolAcceptNoHeader bool `json:"proxy_protocol_accept_no_header,omitempty"`
|
||||
Detour string `json:"detour,omitempty"`
|
||||
Listen ListenAddress `json:"listen"`
|
||||
ListenPort uint16 `json:"listen_port,omitempty"`
|
||||
TCPFastOpen bool `json:"tcp_fast_open,omitempty"`
|
||||
UDPTimeout int64 `json:"udp_timeout,omitempty"`
|
||||
ProxyProtocol bool `json:"proxy_protocol,omitempty"`
|
||||
Detour string `json:"detour,omitempty"`
|
||||
InboundOptions
|
||||
}
|
||||
|
||||
@@ -113,7 +113,6 @@ type DialerOptions struct {
|
||||
ReuseAddr bool `json:"reuse_addr,omitempty"`
|
||||
ConnectTimeout Duration `json:"connect_timeout,omitempty"`
|
||||
TCPFastOpen bool `json:"tcp_fast_open,omitempty"`
|
||||
UDPFragment bool `json:"udp_fragment,omitempty"`
|
||||
DomainStrategy DomainStrategy `json:"domain_strategy,omitempty"`
|
||||
FallbackDelay Duration `json:"fallback_delay,omitempty"`
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ type RouteOptions struct {
|
||||
Final string `json:"final,omitempty"`
|
||||
FindProcess bool `json:"find_process,omitempty"`
|
||||
AutoDetectInterface bool `json:"auto_detect_interface,omitempty"`
|
||||
OverrideAndroidVPN bool `json:"override_android_vpn,omitempty"`
|
||||
DefaultInterface string `json:"default_interface,omitempty"`
|
||||
DefaultMark int `json:"default_mark,omitempty"`
|
||||
}
|
||||
@@ -101,7 +100,6 @@ type DefaultRule struct {
|
||||
PackageName Listable[string] `json:"package_name,omitempty"`
|
||||
User Listable[string] `json:"user,omitempty"`
|
||||
UserID Listable[int32] `json:"user_id,omitempty"`
|
||||
ClashMode string `json:"clash_mode,omitempty"`
|
||||
Invert bool `json:"invert,omitempty"`
|
||||
Outbound string `json:"outbound,omitempty"`
|
||||
}
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
package option
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
|
||||
type InboundTLSOptions struct {
|
||||
Enabled bool `json:"enabled,omitempty"`
|
||||
ServerName string `json:"server_name,omitempty"`
|
||||
@@ -15,28 +21,29 @@ type InboundTLSOptions struct {
|
||||
}
|
||||
|
||||
type OutboundTLSOptions struct {
|
||||
Enabled bool `json:"enabled,omitempty"`
|
||||
DisableSNI bool `json:"disable_sni,omitempty"`
|
||||
ServerName string `json:"server_name,omitempty"`
|
||||
Insecure bool `json:"insecure,omitempty"`
|
||||
ALPN Listable[string] `json:"alpn,omitempty"`
|
||||
MinVersion string `json:"min_version,omitempty"`
|
||||
MaxVersion string `json:"max_version,omitempty"`
|
||||
CipherSuites Listable[string] `json:"cipher_suites,omitempty"`
|
||||
Certificate string `json:"certificate,omitempty"`
|
||||
CertificatePath string `json:"certificate_path,omitempty"`
|
||||
ECH *OutboundECHOptions `json:"ech,omitempty"`
|
||||
UTLS *OutboundUTLSOptions `json:"utls,omitempty"`
|
||||
Enabled bool `json:"enabled,omitempty"`
|
||||
DisableSNI bool `json:"disable_sni,omitempty"`
|
||||
ServerName string `json:"server_name,omitempty"`
|
||||
Insecure bool `json:"insecure,omitempty"`
|
||||
ALPN Listable[string] `json:"alpn,omitempty"`
|
||||
MinVersion string `json:"min_version,omitempty"`
|
||||
MaxVersion string `json:"max_version,omitempty"`
|
||||
CipherSuites Listable[string] `json:"cipher_suites,omitempty"`
|
||||
Certificate string `json:"certificate,omitempty"`
|
||||
CertificatePath string `json:"certificate_path,omitempty"`
|
||||
}
|
||||
|
||||
type OutboundECHOptions struct {
|
||||
Enabled bool `json:"enabled,omitempty"`
|
||||
PQSignatureSchemesEnabled bool `json:"pq_signature_schemes_enabled,omitempty"`
|
||||
DynamicRecordSizingDisabled bool `json:"dynamic_record_sizing_disabled,omitempty"`
|
||||
Config string `json:"config,omitempty"`
|
||||
}
|
||||
|
||||
type OutboundUTLSOptions struct {
|
||||
Enabled bool `json:"enabled,omitempty"`
|
||||
Fingerprint string `json:"fingerprint,omitempty"`
|
||||
func ParseTLSVersion(version string) (uint16, error) {
|
||||
switch version {
|
||||
case "1.0":
|
||||
return tls.VersionTLS10, nil
|
||||
case "1.1":
|
||||
return tls.VersionTLS11, nil
|
||||
case "1.2":
|
||||
return tls.VersionTLS12, nil
|
||||
case "1.3":
|
||||
return tls.VersionTLS13, nil
|
||||
default:
|
||||
return 0, E.New("unknown tls version:", version)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
package option
|
||||
|
||||
type TunInboundOptions struct {
|
||||
InterfaceName string `json:"interface_name,omitempty"`
|
||||
MTU uint32 `json:"mtu,omitempty"`
|
||||
Inet4Address Listable[ListenPrefix] `json:"inet4_address,omitempty"`
|
||||
Inet6Address Listable[ListenPrefix] `json:"inet6_address,omitempty"`
|
||||
AutoRoute bool `json:"auto_route,omitempty"`
|
||||
StrictRoute bool `json:"strict_route,omitempty"`
|
||||
IncludeUID Listable[uint32] `json:"include_uid,omitempty"`
|
||||
IncludeUIDRange Listable[string] `json:"include_uid_range,omitempty"`
|
||||
ExcludeUID Listable[uint32] `json:"exclude_uid,omitempty"`
|
||||
ExcludeUIDRange Listable[string] `json:"exclude_uid_range,omitempty"`
|
||||
IncludeAndroidUser Listable[int] `json:"include_android_user,omitempty"`
|
||||
IncludePackage Listable[string] `json:"include_package,omitempty"`
|
||||
ExcludePackage Listable[string] `json:"exclude_package,omitempty"`
|
||||
EndpointIndependentNat bool `json:"endpoint_independent_nat,omitempty"`
|
||||
UDPTimeout int64 `json:"udp_timeout,omitempty"`
|
||||
Stack string `json:"stack,omitempty"`
|
||||
InterfaceName string `json:"interface_name,omitempty"`
|
||||
MTU uint32 `json:"mtu,omitempty"`
|
||||
Inet4Address *ListenPrefix `json:"inet4_address,omitempty"`
|
||||
Inet6Address *ListenPrefix `json:"inet6_address,omitempty"`
|
||||
AutoRoute bool `json:"auto_route,omitempty"`
|
||||
StrictRoute bool `json:"strict_route,omitempty"`
|
||||
IncludeUID Listable[uint32] `json:"include_uid,omitempty"`
|
||||
IncludeUIDRange Listable[string] `json:"include_uid_range,omitempty"`
|
||||
ExcludeUID Listable[uint32] `json:"exclude_uid,omitempty"`
|
||||
ExcludeUIDRange Listable[string] `json:"exclude_uid_range,omitempty"`
|
||||
IncludeAndroidUser Listable[int] `json:"include_android_user,omitempty"`
|
||||
IncludePackage Listable[string] `json:"include_package,omitempty"`
|
||||
ExcludePackage Listable[string] `json:"exclude_package,omitempty"`
|
||||
EndpointIndependentNat bool `json:"endpoint_independent_nat,omitempty"`
|
||||
UDPTimeout int64 `json:"udp_timeout,omitempty"`
|
||||
Stack string `json:"stack,omitempty"`
|
||||
InboundOptions
|
||||
}
|
||||
|
||||
@@ -184,6 +184,9 @@ func (p *ListenPrefix) UnmarshalJSON(bytes []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p ListenPrefix) Build() netip.Prefix {
|
||||
return netip.Prefix(p)
|
||||
func (p *ListenPrefix) Build() netip.Prefix {
|
||||
if p == nil {
|
||||
return netip.Prefix{}
|
||||
}
|
||||
return netip.Prefix(*p)
|
||||
}
|
||||
|
||||
@@ -3,12 +3,10 @@ package option
|
||||
type WireGuardOutboundOptions struct {
|
||||
DialerOptions
|
||||
ServerOptions
|
||||
SystemInterface bool `json:"system_interface,omitempty"`
|
||||
InterfaceName string `json:"interface_name,omitempty"`
|
||||
LocalAddress Listable[ListenPrefix] `json:"local_address"`
|
||||
PrivateKey string `json:"private_key"`
|
||||
PeerPublicKey string `json:"peer_public_key"`
|
||||
PreSharedKey string `json:"pre_shared_key,omitempty"`
|
||||
MTU uint32 `json:"mtu,omitempty"`
|
||||
Network NetworkList `json:"network,omitempty"`
|
||||
LocalAddress Listable[string] `json:"local_address"`
|
||||
PrivateKey string `json:"private_key"`
|
||||
PeerPublicKey string `json:"peer_public_key"`
|
||||
PreSharedKey string `json:"pre_shared_key,omitempty"`
|
||||
MTU uint32 `json:"mtu,omitempty"`
|
||||
Network NetworkList `json:"network,omitempty"`
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/dialer"
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
@@ -25,7 +24,7 @@ type HTTP struct {
|
||||
}
|
||||
|
||||
func NewHTTP(router adapter.Router, logger log.ContextLogger, tag string, options option.HTTPOutboundOptions) (*HTTP, error) {
|
||||
detour, err := tls.NewDialerFromOptions(router, dialer.New(router, options.DialerOptions), options.Server, common.PtrValueOrDefault(options.TLS))
|
||||
detour, err := dialer.NewTLS(dialer.New(router, options.DialerOptions), options.Server, common.PtrValueOrDefault(options.TLS))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ package outbound
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
@@ -11,7 +12,6 @@ import (
|
||||
"github.com/sagernet/quic-go/congestion"
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/dialer"
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
@@ -30,7 +30,7 @@ type Hysteria struct {
|
||||
ctx context.Context
|
||||
dialer N.Dialer
|
||||
serverAddr M.Socksaddr
|
||||
tlsConfig *tls.STDConfig
|
||||
tlsConfig *tls.Config
|
||||
quicConfig *quic.Config
|
||||
authKey []byte
|
||||
xplusKey []byte
|
||||
@@ -47,11 +47,7 @@ func NewHysteria(ctx context.Context, router adapter.Router, logger log.ContextL
|
||||
if options.TLS == nil || !options.TLS.Enabled {
|
||||
return nil, C.ErrTLSRequired
|
||||
}
|
||||
abstractTLSConfig, err := tls.NewClient(router, options.Server, common.PtrValueOrDefault(options.TLS))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tlsConfig, err := abstractTLSConfig.Config()
|
||||
tlsConfig, err := dialer.TLSConfig(options.Server, common.PtrValueOrDefault(options.TLS))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -59,30 +59,15 @@ func (s *Selector) Start() error {
|
||||
}
|
||||
s.outbounds[tag] = detour
|
||||
}
|
||||
|
||||
if s.tag != "" {
|
||||
if clashServer := s.router.ClashServer(); clashServer != nil && clashServer.StoreSelected() {
|
||||
selected := clashServer.CacheFile().LoadSelected(s.tag)
|
||||
if selected != "" {
|
||||
detour, loaded := s.outbounds[selected]
|
||||
if loaded {
|
||||
s.selected = detour
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if s.defaultTag != "" {
|
||||
detour, loaded := s.outbounds[s.defaultTag]
|
||||
if !loaded {
|
||||
return E.New("default outbound not found: ", s.defaultTag)
|
||||
}
|
||||
s.selected = detour
|
||||
return nil
|
||||
} else {
|
||||
s.selected = s.outbounds[s.tags[0]]
|
||||
}
|
||||
|
||||
s.selected = s.outbounds[s.tags[0]]
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -100,14 +85,6 @@ func (s *Selector) SelectOutbound(tag string) bool {
|
||||
return false
|
||||
}
|
||||
s.selected = detour
|
||||
if s.tag != "" {
|
||||
if clashServer := s.router.ClashServer(); clashServer != nil && clashServer.StoreSelected() {
|
||||
err := clashServer.CacheFile().StoreSelected(s.tag, tag)
|
||||
if err != nil {
|
||||
s.logger.Error("store selected: ", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,12 @@ package outbound
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/dialer"
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
@@ -22,7 +22,7 @@ type ShadowTLS struct {
|
||||
myOutboundAdapter
|
||||
dialer N.Dialer
|
||||
serverAddr M.Socksaddr
|
||||
tlsConfig tls.Config
|
||||
tlsConfig *tls.Config
|
||||
}
|
||||
|
||||
func NewShadowTLS(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowTLSOutboundOptions) (*ShadowTLS, error) {
|
||||
@@ -43,7 +43,7 @@ func NewShadowTLS(ctx context.Context, router adapter.Router, logger log.Context
|
||||
options.TLS.MinVersion = "1.2"
|
||||
options.TLS.MaxVersion = "1.2"
|
||||
var err error
|
||||
outbound.tlsConfig, err = tls.NewClient(router, options.Server, common.PtrValueOrDefault(options.TLS))
|
||||
outbound.tlsConfig, err = dialer.TLSConfig(options.Server, common.PtrValueOrDefault(options.TLS))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -60,7 +60,11 @@ func (s *ShadowTLS) DialContext(ctx context.Context, network string, destination
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = tls.ClientHandshake(ctx, conn, s.tlsConfig)
|
||||
tlsConn, err := dialer.TLSClient(ctx, conn, s.tlsConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = tlsConn.HandshakeContext(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -2,12 +2,12 @@ package outbound
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/dialer"
|
||||
"github.com/sagernet/sing-box/common/mux"
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
@@ -28,7 +28,7 @@ type Trojan struct {
|
||||
serverAddr M.Socksaddr
|
||||
key [56]byte
|
||||
multiplexDialer N.Dialer
|
||||
tlsConfig tls.Config
|
||||
tlsConfig *tls.Config
|
||||
transport adapter.V2RayClientTransport
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ func NewTrojan(ctx context.Context, router adapter.Router, logger log.ContextLog
|
||||
}
|
||||
var err error
|
||||
if options.TLS != nil {
|
||||
outbound.tlsConfig, err = tls.NewClient(router, options.Server, common.PtrValueOrDefault(options.TLS))
|
||||
outbound.tlsConfig, err = dialer.TLSConfig(options.Server, common.PtrValueOrDefault(options.TLS))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -116,7 +116,7 @@ func (h *trojanDialer) DialContext(ctx context.Context, network string, destinat
|
||||
} else {
|
||||
conn, err = h.dialer.DialContext(ctx, N.NetworkTCP, h.serverAddr)
|
||||
if err == nil && h.tlsConfig != nil {
|
||||
conn, err = tls.ClientHandshake(ctx, conn, h.tlsConfig)
|
||||
conn, err = dialer.TLSClient(ctx, conn, h.tlsConfig)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
|
||||
@@ -2,12 +2,12 @@ package outbound
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/dialer"
|
||||
"github.com/sagernet/sing-box/common/mux"
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
@@ -28,7 +28,7 @@ type VMess struct {
|
||||
client *vmess.Client
|
||||
serverAddr M.Socksaddr
|
||||
multiplexDialer N.Dialer
|
||||
tlsConfig tls.Config
|
||||
tlsConfig *tls.Config
|
||||
transport adapter.V2RayClientTransport
|
||||
packetAddr bool
|
||||
}
|
||||
@@ -47,7 +47,7 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg
|
||||
}
|
||||
var err error
|
||||
if options.TLS != nil {
|
||||
outbound.tlsConfig, err = tls.NewClient(router, options.Server, common.PtrValueOrDefault(options.TLS))
|
||||
outbound.tlsConfig, err = dialer.TLSConfig(options.Server, common.PtrValueOrDefault(options.TLS))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -142,7 +142,7 @@ func (h *vmessDialer) DialContext(ctx context.Context, network string, destinati
|
||||
} else {
|
||||
conn, err = h.dialer.DialContext(ctx, N.NetworkTCP, h.serverAddr)
|
||||
if err == nil && h.tlsConfig != nil {
|
||||
conn, err = tls.ClientHandshake(ctx, conn, h.tlsConfig)
|
||||
conn, err = dialer.TLSClient(ctx, conn, h.tlsConfig)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
@@ -169,7 +169,7 @@ func (h *vmessDialer) ListenPacket(ctx context.Context, destination M.Socksaddr)
|
||||
} else {
|
||||
conn, err = h.dialer.DialContext(ctx, N.NetworkTCP, h.serverAddr)
|
||||
if err == nil && h.tlsConfig != nil {
|
||||
conn, err = tls.ClientHandshake(ctx, conn, h.tlsConfig)
|
||||
conn, err = dialer.TLSClient(ctx, conn, h.tlsConfig)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
|
||||
@@ -8,30 +8,49 @@ import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/dialer"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-box/transport/wireguard"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/debug"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
|
||||
"golang.zx2c4.com/wireguard/conn"
|
||||
"golang.zx2c4.com/wireguard/device"
|
||||
"golang.zx2c4.com/wireguard/tun"
|
||||
"gvisor.dev/gvisor/pkg/bufferv2"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/transport/icmp"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/transport/udp"
|
||||
)
|
||||
|
||||
var _ adapter.Outbound = (*WireGuard)(nil)
|
||||
|
||||
type WireGuard struct {
|
||||
myOutboundAdapter
|
||||
bind *wireguard.ClientBind
|
||||
device *device.Device
|
||||
tunDevice wireguard.Device
|
||||
ctx context.Context
|
||||
serverAddr M.Socksaddr
|
||||
dialer N.Dialer
|
||||
endpoint conn.Endpoint
|
||||
device *device.Device
|
||||
tunDevice *wireTunDevice
|
||||
connAccess sync.Mutex
|
||||
conn *wireConn
|
||||
}
|
||||
|
||||
func NewWireGuard(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.WireGuardOutboundOptions) (*WireGuard, error) {
|
||||
@@ -43,13 +62,39 @@ func NewWireGuard(ctx context.Context, router adapter.Router, logger log.Context
|
||||
logger: logger,
|
||||
tag: tag,
|
||||
},
|
||||
ctx: ctx,
|
||||
serverAddr: options.ServerOptions.Build(),
|
||||
dialer: dialer.New(router, options.DialerOptions),
|
||||
}
|
||||
peerAddr := options.ServerOptions.Build()
|
||||
outbound.bind = wireguard.NewClientBind(ctx, dialer.New(router, options.DialerOptions), peerAddr)
|
||||
localPrefixes := common.Map(options.LocalAddress, option.ListenPrefix.Build)
|
||||
if len(localPrefixes) == 0 {
|
||||
var endpointIp netip.Addr
|
||||
if !outbound.serverAddr.IsFqdn() {
|
||||
endpointIp = outbound.serverAddr.Addr
|
||||
} else {
|
||||
endpointIp = netip.AddrFrom4([4]byte{127, 0, 0, 1})
|
||||
}
|
||||
outbound.endpoint = conn.StdNetEndpoint(netip.AddrPortFrom(endpointIp, outbound.serverAddr.Port))
|
||||
localAddress := make([]tcpip.AddressWithPrefix, len(options.LocalAddress))
|
||||
if len(localAddress) == 0 {
|
||||
return nil, E.New("missing local address")
|
||||
}
|
||||
for index, address := range options.LocalAddress {
|
||||
if strings.Contains(address, "/") {
|
||||
prefix, err := netip.ParsePrefix(address)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse local address prefix ", address)
|
||||
}
|
||||
localAddress[index] = tcpip.AddressWithPrefix{
|
||||
Address: tcpip.Address(prefix.Addr().AsSlice()),
|
||||
PrefixLen: prefix.Bits(),
|
||||
}
|
||||
} else {
|
||||
addr, err := netip.ParseAddr(address)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse local address ", address)
|
||||
}
|
||||
localAddress[index] = tcpip.Address(addr.AsSlice()).WithPrefix()
|
||||
}
|
||||
}
|
||||
var privateKey, peerPublicKey, preSharedKey string
|
||||
{
|
||||
bytes, err := base64.StdEncoding.DecodeString(options.PrivateKey)
|
||||
@@ -74,13 +119,13 @@ func NewWireGuard(ctx context.Context, router adapter.Router, logger log.Context
|
||||
}
|
||||
ipcConf := "private_key=" + privateKey
|
||||
ipcConf += "\npublic_key=" + peerPublicKey
|
||||
ipcConf += "\nendpoint=" + peerAddr.String()
|
||||
ipcConf += "\nendpoint=" + outbound.endpoint.DstToString()
|
||||
if preSharedKey != "" {
|
||||
ipcConf += "\npreshared_key=" + preSharedKey
|
||||
}
|
||||
var has4, has6 bool
|
||||
for _, address := range localPrefixes {
|
||||
if address.Addr().Is4() {
|
||||
for _, address := range localAddress {
|
||||
if address.Address.To4() != "" {
|
||||
has4 = true
|
||||
} else {
|
||||
has6 = true
|
||||
@@ -96,17 +141,11 @@ func NewWireGuard(ctx context.Context, router adapter.Router, logger log.Context
|
||||
if mtu == 0 {
|
||||
mtu = 1408
|
||||
}
|
||||
var wireTunDevice wireguard.Device
|
||||
var err error
|
||||
if !options.SystemInterface {
|
||||
wireTunDevice, err = wireguard.NewStackDevice(localPrefixes, mtu)
|
||||
} else {
|
||||
wireTunDevice, err = wireguard.NewSystemDevice(router, options.InterfaceName, localPrefixes, mtu)
|
||||
}
|
||||
wireDevice, err := newWireDevice(localAddress, mtu)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "create WireGuard device")
|
||||
return nil, err
|
||||
}
|
||||
wgDevice := device.NewDevice(wireTunDevice, outbound.bind, &device.Logger{
|
||||
wgDevice := device.NewDevice(wireDevice, (*wireClientBind)(outbound), &device.Logger{
|
||||
Verbosef: func(format string, args ...interface{}) {
|
||||
logger.Debug(fmt.Sprintf(strings.ToLower(format), args...))
|
||||
},
|
||||
@@ -122,7 +161,7 @@ func NewWireGuard(ctx context.Context, router adapter.Router, logger log.Context
|
||||
return nil, E.Cause(err, "setup wireguard")
|
||||
}
|
||||
outbound.device = wgDevice
|
||||
outbound.tunDevice = wireTunDevice
|
||||
outbound.tunDevice = wireDevice
|
||||
return outbound, nil
|
||||
}
|
||||
|
||||
@@ -133,19 +172,54 @@ func (w *WireGuard) DialContext(ctx context.Context, network string, destination
|
||||
case N.NetworkUDP:
|
||||
w.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
||||
}
|
||||
addr := tcpip.FullAddress{
|
||||
NIC: defaultNIC,
|
||||
Port: destination.Port,
|
||||
}
|
||||
if destination.IsFqdn() {
|
||||
addrs, err := w.router.LookupDefault(ctx, destination.Fqdn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return N.DialSerial(ctx, w.tunDevice, network, destination, addrs)
|
||||
addr.Addr = tcpip.Address(addrs[0].AsSlice())
|
||||
} else {
|
||||
addr.Addr = tcpip.Address(destination.Addr.AsSlice())
|
||||
}
|
||||
bind := tcpip.FullAddress{
|
||||
NIC: defaultNIC,
|
||||
}
|
||||
var networkProtocol tcpip.NetworkProtocolNumber
|
||||
if destination.IsIPv4() {
|
||||
networkProtocol = header.IPv4ProtocolNumber
|
||||
bind.Addr = w.tunDevice.addr4
|
||||
} else {
|
||||
networkProtocol = header.IPv6ProtocolNumber
|
||||
bind.Addr = w.tunDevice.addr6
|
||||
}
|
||||
switch N.NetworkName(network) {
|
||||
case N.NetworkTCP:
|
||||
return gonet.DialTCPWithBind(ctx, w.tunDevice.stack, bind, addr, networkProtocol)
|
||||
case N.NetworkUDP:
|
||||
return gonet.DialUDP(w.tunDevice.stack, &bind, &addr, networkProtocol)
|
||||
default:
|
||||
return nil, E.Extend(N.ErrUnknownNetwork, network)
|
||||
}
|
||||
return w.tunDevice.DialContext(ctx, network, destination)
|
||||
}
|
||||
|
||||
func (w *WireGuard) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
w.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
||||
return w.tunDevice.ListenPacket(ctx, destination)
|
||||
bind := tcpip.FullAddress{
|
||||
NIC: defaultNIC,
|
||||
}
|
||||
var networkProtocol tcpip.NetworkProtocolNumber
|
||||
if destination.IsIPv4() || w.tunDevice.addr6 == "" {
|
||||
networkProtocol = header.IPv4ProtocolNumber
|
||||
bind.Addr = w.tunDevice.addr4
|
||||
} else {
|
||||
networkProtocol = header.IPv6ProtocolNumber
|
||||
bind.Addr = w.tunDevice.addr6
|
||||
}
|
||||
return gonet.DialUDP(w.tunDevice.stack, &bind, nil, networkProtocol)
|
||||
}
|
||||
|
||||
func (w *WireGuard) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
@@ -157,13 +231,300 @@ func (w *WireGuard) NewPacketConnection(ctx context.Context, conn N.PacketConn,
|
||||
}
|
||||
|
||||
func (w *WireGuard) Start() error {
|
||||
return w.tunDevice.Start()
|
||||
w.tunDevice.events <- tun.EventUp
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *WireGuard) Close() error {
|
||||
return common.Close(
|
||||
w.tunDevice,
|
||||
common.PtrOrNil(w.tunDevice),
|
||||
common.PtrOrNil(w.device),
|
||||
common.PtrOrNil(w.bind),
|
||||
common.PtrOrNil(w.conn),
|
||||
)
|
||||
}
|
||||
|
||||
var _ conn.Bind = (*wireClientBind)(nil)
|
||||
|
||||
type wireClientBind WireGuard
|
||||
|
||||
func (c *wireClientBind) connect() (*wireConn, error) {
|
||||
c.connAccess.Lock()
|
||||
defer c.connAccess.Unlock()
|
||||
if c.conn != nil {
|
||||
select {
|
||||
case <-c.conn.done:
|
||||
default:
|
||||
return c.conn, nil
|
||||
}
|
||||
}
|
||||
udpConn, err := c.dialer.DialContext(c.ctx, "udp", c.serverAddr)
|
||||
if err != nil {
|
||||
return nil, &wireError{err}
|
||||
}
|
||||
c.conn = &wireConn{
|
||||
Conn: udpConn,
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
return c.conn, nil
|
||||
}
|
||||
|
||||
func (c *wireClientBind) Open(port uint16) (fns []conn.ReceiveFunc, actualPort uint16, err error) {
|
||||
return []conn.ReceiveFunc{c.receive}, 0, nil
|
||||
}
|
||||
|
||||
func (c *wireClientBind) receive(b []byte) (n int, ep conn.Endpoint, err error) {
|
||||
udpConn, err := c.connect()
|
||||
if err != nil {
|
||||
err = &wireError{err}
|
||||
return
|
||||
}
|
||||
n, err = udpConn.Read(b)
|
||||
if err != nil {
|
||||
udpConn.Close()
|
||||
err = &wireError{err}
|
||||
}
|
||||
ep = c.endpoint
|
||||
return
|
||||
}
|
||||
|
||||
func (c *wireClientBind) Close() error {
|
||||
c.connAccess.Lock()
|
||||
defer c.connAccess.Unlock()
|
||||
common.Close(common.PtrOrNil(c.conn))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *wireClientBind) SetMark(mark uint32) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *wireClientBind) Send(b []byte, ep conn.Endpoint) error {
|
||||
udpConn, err := c.connect()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = udpConn.Write(b)
|
||||
if err != nil {
|
||||
udpConn.Close()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *wireClientBind) ParseEndpoint(s string) (conn.Endpoint, error) {
|
||||
return c.endpoint, nil
|
||||
}
|
||||
|
||||
type wireError struct {
|
||||
cause error
|
||||
}
|
||||
|
||||
func (w *wireError) Error() string {
|
||||
return w.cause.Error()
|
||||
}
|
||||
|
||||
func (w *wireError) Timeout() bool {
|
||||
if cause, causeNet := w.cause.(net.Error); causeNet {
|
||||
return cause.Timeout()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (w *wireError) Temporary() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
type wireConn struct {
|
||||
net.Conn
|
||||
access sync.Mutex
|
||||
done chan struct{}
|
||||
}
|
||||
|
||||
func (w *wireConn) Close() error {
|
||||
w.access.Lock()
|
||||
defer w.access.Unlock()
|
||||
select {
|
||||
case <-w.done:
|
||||
return net.ErrClosed
|
||||
default:
|
||||
}
|
||||
w.Conn.Close()
|
||||
close(w.done)
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ tun.Device = (*wireTunDevice)(nil)
|
||||
|
||||
const defaultNIC tcpip.NICID = 1
|
||||
|
||||
type wireTunDevice struct {
|
||||
stack *stack.Stack
|
||||
mtu uint32
|
||||
events chan tun.Event
|
||||
outbound chan *stack.PacketBuffer
|
||||
dispatcher stack.NetworkDispatcher
|
||||
done chan struct{}
|
||||
addr4 tcpip.Address
|
||||
addr6 tcpip.Address
|
||||
}
|
||||
|
||||
func newWireDevice(localAddresses []tcpip.AddressWithPrefix, mtu uint32) (*wireTunDevice, error) {
|
||||
ipStack := stack.New(stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol, ipv6.NewProtocol},
|
||||
TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol, udp.NewProtocol, icmp.NewProtocol4, icmp.NewProtocol6},
|
||||
HandleLocal: true,
|
||||
})
|
||||
tunDevice := &wireTunDevice{
|
||||
stack: ipStack,
|
||||
mtu: mtu,
|
||||
events: make(chan tun.Event, 4),
|
||||
outbound: make(chan *stack.PacketBuffer, 256),
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
err := ipStack.CreateNIC(defaultNIC, (*wireEndpoint)(tunDevice))
|
||||
if err != nil {
|
||||
return nil, E.New(err.String())
|
||||
}
|
||||
for _, addr := range localAddresses {
|
||||
var protoAddr tcpip.ProtocolAddress
|
||||
if len(addr.Address) == net.IPv4len {
|
||||
tunDevice.addr4 = addr.Address
|
||||
protoAddr = tcpip.ProtocolAddress{
|
||||
Protocol: ipv4.ProtocolNumber,
|
||||
AddressWithPrefix: addr,
|
||||
}
|
||||
} else {
|
||||
tunDevice.addr6 = addr.Address
|
||||
protoAddr = tcpip.ProtocolAddress{
|
||||
Protocol: ipv6.ProtocolNumber,
|
||||
AddressWithPrefix: addr,
|
||||
}
|
||||
}
|
||||
err = ipStack.AddProtocolAddress(defaultNIC, protoAddr, stack.AddressProperties{})
|
||||
if err != nil {
|
||||
return nil, E.New("parse local address ", protoAddr.AddressWithPrefix, ": ", err.String())
|
||||
}
|
||||
}
|
||||
sOpt := tcpip.TCPSACKEnabled(true)
|
||||
ipStack.SetTransportProtocolOption(tcp.ProtocolNumber, &sOpt)
|
||||
cOpt := tcpip.CongestionControlOption("cubic")
|
||||
ipStack.SetTransportProtocolOption(tcp.ProtocolNumber, &cOpt)
|
||||
ipStack.AddRoute(tcpip.Route{Destination: header.IPv4EmptySubnet, NIC: defaultNIC})
|
||||
ipStack.AddRoute(tcpip.Route{Destination: header.IPv6EmptySubnet, NIC: defaultNIC})
|
||||
return tunDevice, nil
|
||||
}
|
||||
|
||||
func (w *wireTunDevice) File() *os.File {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *wireTunDevice) Read(p []byte, offset int) (n int, err error) {
|
||||
packetBuffer, ok := <-w.outbound
|
||||
if !ok {
|
||||
return 0, os.ErrClosed
|
||||
}
|
||||
defer packetBuffer.DecRef()
|
||||
p = p[offset:]
|
||||
for _, slice := range packetBuffer.AsSlices() {
|
||||
n += copy(p[n:], slice)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (w *wireTunDevice) Write(p []byte, offset int) (n int, err error) {
|
||||
p = p[offset:]
|
||||
if len(p) == 0 {
|
||||
return
|
||||
}
|
||||
var networkProtocol tcpip.NetworkProtocolNumber
|
||||
switch header.IPVersion(p) {
|
||||
case header.IPv4Version:
|
||||
networkProtocol = header.IPv4ProtocolNumber
|
||||
case header.IPv6Version:
|
||||
networkProtocol = header.IPv6ProtocolNumber
|
||||
}
|
||||
packetBuffer := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
Payload: bufferv2.MakeWithData(p),
|
||||
})
|
||||
defer packetBuffer.DecRef()
|
||||
w.dispatcher.DeliverNetworkPacket(networkProtocol, packetBuffer)
|
||||
n = len(p)
|
||||
return
|
||||
}
|
||||
|
||||
func (w *wireTunDevice) Flush() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *wireTunDevice) MTU() (int, error) {
|
||||
return int(w.mtu), nil
|
||||
}
|
||||
|
||||
func (w *wireTunDevice) Name() (string, error) {
|
||||
return "sing-box", nil
|
||||
}
|
||||
|
||||
func (w *wireTunDevice) Events() chan tun.Event {
|
||||
return w.events
|
||||
}
|
||||
|
||||
func (w *wireTunDevice) Close() error {
|
||||
select {
|
||||
case <-w.done:
|
||||
return os.ErrClosed
|
||||
default:
|
||||
}
|
||||
close(w.done)
|
||||
w.stack.Close()
|
||||
for _, endpoint := range w.stack.CleanupEndpoints() {
|
||||
endpoint.Abort()
|
||||
}
|
||||
w.stack.Wait()
|
||||
close(w.outbound)
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ stack.LinkEndpoint = (*wireEndpoint)(nil)
|
||||
|
||||
type wireEndpoint wireTunDevice
|
||||
|
||||
func (ep *wireEndpoint) MTU() uint32 {
|
||||
return ep.mtu
|
||||
}
|
||||
|
||||
func (ep *wireEndpoint) MaxHeaderLength() uint16 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (ep *wireEndpoint) LinkAddress() tcpip.LinkAddress {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (ep *wireEndpoint) Capabilities() stack.LinkEndpointCapabilities {
|
||||
return stack.CapabilityNone
|
||||
}
|
||||
|
||||
func (ep *wireEndpoint) Attach(dispatcher stack.NetworkDispatcher) {
|
||||
ep.dispatcher = dispatcher
|
||||
}
|
||||
|
||||
func (ep *wireEndpoint) IsAttached() bool {
|
||||
return ep.dispatcher != nil
|
||||
}
|
||||
|
||||
func (ep *wireEndpoint) Wait() {
|
||||
}
|
||||
|
||||
func (ep *wireEndpoint) ARPHardwareType() header.ARPHardwareType {
|
||||
return header.ARPHardwareNone
|
||||
}
|
||||
|
||||
func (ep *wireEndpoint) AddHeader(buffer *stack.PacketBuffer) {
|
||||
}
|
||||
|
||||
func (ep *wireEndpoint) WritePackets(list stack.PacketBufferList) (int, tcpip.Error) {
|
||||
for _, packetBuffer := range list.AsSlice() {
|
||||
packetBuffer.IncRef()
|
||||
ep.outbound <- packetBuffer
|
||||
}
|
||||
return list.Len(), nil
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ type Router struct {
|
||||
networkMonitor tun.NetworkUpdateMonitor
|
||||
interfaceMonitor tun.DefaultInterfaceMonitor
|
||||
packageManager tun.PackageManager
|
||||
clashServer adapter.ClashServer
|
||||
trafficController adapter.TrafficController
|
||||
processSearcher process.Searcher
|
||||
}
|
||||
|
||||
@@ -244,7 +244,7 @@ func NewRouter(ctx context.Context, logger log.ContextLogger, dnsLogger log.Cont
|
||||
|
||||
needInterfaceMonitor := options.AutoDetectInterface ||
|
||||
C.IsDarwin && common.Any(inbounds, func(inbound option.Inbound) bool {
|
||||
return inbound.HTTPOptions.SetSystemProxy || inbound.MixedOptions.SetSystemProxy || C.IsAndroid && inbound.TunOptions.AutoRoute
|
||||
return inbound.HTTPOptions.SetSystemProxy || inbound.MixedOptions.SetSystemProxy
|
||||
})
|
||||
|
||||
if router.interfaceBindManager != nil || needInterfaceMonitor {
|
||||
@@ -258,24 +258,12 @@ func NewRouter(ctx context.Context, logger log.ContextLogger, dnsLogger log.Cont
|
||||
}
|
||||
|
||||
if router.networkMonitor != nil && needInterfaceMonitor {
|
||||
interfaceMonitor, err := tun.NewDefaultInterfaceMonitor(router.networkMonitor, tun.DefaultInterfaceMonitorOptions{
|
||||
OverrideAndroidVPN: options.OverrideAndroidVPN,
|
||||
})
|
||||
interfaceMonitor, err := tun.NewDefaultInterfaceMonitor(router.networkMonitor)
|
||||
if err != nil {
|
||||
return nil, E.New("auto_detect_interface unsupported on current platform")
|
||||
}
|
||||
interfaceMonitor.RegisterCallback(func(event int) error {
|
||||
if C.IsAndroid {
|
||||
var vpnStatus string
|
||||
if router.interfaceMonitor.AndroidVPNEnabled() {
|
||||
vpnStatus = "enabled"
|
||||
} else {
|
||||
vpnStatus = "disabled"
|
||||
}
|
||||
router.logger.Info("updated default interface ", router.interfaceMonitor.DefaultInterfaceName(netip.IPv4Unspecified()), ", index ", router.interfaceMonitor.DefaultInterfaceIndex(netip.IPv4Unspecified()), ", vpn ", vpnStatus)
|
||||
} else {
|
||||
router.logger.Info("updated default interface ", router.interfaceMonitor.DefaultInterfaceName(netip.IPv4Unspecified()), ", index ", router.interfaceMonitor.DefaultInterfaceIndex(netip.IPv4Unspecified()))
|
||||
}
|
||||
interfaceMonitor.RegisterCallback(func() error {
|
||||
router.logger.Info("updated default interface ", router.interfaceMonitor.DefaultInterfaceName(netip.IPv4Unspecified()), ", index ", router.interfaceMonitor.DefaultInterfaceIndex(netip.IPv4Unspecified()))
|
||||
return nil
|
||||
})
|
||||
router.interfaceMonitor = interfaceMonitor
|
||||
@@ -588,8 +576,8 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad
|
||||
conn.Close()
|
||||
return E.New("missing supported outbound, closing connection")
|
||||
}
|
||||
if r.clashServer != nil {
|
||||
trackerConn, tracker := r.clashServer.RoutedConnection(ctx, conn, metadata, matchedRule)
|
||||
if r.trafficController != nil {
|
||||
trackerConn, tracker := r.trafficController.RoutedConnection(ctx, conn, metadata, matchedRule)
|
||||
defer tracker.Leave()
|
||||
conn = trackerConn
|
||||
}
|
||||
@@ -661,8 +649,8 @@ func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, m
|
||||
conn.Close()
|
||||
return E.New("missing supported outbound, closing packet connection")
|
||||
}
|
||||
if r.clashServer != nil {
|
||||
trackerConn, tracker := r.clashServer.RoutedPacketConnection(ctx, conn, metadata, matchedRule)
|
||||
if r.trafficController != nil {
|
||||
trackerConn, tracker := r.trafficController.RoutedPacketConnection(ctx, conn, metadata, matchedRule)
|
||||
defer tracker.Leave()
|
||||
conn = trackerConn
|
||||
}
|
||||
@@ -679,12 +667,12 @@ func (r *Router) match(ctx context.Context, metadata *adapter.InboundContext, de
|
||||
}
|
||||
processInfo, err := process.FindProcessInfo(r.processSearcher, ctx, metadata.Network, metadata.Source.AddrPort(), originDestination)
|
||||
if err != nil {
|
||||
r.logger.InfoContext(ctx, "failed to search process: ", err)
|
||||
r.logger.DebugContext(ctx, "failed to search process: ", err)
|
||||
} else {
|
||||
if processInfo.ProcessPath != "" {
|
||||
r.logger.InfoContext(ctx, "found process path: ", processInfo.ProcessPath)
|
||||
r.logger.DebugContext(ctx, "found process path: ", processInfo.ProcessPath)
|
||||
} else if processInfo.PackageName != "" {
|
||||
r.logger.InfoContext(ctx, "found package name: ", processInfo.PackageName)
|
||||
r.logger.DebugContext(ctx, "found package name: ", processInfo.PackageName)
|
||||
} else if processInfo.UserId != -1 {
|
||||
if /*needUserName &&*/ true {
|
||||
osUser, _ := user.LookupId(F.ToString(processInfo.UserId))
|
||||
@@ -693,9 +681,9 @@ func (r *Router) match(ctx context.Context, metadata *adapter.InboundContext, de
|
||||
}
|
||||
}
|
||||
if processInfo.User != "" {
|
||||
r.logger.InfoContext(ctx, "found user: ", processInfo.User)
|
||||
r.logger.DebugContext(ctx, "found user: ", processInfo.User)
|
||||
} else {
|
||||
r.logger.InfoContext(ctx, "found user id: ", processInfo.UserId)
|
||||
r.logger.DebugContext(ctx, "found user id: ", processInfo.UserId)
|
||||
}
|
||||
}
|
||||
metadata.ProcessInfo = processInfo
|
||||
@@ -746,12 +734,8 @@ func (r *Router) PackageManager() tun.PackageManager {
|
||||
return r.packageManager
|
||||
}
|
||||
|
||||
func (r *Router) ClashServer() adapter.ClashServer {
|
||||
return r.clashServer
|
||||
}
|
||||
|
||||
func (r *Router) SetClashServer(controller adapter.ClashServer) {
|
||||
r.clashServer = controller
|
||||
func (r *Router) SetTrafficController(controller adapter.TrafficController) {
|
||||
r.trafficController = controller
|
||||
}
|
||||
|
||||
func hasRule(rules []option.Rule, cond func(rule option.DefaultRule) bool) bool {
|
||||
|
||||
@@ -192,11 +192,6 @@ func NewDefaultRule(router adapter.Router, logger log.ContextLogger, options opt
|
||||
rule.items = append(rule.items, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if options.ClashMode != "" {
|
||||
item := NewClashModeItem(router, options.ClashMode)
|
||||
rule.items = append(rule.items, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
return rule, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
package route
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
)
|
||||
|
||||
var _ RuleItem = (*ClashModeItem)(nil)
|
||||
|
||||
type ClashModeItem struct {
|
||||
router adapter.Router
|
||||
mode string
|
||||
}
|
||||
|
||||
func NewClashModeItem(router adapter.Router, mode string) *ClashModeItem {
|
||||
return &ClashModeItem{
|
||||
router: router,
|
||||
mode: strings.ToLower(mode),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *ClashModeItem) Match(metadata *adapter.InboundContext) bool {
|
||||
clashServer := r.router.ClashServer()
|
||||
if clashServer == nil {
|
||||
return false
|
||||
}
|
||||
return clashServer.Mode() == r.mode
|
||||
}
|
||||
|
||||
func (r *ClashModeItem) String() string {
|
||||
return "clash_mode=" + r.mode
|
||||
}
|
||||
@@ -180,11 +180,6 @@ func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options
|
||||
rule.items = append(rule.items, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if options.ClashMode != "" {
|
||||
item := NewClashModeItem(router, options.ClashMode)
|
||||
rule.items = append(rule.items, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
return rule, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[Interface]
|
||||
PrivateKey = gHWUGzTh5YCEV6k8dneVP537XhVtoQJPIlFNs2zsxlE=
|
||||
Address = 10.0.0.1/32
|
||||
Address = 10.0.0.1/24
|
||||
ListenPort = 10000
|
||||
|
||||
[Peer]
|
||||
|
||||
27
test/go.mod
27
test/go.mod
@@ -7,14 +7,14 @@ require github.com/sagernet/sing-box v0.0.0
|
||||
replace github.com/sagernet/sing-box => ../
|
||||
|
||||
require (
|
||||
github.com/docker/docker v20.10.18+incompatible
|
||||
github.com/docker/docker v20.10.17+incompatible
|
||||
github.com/docker/go-connections v0.4.0
|
||||
github.com/gofrs/uuid v4.3.0+incompatible
|
||||
github.com/sagernet/sing v0.0.0-20220910144724-62c4ebdbcb3f
|
||||
github.com/gofrs/uuid v4.2.0+incompatible
|
||||
github.com/sagernet/sing v0.0.0-20220903085538-02b9ca1cc133
|
||||
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6
|
||||
github.com/spyzhov/ajson v0.7.1
|
||||
github.com/stretchr/testify v1.8.0
|
||||
golang.org/x/net v0.0.0-20220909164309-bea034e7d591
|
||||
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b
|
||||
)
|
||||
|
||||
//replace github.com/sagernet/sing => ../../sing
|
||||
@@ -23,8 +23,6 @@ require (
|
||||
berty.tech/go-libtor v1.0.385 // indirect
|
||||
github.com/Microsoft/go-winio v0.5.1 // indirect
|
||||
github.com/ajg/form v1.5.1 // indirect
|
||||
github.com/andybalholm/brotli v1.0.4 // indirect
|
||||
github.com/cloudflare/circl v1.2.1-0.20220831060716-4cf0150356fc // indirect
|
||||
github.com/cretz/bine v0.2.0 // indirect
|
||||
github.com/database64128/tfo-go v1.1.2 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
@@ -41,7 +39,6 @@ require (
|
||||
github.com/google/btree v1.0.1 // indirect
|
||||
github.com/gorilla/websocket v1.5.0 // indirect
|
||||
github.com/hashicorp/yamux v0.1.1 // indirect
|
||||
github.com/klauspost/compress v1.13.6 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.1.0 // indirect
|
||||
github.com/libdns/libdns v0.2.1 // indirect
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible // indirect
|
||||
@@ -49,7 +46,6 @@ require (
|
||||
github.com/marten-seemann/qtls-go1-18 v0.1.2 // indirect
|
||||
github.com/marten-seemann/qtls-go1-19 v0.1.0 // indirect
|
||||
github.com/mholt/acmez v1.0.4 // indirect
|
||||
github.com/miekg/dns v1.1.50 // indirect
|
||||
github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/nxadm/tail v1.4.8 // indirect
|
||||
@@ -60,19 +56,17 @@ require (
|
||||
github.com/pires/go-proxyproto v0.6.2 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/refraction-networking/utls v1.1.2 // indirect
|
||||
github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e // indirect
|
||||
github.com/sagernet/certmagic v0.0.0-20220819042630-4a57f8b6853a // indirect
|
||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect
|
||||
github.com/sagernet/netlink v0.0.0-20220826133217-3fb4ff92ea17 // indirect
|
||||
github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb // indirect
|
||||
github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666 // indirect
|
||||
github.com/sagernet/sing-tun v0.0.0-20220909114108-a6b5a9289ecb // indirect
|
||||
github.com/sagernet/sing-dns v0.0.0-20220903082137-b1102b8fc961 // indirect
|
||||
github.com/sagernet/sing-tun v0.0.0-20220828031750-185b6c880a83 // indirect
|
||||
github.com/sagernet/sing-vmess v0.0.0-20220907073918-72d7fdf6825f // indirect
|
||||
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195 // indirect
|
||||
github.com/sirupsen/logrus v1.8.1 // indirect
|
||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
|
||||
go.etcd.io/bbolt v1.3.6 // indirect
|
||||
go.uber.org/atomic v1.10.0 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
go.uber.org/zap v1.22.0 // indirect
|
||||
@@ -80,18 +74,19 @@ require (
|
||||
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 // indirect
|
||||
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
|
||||
golang.org/x/sys v0.0.0-20220909162455-aba9fc2a8ff2 // indirect
|
||||
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
|
||||
golang.org/x/tools v0.1.11-0.20220513221640-090b14e8501f // indirect
|
||||
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect
|
||||
golang.zx2c4.com/wireguard v0.0.0-20220904105730-b51010ba13f0 // indirect
|
||||
golang.zx2c4.com/wireguard v0.0.0-20220829161405-d1d08426b27b // indirect
|
||||
google.golang.org/genproto v0.0.0-20210722135532-667f2b7c528f // indirect
|
||||
google.golang.org/grpc v1.49.0 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
gotest.tools/v3 v3.3.0 // indirect
|
||||
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c // indirect
|
||||
gvisor.dev/gvisor v0.0.0-20220819163037-ba6e795b139a // indirect
|
||||
lukechampine.com/blake3 v1.1.7 // indirect
|
||||
)
|
||||
|
||||
62
test/go.sum
62
test/go.sum
@@ -9,15 +9,11 @@ github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6
|
||||
github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
|
||||
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
|
||||
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudflare/circl v1.2.1-0.20220831060716-4cf0150356fc h1:307gdRLiZ08dwOIKwc5lAQ19DRFaQQvdhHalyB4Asx8=
|
||||
github.com/cloudflare/circl v1.2.1-0.20220831060716-4cf0150356fc/go.mod h1:+CauBF6R70Jqcyl8N2hC8pAXYbWkGIezuSbuGLtRhnw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
@@ -32,8 +28,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68=
|
||||
github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v20.10.18+incompatible h1:SN84VYXTBNGn92T/QwIRPlum9zfemfitN7pbsp26WSc=
|
||||
github.com/docker/docker v20.10.18+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v20.10.17+incompatible h1:JYCuMrWaVNophQTOrMMoSwudOVEfcegoZZrleKc1xwE=
|
||||
github.com/docker/docker v20.10.17+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
|
||||
@@ -57,8 +53,8 @@ github.com/go-chi/render v1.0.2 h1:4ER/udB0+fMWB2Jlf15RV3F4A2FDuYi/9f+lFttR/Lg=
|
||||
github.com/go-chi/render v1.0.2/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/gofrs/uuid v4.3.0+incompatible h1:CaSVZxm5B+7o45rtab4jC2G37WGYX1zQfuU2i6DSvnc=
|
||||
github.com/gofrs/uuid v4.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0=
|
||||
github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
@@ -97,8 +93,6 @@ github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbg
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
|
||||
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.1.0 h1:eyi1Ad2aNJMW95zcSbmGg7Cg6cq3ADwLpMAP96d8rF0=
|
||||
github.com/klauspost/cpuid/v2 v2.1.0/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||
@@ -119,8 +113,6 @@ github.com/marten-seemann/qtls-go1-19 v0.1.0 h1:rLFKD/9mp/uq1SYGYuVZhm83wkmU95pK
|
||||
github.com/marten-seemann/qtls-go1-19 v0.1.0/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI=
|
||||
github.com/mholt/acmez v1.0.4 h1:N3cE4Pek+dSolbsofIkAYz6H1d3pE+2G0os7QHslf80=
|
||||
github.com/mholt/acmez v1.0.4/go.mod h1:qFGLZ4u+ehWINeJZjzPlsnjJBCPAADWTcIqE/7DAYQY=
|
||||
github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
|
||||
github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
|
||||
github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae h1:O4SWKdcHVCvYqyDV+9CJA1fcDN2L11Bule0iFy3YlAI=
|
||||
github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw=
|
||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||
@@ -150,8 +142,6 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/refraction-networking/utls v1.1.2 h1:a7GQauRt72VG+wtNm0lnrAaCGlyX47gEi1++dSsDBpw=
|
||||
github.com/refraction-networking/utls v1.1.2/go.mod h1:+D89TUtA8+NKVFj1IXWr0p3tSdX1+SqUB7rL0QnGqyg=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e h1:5CFRo8FJbCuf5s/eTBdZpmMbn8Fe2eSMLNAYfKanA34=
|
||||
github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e/go.mod h1:qbt0dWObotCfcjAJJ9AxtFPNSDUfZF+6dCpgKEOBn/g=
|
||||
@@ -159,20 +149,20 @@ github.com/sagernet/certmagic v0.0.0-20220819042630-4a57f8b6853a h1:SE3Xn4GOQ+kx
|
||||
github.com/sagernet/certmagic v0.0.0-20220819042630-4a57f8b6853a/go.mod h1:Q+ZXyesnkjV5B70B1ixk65ecKrlJ2jz0atv3fPKsVVo=
|
||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA=
|
||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h1:QUQ4RRHD6hGGHdFMEtR8T2P6GS6R3D/CXKdaYHKKXms=
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
||||
github.com/sagernet/netlink v0.0.0-20220826133217-3fb4ff92ea17 h1:zvm6IrIgo4rLizJCHkH+SWUBhm+jyjjozX031QdAlj8=
|
||||
github.com/sagernet/netlink v0.0.0-20220826133217-3fb4ff92ea17/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
||||
github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb h1:wc0yQ+SBn4TaTYRwpwvEm3nc4eRdxk6vtRbouLVZAzk=
|
||||
github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb/go.mod h1:MIccjRKnPTjWwAOpl+AUGWOkzyTd9tERytudxu+1ra4=
|
||||
github.com/sagernet/sing v0.0.0-20220812082120-05f9836bff8f/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
|
||||
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
|
||||
github.com/sagernet/sing v0.0.0-20220910144724-62c4ebdbcb3f h1:w1TJq7Lw3It35tDyMsZLtYz4T2msf1UK9JxC85L5+sk=
|
||||
github.com/sagernet/sing v0.0.0-20220910144724-62c4ebdbcb3f/go.mod h1:kZvzh1VDa/Dg/Bt5WaYKU0jl5ept8KKDpl3Ay4gRtRQ=
|
||||
github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666 h1:XUTocA/Ek0dFxUX+xJCWMPPFZCn2GC/uLrBjTSr1vHY=
|
||||
github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666/go.mod h1:eDyH7AJmqBGjZQdQmpZIzlbTREudZuWDExMuGKgjRVM=
|
||||
github.com/sagernet/sing v0.0.0-20220903085538-02b9ca1cc133 h1:krnb8wKEFIdXhmJYlhJMbEcPsJFISy2fz90uHVz7hMU=
|
||||
github.com/sagernet/sing v0.0.0-20220903085538-02b9ca1cc133/go.mod h1:kZvzh1VDa/Dg/Bt5WaYKU0jl5ept8KKDpl3Ay4gRtRQ=
|
||||
github.com/sagernet/sing-dns v0.0.0-20220903082137-b1102b8fc961 h1:5JeqhvCGV6AQQiAO0V67Loh2eyO3JNjIQnvRF8NnTE0=
|
||||
github.com/sagernet/sing-dns v0.0.0-20220903082137-b1102b8fc961/go.mod h1:vKBBy4mNJRaFuJ8H6kYIOPofsZ1JT5mgdwIlebtvnZ4=
|
||||
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6 h1:JJfDeYYhWunvtxsU/mOVNTmFQmnzGx9dY034qG6G3g4=
|
||||
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6/go.mod h1:EX3RbZvrwAkPI2nuGa78T2iQXmrkT+/VQtskjou42xM=
|
||||
github.com/sagernet/sing-tun v0.0.0-20220909114108-a6b5a9289ecb h1:/swVU2mgwDwZ9l67v1Sim1ias/ZmriGTxQLnMakPhtQ=
|
||||
github.com/sagernet/sing-tun v0.0.0-20220909114108-a6b5a9289ecb/go.mod h1:5AhPUv9jWDQ3pv3Mj78SL/1TSjhoaj6WNASxRKLqXqM=
|
||||
github.com/sagernet/sing-tun v0.0.0-20220828031750-185b6c880a83 h1:SoWiHYuOCVedqA7T/CJSZUUrcPGKQb2wFKEq8DphiAI=
|
||||
github.com/sagernet/sing-tun v0.0.0-20220828031750-185b6c880a83/go.mod h1:76r07HS1WRcEI4mE9pFsohfTBUt1j/G9Avz6DaOP3VU=
|
||||
github.com/sagernet/sing-vmess v0.0.0-20220907073918-72d7fdf6825f h1:6l9aXZqAl1JqXJWi89KHpWnM/moQUPGG+XiwMc+yD0A=
|
||||
github.com/sagernet/sing-vmess v0.0.0-20220907073918-72d7fdf6825f/go.mod h1:u66Vv7NHXJWfeAmhh7JuJp/cwxmuQlM56QoZ7B7Mmd0=
|
||||
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195 h1:5VBIbVw9q7aKbrFdT83mjkyvQ+VaRsQ6yflTepfln38=
|
||||
@@ -197,8 +187,6 @@ github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
|
||||
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
|
||||
@@ -217,7 +205,6 @@ golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaE
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM=
|
||||
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
@@ -250,11 +237,9 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211111160137-58aab5ef257a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220630215102-69896b714898/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20220909164309-bea034e7d591 h1:D0B/7al0LLrVC8aWF4+oxpv/m8bc7ViFfVS8/gXGdqI=
|
||||
golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b h1:ZmngSVLe/wycRns9MKikG9OWIEjGcGAkacif7oYQaUY=
|
||||
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -263,7 +248,6 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -277,7 +261,6 @@ golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -288,13 +271,12 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220909162455-aba9fc2a8ff2 h1:wM1k/lXfpc5HdkJJyW9GELpd8ERGdnh8sMGL6Gzq3Ho=
|
||||
golang.org/x/sys v0.0.0-20220909162455-aba9fc2a8ff2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 h1:v6hYoSR9T5oet+pMXwUWkbiVqx/63mlHjefrHmxwfeY=
|
||||
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
@@ -321,7 +303,6 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.11-0.20220513221640-090b14e8501f h1:OKYpQQVE3DKSc3r3zHVzq46vq5YH7x8xpR3/k9ixmUg=
|
||||
golang.org/x/tools v0.1.11-0.20220513221640-090b14e8501f/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -330,8 +311,8 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 h1:Ug9qvr1myri/zFN6xL17LSCBGFDnphBBhzmILHsM5TY=
|
||||
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20220904105730-b51010ba13f0 h1:5ZkdpbduT/g+9OtbSDvbF3KvfQG45CtH/ppO8FUmvCQ=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20220904105730-b51010ba13f0/go.mod h1:enML0deDxY1ux+B6ANGiwtg0yAJi1rctkTpcHNAVPyg=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20220829161405-d1d08426b27b h1:qgrKnOfe1zyURRNdmDlGbN32i38Zjmw0B1+TMdHcOvg=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20220829161405-d1d08426b27b/go.mod h1:6y4CqPAy54NwiN4nC8K+R1eMpQDB1P2d25qmunh2RSA=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
@@ -373,8 +354,9 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
@@ -382,8 +364,8 @@ gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
|
||||
gotest.tools/v3 v3.3.0 h1:MfDY1b1/0xN1CyMlQDac0ziEy9zJQd9CXBRRDHw2jJo=
|
||||
gotest.tools/v3 v3.3.0/go.mod h1:Mcr9QNxkg0uMvy/YElmo4SpXgJKWgQvYrT7Kw5RzJ1A=
|
||||
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c h1:m5lcgWnL3OElQNVyp3qcncItJ2c0sQlSGjYK2+nJTA4=
|
||||
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM=
|
||||
gvisor.dev/gvisor v0.0.0-20220819163037-ba6e795b139a h1:W1h3JsEzYWg7eD4908iHv49p7AOx7JPKsoh/fsxgylM=
|
||||
gvisor.dev/gvisor v0.0.0-20220819163037-ba6e795b139a/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0=
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"testing"
|
||||
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
)
|
||||
|
||||
func TestUTLS(t *testing.T) {
|
||||
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
Tag: "mixed-in",
|
||||
MixedOptions: option.HTTPMixedInboundOptions{
|
||||
ListenOptions: option.ListenOptions{
|
||||
Listen: option.ListenAddress(netip.IPv4Unspecified()),
|
||||
ListenPort: clientPort,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: C.TypeTrojan,
|
||||
TrojanOptions: option.TrojanInboundOptions{
|
||||
ListenOptions: option.ListenOptions{
|
||||
Listen: option.ListenAddress(netip.IPv4Unspecified()),
|
||||
ListenPort: serverPort,
|
||||
},
|
||||
Users: []option.TrojanUser{
|
||||
{
|
||||
Name: "sekai",
|
||||
Password: "password",
|
||||
},
|
||||
},
|
||||
TLS: &option.InboundTLSOptions{
|
||||
Enabled: true,
|
||||
ServerName: "example.org",
|
||||
CertificatePath: certPem,
|
||||
KeyPath: keyPem,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Outbounds: []option.Outbound{
|
||||
{
|
||||
Type: C.TypeDirect,
|
||||
},
|
||||
{
|
||||
Type: C.TypeTrojan,
|
||||
Tag: "trojan-out",
|
||||
TrojanOptions: option.TrojanOutboundOptions{
|
||||
ServerOptions: option.ServerOptions{
|
||||
Server: "127.0.0.1",
|
||||
ServerPort: serverPort,
|
||||
},
|
||||
Password: "password",
|
||||
TLS: &option.OutboundTLSOptions{
|
||||
Enabled: true,
|
||||
ServerName: "example.org",
|
||||
CertificatePath: certPem,
|
||||
UTLS: &option.OutboundUTLSOptions{
|
||||
Enabled: true,
|
||||
Fingerprint: "chrome",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Route: &option.RouteOptions{
|
||||
Rules: []option.Rule{
|
||||
{
|
||||
DefaultOptions: option.DefaultRule{
|
||||
Inbound: []string{"mixed-in"},
|
||||
Outbound: "trojan-out",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
testSuit(t, clientPort, testPort)
|
||||
}
|
||||
@@ -40,7 +40,7 @@ func TestWireGuard(t *testing.T) {
|
||||
Server: "127.0.0.1",
|
||||
ServerPort: serverPort,
|
||||
},
|
||||
LocalAddress: []option.ListenPrefix{option.ListenPrefix(netip.MustParsePrefix("10.0.0.2/32"))},
|
||||
LocalAddress: []string{"10.0.0.2/32"},
|
||||
PrivateKey: "qGnwlkZljMxeECW8fbwAWdvgntnbK7B8UmMFl3zM0mk=",
|
||||
PeerPublicKey: "QsdcBm+oJw2oNv0cIFXLIq1E850lgTBonup4qnKEQBg=",
|
||||
},
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
# cloudflare-tls
|
||||
|
||||
kanged from https://github.com/cloudflare/go
|
||||
branch: cf
|
||||
commit: 4d2a840e50d2b4316aa19934271832d080c44f7f
|
||||
go: 1.18.5
|
||||
changes: use github.com/cloudflare/circl 4cf0150356fc62a0ea5c0eec2f64b756cb404145
|
||||
@@ -1,101 +0,0 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package tls
|
||||
|
||||
import "strconv"
|
||||
|
||||
type alert uint8
|
||||
|
||||
const (
|
||||
// alert level
|
||||
alertLevelWarning = 1
|
||||
alertLevelError = 2
|
||||
)
|
||||
|
||||
const (
|
||||
alertCloseNotify alert = 0
|
||||
alertUnexpectedMessage alert = 10
|
||||
alertBadRecordMAC alert = 20
|
||||
alertDecryptionFailed alert = 21
|
||||
alertRecordOverflow alert = 22
|
||||
alertDecompressionFailure alert = 30
|
||||
alertHandshakeFailure alert = 40
|
||||
alertBadCertificate alert = 42
|
||||
alertUnsupportedCertificate alert = 43
|
||||
alertCertificateRevoked alert = 44
|
||||
alertCertificateExpired alert = 45
|
||||
alertCertificateUnknown alert = 46
|
||||
alertIllegalParameter alert = 47
|
||||
alertUnknownCA alert = 48
|
||||
alertAccessDenied alert = 49
|
||||
alertDecodeError alert = 50
|
||||
alertDecryptError alert = 51
|
||||
alertExportRestriction alert = 60
|
||||
alertProtocolVersion alert = 70
|
||||
alertInsufficientSecurity alert = 71
|
||||
alertInternalError alert = 80
|
||||
alertInappropriateFallback alert = 86
|
||||
alertUserCanceled alert = 90
|
||||
alertNoRenegotiation alert = 100
|
||||
alertMissingExtension alert = 109
|
||||
alertUnsupportedExtension alert = 110
|
||||
alertCertificateUnobtainable alert = 111
|
||||
alertUnrecognizedName alert = 112
|
||||
alertBadCertificateStatusResponse alert = 113
|
||||
alertBadCertificateHashValue alert = 114
|
||||
alertUnknownPSKIdentity alert = 115
|
||||
alertCertificateRequired alert = 116
|
||||
alertNoApplicationProtocol alert = 120
|
||||
alertECHRequired alert = 121
|
||||
)
|
||||
|
||||
var alertText = map[alert]string{
|
||||
alertCloseNotify: "close notify",
|
||||
alertUnexpectedMessage: "unexpected message",
|
||||
alertBadRecordMAC: "bad record MAC",
|
||||
alertDecryptionFailed: "decryption failed",
|
||||
alertRecordOverflow: "record overflow",
|
||||
alertDecompressionFailure: "decompression failure",
|
||||
alertHandshakeFailure: "handshake failure",
|
||||
alertBadCertificate: "bad certificate",
|
||||
alertUnsupportedCertificate: "unsupported certificate",
|
||||
alertCertificateRevoked: "revoked certificate",
|
||||
alertCertificateExpired: "expired certificate",
|
||||
alertCertificateUnknown: "unknown certificate",
|
||||
alertIllegalParameter: "illegal parameter",
|
||||
alertUnknownCA: "unknown certificate authority",
|
||||
alertAccessDenied: "access denied",
|
||||
alertDecodeError: "error decoding message",
|
||||
alertDecryptError: "error decrypting message",
|
||||
alertExportRestriction: "export restriction",
|
||||
alertProtocolVersion: "protocol version not supported",
|
||||
alertInsufficientSecurity: "insufficient security level",
|
||||
alertInternalError: "internal error",
|
||||
alertInappropriateFallback: "inappropriate fallback",
|
||||
alertUserCanceled: "user canceled",
|
||||
alertNoRenegotiation: "no renegotiation",
|
||||
alertMissingExtension: "missing extension",
|
||||
alertUnsupportedExtension: "unsupported extension",
|
||||
alertCertificateUnobtainable: "certificate unobtainable",
|
||||
alertUnrecognizedName: "unrecognized name",
|
||||
alertBadCertificateStatusResponse: "bad certificate status response",
|
||||
alertBadCertificateHashValue: "bad certificate hash value",
|
||||
alertUnknownPSKIdentity: "unknown PSK identity",
|
||||
alertCertificateRequired: "certificate required",
|
||||
alertNoApplicationProtocol: "no application protocol",
|
||||
alertECHRequired: "ECH required",
|
||||
}
|
||||
|
||||
func (e alert) String() string {
|
||||
s, ok := alertText[e]
|
||||
if ok {
|
||||
return "tls: " + s
|
||||
}
|
||||
return "tls: alert(" + strconv.Itoa(int(e)) + ")"
|
||||
}
|
||||
|
||||
func (e alert) Error() string {
|
||||
return e.String()
|
||||
}
|
||||
@@ -1,345 +0,0 @@
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package tls
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/ed25519"
|
||||
"crypto/elliptic"
|
||||
"crypto/rsa"
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash"
|
||||
"io"
|
||||
|
||||
circlPki "github.com/cloudflare/circl/pki"
|
||||
circlSign "github.com/cloudflare/circl/sign"
|
||||
)
|
||||
|
||||
// verifyHandshakeSignature verifies a signature against pre-hashed
|
||||
// (if required) handshake contents.
|
||||
func verifyHandshakeSignature(sigType uint8, pubkey crypto.PublicKey, hashFunc crypto.Hash, signed, sig []byte) error {
|
||||
switch sigType {
|
||||
case signatureECDSA:
|
||||
pubKey, ok := pubkey.(*ecdsa.PublicKey)
|
||||
if !ok {
|
||||
return fmt.Errorf("expected an ECDSA public key, got %T", pubkey)
|
||||
}
|
||||
if !ecdsa.VerifyASN1(pubKey, signed, sig) {
|
||||
return errors.New("ECDSA verification failure")
|
||||
}
|
||||
case signatureEd25519:
|
||||
pubKey, ok := pubkey.(ed25519.PublicKey)
|
||||
if !ok {
|
||||
return fmt.Errorf("expected an Ed25519 public key, got %T", pubkey)
|
||||
}
|
||||
if !ed25519.Verify(pubKey, signed, sig) {
|
||||
return errors.New("Ed25519 verification failure")
|
||||
}
|
||||
case signaturePKCS1v15:
|
||||
pubKey, ok := pubkey.(*rsa.PublicKey)
|
||||
if !ok {
|
||||
return fmt.Errorf("expected an RSA public key, got %T", pubkey)
|
||||
}
|
||||
if err := rsa.VerifyPKCS1v15(pubKey, hashFunc, signed, sig); err != nil {
|
||||
return err
|
||||
}
|
||||
case signatureRSAPSS:
|
||||
pubKey, ok := pubkey.(*rsa.PublicKey)
|
||||
if !ok {
|
||||
return fmt.Errorf("expected an RSA public key, got %T", pubkey)
|
||||
}
|
||||
signOpts := &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash}
|
||||
if err := rsa.VerifyPSS(pubKey, hashFunc, signed, sig, signOpts); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
scheme := circlSchemeBySigType(sigType)
|
||||
if scheme == nil {
|
||||
return errors.New("internal error: unknown signature type")
|
||||
}
|
||||
pubKey, ok := pubkey.(circlSign.PublicKey)
|
||||
if !ok {
|
||||
return fmt.Errorf("expected a %s public key, got %T", scheme.Name(), pubkey)
|
||||
}
|
||||
if !scheme.Verify(pubKey, signed, sig, nil) {
|
||||
return fmt.Errorf("%s verification failure", scheme.Name())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
serverSignatureContext = "TLS 1.3, server CertificateVerify\x00"
|
||||
clientSignatureContext = "TLS 1.3, client CertificateVerify\x00"
|
||||
)
|
||||
|
||||
var signaturePadding = []byte{
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
}
|
||||
|
||||
// signedMessage returns the pre-hashed (if necessary) message to be signed by
|
||||
// certificate keys in TLS 1.3. See RFC 8446, Section 4.4.3.
|
||||
func signedMessage(sigHash crypto.Hash, context string, transcript hash.Hash) []byte {
|
||||
if sigHash == directSigning {
|
||||
b := &bytes.Buffer{}
|
||||
b.Write(signaturePadding)
|
||||
io.WriteString(b, context)
|
||||
b.Write(transcript.Sum(nil))
|
||||
return b.Bytes()
|
||||
}
|
||||
h := sigHash.New()
|
||||
h.Write(signaturePadding)
|
||||
io.WriteString(h, context)
|
||||
h.Write(transcript.Sum(nil))
|
||||
return h.Sum(nil)
|
||||
}
|
||||
|
||||
// typeAndHashFromSignatureScheme returns the corresponding signature type and
|
||||
// crypto.Hash for a given TLS SignatureScheme.
|
||||
func typeAndHashFromSignatureScheme(signatureAlgorithm SignatureScheme) (sigType uint8, hash crypto.Hash, err error) {
|
||||
switch signatureAlgorithm {
|
||||
case PKCS1WithSHA1, PKCS1WithSHA256, PKCS1WithSHA384, PKCS1WithSHA512:
|
||||
sigType = signaturePKCS1v15
|
||||
case PSSWithSHA256, PSSWithSHA384, PSSWithSHA512:
|
||||
sigType = signatureRSAPSS
|
||||
case ECDSAWithSHA1, ECDSAWithP256AndSHA256, ECDSAWithP384AndSHA384, ECDSAWithP521AndSHA512:
|
||||
sigType = signatureECDSA
|
||||
case Ed25519:
|
||||
sigType = signatureEd25519
|
||||
default:
|
||||
scheme := circlPki.SchemeByTLSID(uint(signatureAlgorithm))
|
||||
if scheme == nil {
|
||||
return 0, 0, fmt.Errorf("unsupported signature algorithm: %v", signatureAlgorithm)
|
||||
}
|
||||
sigType = sigTypeByCirclScheme(scheme)
|
||||
if sigType == 0 {
|
||||
return 0, 0, fmt.Errorf("github.com/cloudflare/circl scheme %s not supported",
|
||||
scheme.Name())
|
||||
}
|
||||
}
|
||||
switch signatureAlgorithm {
|
||||
case PKCS1WithSHA1, ECDSAWithSHA1:
|
||||
hash = crypto.SHA1
|
||||
case PKCS1WithSHA256, PSSWithSHA256, ECDSAWithP256AndSHA256:
|
||||
hash = crypto.SHA256
|
||||
case PKCS1WithSHA384, PSSWithSHA384, ECDSAWithP384AndSHA384:
|
||||
hash = crypto.SHA384
|
||||
case PKCS1WithSHA512, PSSWithSHA512, ECDSAWithP521AndSHA512:
|
||||
hash = crypto.SHA512
|
||||
case Ed25519:
|
||||
hash = directSigning
|
||||
default:
|
||||
scheme := circlPki.SchemeByTLSID(uint(signatureAlgorithm))
|
||||
if scheme == nil {
|
||||
return 0, 0, fmt.Errorf("unsupported signature algorithm: %v", signatureAlgorithm)
|
||||
}
|
||||
hash = directSigning
|
||||
}
|
||||
return sigType, hash, nil
|
||||
}
|
||||
|
||||
// legacyTypeAndHashFromPublicKey returns the fixed signature type and crypto.Hash for
|
||||
// a given public key used with TLS 1.0 and 1.1, before the introduction of
|
||||
// signature algorithm negotiation.
|
||||
func legacyTypeAndHashFromPublicKey(pub crypto.PublicKey) (sigType uint8, hash crypto.Hash, err error) {
|
||||
switch pub.(type) {
|
||||
case *rsa.PublicKey:
|
||||
return signaturePKCS1v15, crypto.MD5SHA1, nil
|
||||
case *ecdsa.PublicKey:
|
||||
return signatureECDSA, crypto.SHA1, nil
|
||||
case ed25519.PublicKey:
|
||||
// RFC 8422 specifies support for Ed25519 in TLS 1.0 and 1.1,
|
||||
// but it requires holding on to a handshake transcript to do a
|
||||
// full signature, and not even OpenSSL bothers with the
|
||||
// complexity, so we can't even test it properly.
|
||||
return 0, 0, fmt.Errorf("tls: Ed25519 public keys are not supported before TLS 1.2")
|
||||
case circlSign.PublicKey:
|
||||
return 0, 0, fmt.Errorf("tls: circl public keys are not supported before TLS 1.2")
|
||||
default:
|
||||
return 0, 0, fmt.Errorf("tls: unsupported public key: %T", pub)
|
||||
}
|
||||
}
|
||||
|
||||
var rsaSignatureSchemes = []struct {
|
||||
scheme SignatureScheme
|
||||
minModulusBytes int
|
||||
maxVersion uint16
|
||||
}{
|
||||
// RSA-PSS is used with PSSSaltLengthEqualsHash, and requires
|
||||
// emLen >= hLen + sLen + 2
|
||||
{PSSWithSHA256, crypto.SHA256.Size()*2 + 2, VersionTLS13},
|
||||
{PSSWithSHA384, crypto.SHA384.Size()*2 + 2, VersionTLS13},
|
||||
{PSSWithSHA512, crypto.SHA512.Size()*2 + 2, VersionTLS13},
|
||||
// PKCS #1 v1.5 uses prefixes from hashPrefixes in crypto/rsa, and requires
|
||||
// emLen >= len(prefix) + hLen + 11
|
||||
// TLS 1.3 dropped support for PKCS #1 v1.5 in favor of RSA-PSS.
|
||||
{PKCS1WithSHA256, 19 + crypto.SHA256.Size() + 11, VersionTLS12},
|
||||
{PKCS1WithSHA384, 19 + crypto.SHA384.Size() + 11, VersionTLS12},
|
||||
{PKCS1WithSHA512, 19 + crypto.SHA512.Size() + 11, VersionTLS12},
|
||||
{PKCS1WithSHA1, 15 + crypto.SHA1.Size() + 11, VersionTLS12},
|
||||
}
|
||||
|
||||
// signatureSchemesForCertificate returns the list of supported SignatureSchemes
|
||||
// for a given certificate, based on the public key and the protocol version,
|
||||
// and optionally filtered by its explicit SupportedSignatureAlgorithms.
|
||||
//
|
||||
// This function must be kept in sync with supportedSignatureAlgorithms.
|
||||
func signatureSchemesForCertificate(version uint16, cert *Certificate) []SignatureScheme {
|
||||
priv, ok := cert.PrivateKey.(crypto.Signer)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
var sigAlgs []SignatureScheme
|
||||
switch pub := priv.Public().(type) {
|
||||
case *ecdsa.PublicKey:
|
||||
if version != VersionTLS13 {
|
||||
// In TLS 1.2 and earlier, ECDSA algorithms are not
|
||||
// constrained to a single curve.
|
||||
sigAlgs = []SignatureScheme{
|
||||
ECDSAWithP256AndSHA256,
|
||||
ECDSAWithP384AndSHA384,
|
||||
ECDSAWithP521AndSHA512,
|
||||
ECDSAWithSHA1,
|
||||
}
|
||||
break
|
||||
}
|
||||
switch pub.Curve {
|
||||
case elliptic.P256():
|
||||
sigAlgs = []SignatureScheme{ECDSAWithP256AndSHA256}
|
||||
case elliptic.P384():
|
||||
sigAlgs = []SignatureScheme{ECDSAWithP384AndSHA384}
|
||||
case elliptic.P521():
|
||||
sigAlgs = []SignatureScheme{ECDSAWithP521AndSHA512}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
case *rsa.PublicKey:
|
||||
size := pub.Size()
|
||||
sigAlgs = make([]SignatureScheme, 0, len(rsaSignatureSchemes))
|
||||
for _, candidate := range rsaSignatureSchemes {
|
||||
if size >= candidate.minModulusBytes && version <= candidate.maxVersion {
|
||||
sigAlgs = append(sigAlgs, candidate.scheme)
|
||||
}
|
||||
}
|
||||
case ed25519.PublicKey:
|
||||
sigAlgs = []SignatureScheme{Ed25519}
|
||||
case circlSign.PublicKey:
|
||||
scheme := pub.Scheme()
|
||||
tlsScheme, ok := scheme.(circlPki.TLSScheme)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
sigAlgs = []SignatureScheme{SignatureScheme(tlsScheme.TLSIdentifier())}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
if cert.SupportedSignatureAlgorithms != nil {
|
||||
var filteredSigAlgs []SignatureScheme
|
||||
for _, sigAlg := range sigAlgs {
|
||||
if isSupportedSignatureAlgorithm(sigAlg, cert.SupportedSignatureAlgorithms) {
|
||||
filteredSigAlgs = append(filteredSigAlgs, sigAlg)
|
||||
}
|
||||
}
|
||||
return filteredSigAlgs
|
||||
}
|
||||
return sigAlgs
|
||||
}
|
||||
|
||||
// selectSignatureSchemeDC picks a SignatureScheme from the peer's preference list
|
||||
// that works with the selected delegated credential. It's only called for protocol
|
||||
// versions that support delegated credential, so TLS 1.3.
|
||||
func selectSignatureSchemeDC(vers uint16, dc *DelegatedCredential, peerAlgs []SignatureScheme, peerAlgsDC []SignatureScheme) (SignatureScheme, error) {
|
||||
if vers != VersionTLS13 {
|
||||
return 0, errors.New("unsupported TLS version for dc")
|
||||
}
|
||||
|
||||
if !isSupportedSignatureAlgorithm(dc.algorithm, peerAlgs) {
|
||||
return undefinedSignatureScheme, errors.New("tls: peer doesn't support the delegated credential's signature")
|
||||
}
|
||||
|
||||
// Pick signature scheme in the peer's preference order, as our
|
||||
// preference order is not configurable.
|
||||
for _, preferredAlg := range peerAlgsDC {
|
||||
if preferredAlg == dc.cred.expCertVerfAlgo {
|
||||
return preferredAlg, nil
|
||||
}
|
||||
}
|
||||
return 0, errors.New("tls: peer doesn't support the delegated credential's signature algorithm")
|
||||
}
|
||||
|
||||
// selectSignatureScheme picks a SignatureScheme from the peer's preference list
|
||||
// that works with the selected certificate. It's only called for protocol
|
||||
// versions that support signature algorithms, so TLS 1.2 and 1.3.
|
||||
func selectSignatureScheme(vers uint16, c *Certificate, peerAlgs []SignatureScheme) (SignatureScheme, error) {
|
||||
supportedAlgs := signatureSchemesForCertificate(vers, c)
|
||||
if len(supportedAlgs) == 0 {
|
||||
return 0, unsupportedCertificateError(c)
|
||||
}
|
||||
if len(peerAlgs) == 0 && vers == VersionTLS12 {
|
||||
// For TLS 1.2, if the client didn't send signature_algorithms then we
|
||||
// can assume that it supports SHA1. See RFC 5246, Section 7.4.1.4.1.
|
||||
peerAlgs = []SignatureScheme{PKCS1WithSHA1, ECDSAWithSHA1}
|
||||
}
|
||||
// Pick signature scheme in the peer's preference order, as our
|
||||
// preference order is not configurable.
|
||||
for _, preferredAlg := range peerAlgs {
|
||||
if isSupportedSignatureAlgorithm(preferredAlg, supportedAlgs) {
|
||||
return preferredAlg, nil
|
||||
}
|
||||
}
|
||||
return 0, errors.New("tls: peer doesn't support any of the certificate's signature algorithms")
|
||||
}
|
||||
|
||||
// unsupportedCertificateError returns a helpful error for certificates with
|
||||
// an unsupported private key.
|
||||
func unsupportedCertificateError(cert *Certificate) error {
|
||||
switch cert.PrivateKey.(type) {
|
||||
case rsa.PrivateKey, ecdsa.PrivateKey:
|
||||
return fmt.Errorf("tls: unsupported certificate: private key is %T, expected *%T",
|
||||
cert.PrivateKey, cert.PrivateKey)
|
||||
case *ed25519.PrivateKey:
|
||||
return fmt.Errorf("tls: unsupported certificate: private key is *ed25519.PrivateKey, expected ed25519.PrivateKey")
|
||||
}
|
||||
|
||||
signer, ok := cert.PrivateKey.(crypto.Signer)
|
||||
if !ok {
|
||||
return fmt.Errorf("tls: certificate private key (%T) does not implement crypto.Signer",
|
||||
cert.PrivateKey)
|
||||
}
|
||||
|
||||
switch pub := signer.Public().(type) {
|
||||
case *ecdsa.PublicKey:
|
||||
switch pub.Curve {
|
||||
case elliptic.P256():
|
||||
case elliptic.P384():
|
||||
case elliptic.P521():
|
||||
default:
|
||||
return fmt.Errorf("tls: unsupported certificate curve (%s)", pub.Curve.Params().Name)
|
||||
}
|
||||
case *rsa.PublicKey:
|
||||
return fmt.Errorf("tls: certificate RSA key size too small for supported signature algorithms")
|
||||
case ed25519.PublicKey:
|
||||
default:
|
||||
return fmt.Errorf("tls: unsupported certificate key (%T)", pub)
|
||||
}
|
||||
|
||||
if cert.SupportedSignatureAlgorithms != nil {
|
||||
return fmt.Errorf("tls: peer doesn't support the certificate custom signature algorithms")
|
||||
}
|
||||
|
||||
return fmt.Errorf("tls: internal error: unsupported key (%T)", cert.PrivateKey)
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
// Copyright 2022 Cloudflare, Inc. All rights reserved. Use of this source code
|
||||
// is governed by a BSD-style license that can be found in the LICENSE file.
|
||||
//
|
||||
// Glue to add Circl's (post-quantum) hybrid KEMs.
|
||||
//
|
||||
// To enable set CurvePreferences with the desired scheme as the first element:
|
||||
//
|
||||
// import (
|
||||
// "github.com/cloudflare/circl/kem/tls"
|
||||
// "github.com/cloudflare/circl/kem/hybrid"
|
||||
//
|
||||
// [...]
|
||||
//
|
||||
// config.CurvePreferences = []tls.CurveID{
|
||||
// hybrid.X25519Kyber512Draft00().(tls.TLSScheme).TLSCurveID(),
|
||||
// tls.X25519,
|
||||
// tls.P256,
|
||||
// }
|
||||
|
||||
package tls
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/cloudflare/circl/kem"
|
||||
"github.com/cloudflare/circl/kem/hybrid"
|
||||
)
|
||||
|
||||
// Either ecdheParameters or kem.PrivateKey
|
||||
type clientKeySharePrivate interface{}
|
||||
|
||||
var (
|
||||
X25519Kyber512Draft00 = CurveID(0xfe30)
|
||||
X25519Kyber768Draft00 = CurveID(0xfe31)
|
||||
invalidCurveID = CurveID(0)
|
||||
)
|
||||
|
||||
func kemSchemeKeyToCurveID(s kem.Scheme) CurveID {
|
||||
switch s.Name() {
|
||||
case "Kyber512-X25519":
|
||||
return X25519Kyber512Draft00
|
||||
case "Kyber768-X25519":
|
||||
return X25519Kyber768Draft00
|
||||
default:
|
||||
return invalidCurveID
|
||||
}
|
||||
}
|
||||
|
||||
// Extract CurveID from clientKeySharePrivate
|
||||
func clientKeySharePrivateCurveID(ks clientKeySharePrivate) CurveID {
|
||||
switch v := ks.(type) {
|
||||
case kem.PrivateKey:
|
||||
ret := kemSchemeKeyToCurveID(v.Scheme())
|
||||
if ret == invalidCurveID {
|
||||
panic("cfkem: internal error: don't know CurveID for this KEM")
|
||||
}
|
||||
return ret
|
||||
case ecdheParameters:
|
||||
return v.CurveID()
|
||||
default:
|
||||
panic("cfkem: internal error: unknown clientKeySharePrivate")
|
||||
}
|
||||
}
|
||||
|
||||
// Returns scheme by CurveID if supported by Circl
|
||||
func curveIdToCirclScheme(id CurveID) kem.Scheme {
|
||||
switch id {
|
||||
case X25519Kyber512Draft00:
|
||||
return hybrid.Kyber512X25519()
|
||||
case X25519Kyber768Draft00:
|
||||
return hybrid.Kyber768X25519()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Generate a new shared secret and encapsulates it for the packed
|
||||
// public key in ppk using randomness from rnd.
|
||||
func encapsulateForKem(scheme kem.Scheme, rnd io.Reader, ppk []byte) (
|
||||
ct, ss []byte, alert alert, err error,
|
||||
) {
|
||||
pk, err := scheme.UnmarshalBinaryPublicKey(ppk)
|
||||
if err != nil {
|
||||
return nil, nil, alertIllegalParameter, fmt.Errorf("unpack pk: %w", err)
|
||||
}
|
||||
seed := make([]byte, scheme.EncapsulationSeedSize())
|
||||
if _, err := io.ReadFull(rnd, seed); err != nil {
|
||||
return nil, nil, alertInternalError, fmt.Errorf("random: %w", err)
|
||||
}
|
||||
ct, ss, err = scheme.EncapsulateDeterministically(pk, seed)
|
||||
return ct, ss, alertIllegalParameter, err
|
||||
}
|
||||
|
||||
// Generate a new keypair using randomness from rnd.
|
||||
func generateKemKeyPair(scheme kem.Scheme, rnd io.Reader) (
|
||||
kem.PublicKey, kem.PrivateKey, error,
|
||||
) {
|
||||
seed := make([]byte, scheme.SeedSize())
|
||||
if _, err := io.ReadFull(rnd, seed); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
pk, sk := scheme.DeriveKeyPair(seed)
|
||||
return pk, sk, nil
|
||||
}
|
||||
@@ -1,688 +0,0 @@
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package tls
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/des"
|
||||
"crypto/hmac"
|
||||
"crypto/rc4"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"hash"
|
||||
"runtime"
|
||||
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
"golang.org/x/sys/cpu"
|
||||
)
|
||||
|
||||
// CipherSuite is a TLS cipher suite. Note that most functions in this package
|
||||
// accept and expose cipher suite IDs instead of this type.
|
||||
type CipherSuite struct {
|
||||
ID uint16
|
||||
Name string
|
||||
|
||||
// Supported versions is the list of TLS protocol versions that can
|
||||
// negotiate this cipher suite.
|
||||
SupportedVersions []uint16
|
||||
|
||||
// Insecure is true if the cipher suite has known security issues
|
||||
// due to its primitives, design, or implementation.
|
||||
Insecure bool
|
||||
}
|
||||
|
||||
var (
|
||||
supportedUpToTLS12 = []uint16{VersionTLS10, VersionTLS11, VersionTLS12}
|
||||
supportedOnlyTLS12 = []uint16{VersionTLS12}
|
||||
supportedOnlyTLS13 = []uint16{VersionTLS13}
|
||||
)
|
||||
|
||||
// CipherSuites returns a list of cipher suites currently implemented by this
|
||||
// package, excluding those with security issues, which are returned by
|
||||
// InsecureCipherSuites.
|
||||
//
|
||||
// The list is sorted by ID. Note that the default cipher suites selected by
|
||||
// this package might depend on logic that can't be captured by a static list,
|
||||
// and might not match those returned by this function.
|
||||
func CipherSuites() []*CipherSuite {
|
||||
return []*CipherSuite{
|
||||
{TLS_RSA_WITH_AES_128_CBC_SHA, "TLS_RSA_WITH_AES_128_CBC_SHA", supportedUpToTLS12, false},
|
||||
{TLS_RSA_WITH_AES_256_CBC_SHA, "TLS_RSA_WITH_AES_256_CBC_SHA", supportedUpToTLS12, false},
|
||||
{TLS_RSA_WITH_AES_128_GCM_SHA256, "TLS_RSA_WITH_AES_128_GCM_SHA256", supportedOnlyTLS12, false},
|
||||
{TLS_RSA_WITH_AES_256_GCM_SHA384, "TLS_RSA_WITH_AES_256_GCM_SHA384", supportedOnlyTLS12, false},
|
||||
|
||||
{TLS_AES_128_GCM_SHA256, "TLS_AES_128_GCM_SHA256", supportedOnlyTLS13, false},
|
||||
{TLS_AES_256_GCM_SHA384, "TLS_AES_256_GCM_SHA384", supportedOnlyTLS13, false},
|
||||
{TLS_CHACHA20_POLY1305_SHA256, "TLS_CHACHA20_POLY1305_SHA256", supportedOnlyTLS13, false},
|
||||
|
||||
{TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", supportedUpToTLS12, false},
|
||||
{TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", supportedUpToTLS12, false},
|
||||
{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", supportedUpToTLS12, false},
|
||||
{TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", supportedUpToTLS12, false},
|
||||
{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", supportedOnlyTLS12, false},
|
||||
{TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", supportedOnlyTLS12, false},
|
||||
{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", supportedOnlyTLS12, false},
|
||||
{TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", supportedOnlyTLS12, false},
|
||||
{TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", supportedOnlyTLS12, false},
|
||||
{TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", supportedOnlyTLS12, false},
|
||||
}
|
||||
}
|
||||
|
||||
// InsecureCipherSuites returns a list of cipher suites currently implemented by
|
||||
// this package and which have security issues.
|
||||
//
|
||||
// Most applications should not use the cipher suites in this list, and should
|
||||
// only use those returned by CipherSuites.
|
||||
func InsecureCipherSuites() []*CipherSuite {
|
||||
// This list includes RC4, CBC_SHA256, and 3DES cipher suites. See
|
||||
// cipherSuitesPreferenceOrder for details.
|
||||
return []*CipherSuite{
|
||||
{TLS_RSA_WITH_RC4_128_SHA, "TLS_RSA_WITH_RC4_128_SHA", supportedUpToTLS12, true},
|
||||
{TLS_RSA_WITH_3DES_EDE_CBC_SHA, "TLS_RSA_WITH_3DES_EDE_CBC_SHA", supportedUpToTLS12, true},
|
||||
{TLS_RSA_WITH_AES_128_CBC_SHA256, "TLS_RSA_WITH_AES_128_CBC_SHA256", supportedOnlyTLS12, true},
|
||||
{TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", supportedUpToTLS12, true},
|
||||
{TLS_ECDHE_RSA_WITH_RC4_128_SHA, "TLS_ECDHE_RSA_WITH_RC4_128_SHA", supportedUpToTLS12, true},
|
||||
{TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", supportedUpToTLS12, true},
|
||||
{TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", supportedOnlyTLS12, true},
|
||||
{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", supportedOnlyTLS12, true},
|
||||
}
|
||||
}
|
||||
|
||||
// CipherSuiteName returns the standard name for the passed cipher suite ID
|
||||
// (e.g. "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"), or a fallback representation
|
||||
// of the ID value if the cipher suite is not implemented by this package.
|
||||
func CipherSuiteName(id uint16) string {
|
||||
for _, c := range CipherSuites() {
|
||||
if c.ID == id {
|
||||
return c.Name
|
||||
}
|
||||
}
|
||||
for _, c := range InsecureCipherSuites() {
|
||||
if c.ID == id {
|
||||
return c.Name
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("0x%04X", id)
|
||||
}
|
||||
|
||||
const (
|
||||
// suiteECDHE indicates that the cipher suite involves elliptic curve
|
||||
// Diffie-Hellman. This means that it should only be selected when the
|
||||
// client indicates that it supports ECC with a curve and point format
|
||||
// that we're happy with.
|
||||
suiteECDHE = 1 << iota
|
||||
// suiteECSign indicates that the cipher suite involves an ECDSA or
|
||||
// EdDSA signature and therefore may only be selected when the server's
|
||||
// certificate is ECDSA or EdDSA. If this is not set then the cipher suite
|
||||
// is RSA based.
|
||||
suiteECSign
|
||||
// suiteTLS12 indicates that the cipher suite should only be advertised
|
||||
// and accepted when using TLS 1.2.
|
||||
suiteTLS12
|
||||
// suiteSHA384 indicates that the cipher suite uses SHA384 as the
|
||||
// handshake hash.
|
||||
suiteSHA384
|
||||
)
|
||||
|
||||
// A cipherSuite is a TLS 1.0–1.2 cipher suite, and defines the key exchange
|
||||
// mechanism, as well as the cipher+MAC pair or the AEAD.
|
||||
type cipherSuite struct {
|
||||
id uint16
|
||||
// the lengths, in bytes, of the key material needed for each component.
|
||||
keyLen int
|
||||
macLen int
|
||||
ivLen int
|
||||
ka func(version uint16) keyAgreement
|
||||
// flags is a bitmask of the suite* values, above.
|
||||
flags int
|
||||
cipher func(key, iv []byte, isRead bool) any
|
||||
mac func(key []byte) hash.Hash
|
||||
aead func(key, fixedNonce []byte) aead
|
||||
}
|
||||
|
||||
var cipherSuites = []*cipherSuite{ // TODO: replace with a map, since the order doesn't matter.
|
||||
{TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, 32, 0, 12, ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadChaCha20Poly1305},
|
||||
{TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, 32, 0, 12, ecdheECDSAKA, suiteECDHE | suiteECSign | suiteTLS12, nil, nil, aeadChaCha20Poly1305},
|
||||
{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadAESGCM},
|
||||
{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheECDSAKA, suiteECDHE | suiteECSign | suiteTLS12, nil, nil, aeadAESGCM},
|
||||
{TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, ecdheRSAKA, suiteECDHE | suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
|
||||
{TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, ecdheECDSAKA, suiteECDHE | suiteECSign | suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
|
||||
{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, ecdheRSAKA, suiteECDHE | suiteTLS12, cipherAES, macSHA256, nil},
|
||||
{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil},
|
||||
{TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, ecdheECDSAKA, suiteECDHE | suiteECSign | suiteTLS12, cipherAES, macSHA256, nil},
|
||||
{TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheECDSAKA, suiteECDHE | suiteECSign, cipherAES, macSHA1, nil},
|
||||
{TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil},
|
||||
{TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheECDSAKA, suiteECDHE | suiteECSign, cipherAES, macSHA1, nil},
|
||||
{TLS_RSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, rsaKA, suiteTLS12, nil, nil, aeadAESGCM},
|
||||
{TLS_RSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, rsaKA, suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
|
||||
{TLS_RSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, rsaKA, suiteTLS12, cipherAES, macSHA256, nil},
|
||||
{TLS_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, rsaKA, 0, cipherAES, macSHA1, nil},
|
||||
{TLS_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, rsaKA, 0, cipherAES, macSHA1, nil},
|
||||
{TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, ecdheRSAKA, suiteECDHE, cipher3DES, macSHA1, nil},
|
||||
{TLS_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, rsaKA, 0, cipher3DES, macSHA1, nil},
|
||||
{TLS_RSA_WITH_RC4_128_SHA, 16, 20, 0, rsaKA, 0, cipherRC4, macSHA1, nil},
|
||||
{TLS_ECDHE_RSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheRSAKA, suiteECDHE, cipherRC4, macSHA1, nil},
|
||||
{TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheECDSAKA, suiteECDHE | suiteECSign, cipherRC4, macSHA1, nil},
|
||||
}
|
||||
|
||||
// selectCipherSuite returns the first TLS 1.0–1.2 cipher suite from ids which
|
||||
// is also in supportedIDs and passes the ok filter.
|
||||
func selectCipherSuite(ids, supportedIDs []uint16, ok func(*cipherSuite) bool) *cipherSuite {
|
||||
for _, id := range ids {
|
||||
candidate := cipherSuiteByID(id)
|
||||
if candidate == nil || !ok(candidate) {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, suppID := range supportedIDs {
|
||||
if id == suppID {
|
||||
return candidate
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// A cipherSuiteTLS13 defines only the pair of the AEAD algorithm and hash
|
||||
// algorithm to be used with HKDF. See RFC 8446, Appendix B.4.
|
||||
type cipherSuiteTLS13 struct {
|
||||
id uint16
|
||||
keyLen int
|
||||
aead func(key, fixedNonce []byte) aead
|
||||
hash crypto.Hash
|
||||
}
|
||||
|
||||
var cipherSuitesTLS13 = []*cipherSuiteTLS13{ // TODO: replace with a map.
|
||||
{TLS_AES_128_GCM_SHA256, 16, aeadAESGCMTLS13, crypto.SHA256},
|
||||
{TLS_CHACHA20_POLY1305_SHA256, 32, aeadChaCha20Poly1305, crypto.SHA256},
|
||||
{TLS_AES_256_GCM_SHA384, 32, aeadAESGCMTLS13, crypto.SHA384},
|
||||
}
|
||||
|
||||
// cipherSuitesPreferenceOrder is the order in which we'll select (on the
|
||||
// server) or advertise (on the client) TLS 1.0–1.2 cipher suites.
|
||||
//
|
||||
// Cipher suites are filtered but not reordered based on the application and
|
||||
// peer's preferences, meaning we'll never select a suite lower in this list if
|
||||
// any higher one is available. This makes it more defensible to keep weaker
|
||||
// cipher suites enabled, especially on the server side where we get the last
|
||||
// word, since there are no known downgrade attacks on cipher suites selection.
|
||||
//
|
||||
// The list is sorted by applying the following priority rules, stopping at the
|
||||
// first (most important) applicable one:
|
||||
//
|
||||
// - Anything else comes before RC4
|
||||
//
|
||||
// RC4 has practically exploitable biases. See https://www.rc4nomore.com.
|
||||
//
|
||||
// - Anything else comes before CBC_SHA256
|
||||
//
|
||||
// SHA-256 variants of the CBC ciphersuites don't implement any Lucky13
|
||||
// countermeasures. See http://www.isg.rhul.ac.uk/tls/Lucky13.html and
|
||||
// https://www.imperialviolet.org/2013/02/04/luckythirteen.html.
|
||||
//
|
||||
// - Anything else comes before 3DES
|
||||
//
|
||||
// 3DES has 64-bit blocks, which makes it fundamentally susceptible to
|
||||
// birthday attacks. See https://sweet32.info.
|
||||
//
|
||||
// - ECDHE comes before anything else
|
||||
//
|
||||
// Once we got the broken stuff out of the way, the most important
|
||||
// property a cipher suite can have is forward secrecy. We don't
|
||||
// implement FFDHE, so that means ECDHE.
|
||||
//
|
||||
// - AEADs come before CBC ciphers
|
||||
//
|
||||
// Even with Lucky13 countermeasures, MAC-then-Encrypt CBC cipher suites
|
||||
// are fundamentally fragile, and suffered from an endless sequence of
|
||||
// padding oracle attacks. See https://eprint.iacr.org/2015/1129,
|
||||
// https://www.imperialviolet.org/2014/12/08/poodleagain.html, and
|
||||
// https://blog.cloudflare.com/yet-another-padding-oracle-in-openssl-cbc-ciphersuites/.
|
||||
//
|
||||
// - AES comes before ChaCha20
|
||||
//
|
||||
// When AES hardware is available, AES-128-GCM and AES-256-GCM are faster
|
||||
// than ChaCha20Poly1305.
|
||||
//
|
||||
// When AES hardware is not available, AES-128-GCM is one or more of: much
|
||||
// slower, way more complex, and less safe (because not constant time)
|
||||
// than ChaCha20Poly1305.
|
||||
//
|
||||
// We use this list if we think both peers have AES hardware, and
|
||||
// cipherSuitesPreferenceOrderNoAES otherwise.
|
||||
//
|
||||
// - AES-128 comes before AES-256
|
||||
//
|
||||
// The only potential advantages of AES-256 are better multi-target
|
||||
// margins, and hypothetical post-quantum properties. Neither apply to
|
||||
// TLS, and AES-256 is slower due to its four extra rounds (which don't
|
||||
// contribute to the advantages above).
|
||||
//
|
||||
// - ECDSA comes before RSA
|
||||
//
|
||||
// The relative order of ECDSA and RSA cipher suites doesn't matter,
|
||||
// as they depend on the certificate. Pick one to get a stable order.
|
||||
var cipherSuitesPreferenceOrder = []uint16{
|
||||
// AEADs w/ ECDHE
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
|
||||
|
||||
// CBC w/ ECDHE
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||
|
||||
// AEADs w/o ECDHE
|
||||
TLS_RSA_WITH_AES_128_GCM_SHA256,
|
||||
TLS_RSA_WITH_AES_256_GCM_SHA384,
|
||||
|
||||
// CBC w/o ECDHE
|
||||
TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||
TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||
|
||||
// 3DES
|
||||
TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
TLS_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
|
||||
// CBC_SHA256
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
|
||||
TLS_RSA_WITH_AES_128_CBC_SHA256,
|
||||
|
||||
// RC4
|
||||
TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA,
|
||||
TLS_RSA_WITH_RC4_128_SHA,
|
||||
}
|
||||
|
||||
var cipherSuitesPreferenceOrderNoAES = []uint16{
|
||||
// ChaCha20Poly1305
|
||||
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
|
||||
|
||||
// AES-GCM w/ ECDHE
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
|
||||
// The rest of cipherSuitesPreferenceOrder.
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||
TLS_RSA_WITH_AES_128_GCM_SHA256,
|
||||
TLS_RSA_WITH_AES_256_GCM_SHA384,
|
||||
TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||
TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||
TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
TLS_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
|
||||
TLS_RSA_WITH_AES_128_CBC_SHA256,
|
||||
TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA,
|
||||
TLS_RSA_WITH_RC4_128_SHA,
|
||||
}
|
||||
|
||||
// disabledCipherSuites are not used unless explicitly listed in
|
||||
// Config.CipherSuites. They MUST be at the end of cipherSuitesPreferenceOrder.
|
||||
var disabledCipherSuites = []uint16{
|
||||
// CBC_SHA256
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
|
||||
TLS_RSA_WITH_AES_128_CBC_SHA256,
|
||||
|
||||
// RC4
|
||||
TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA,
|
||||
TLS_RSA_WITH_RC4_128_SHA,
|
||||
}
|
||||
|
||||
var (
|
||||
defaultCipherSuitesLen = len(cipherSuitesPreferenceOrder) - len(disabledCipherSuites)
|
||||
defaultCipherSuites = cipherSuitesPreferenceOrder[:defaultCipherSuitesLen]
|
||||
)
|
||||
|
||||
// defaultCipherSuitesTLS13 is also the preference order, since there are no
|
||||
// disabled by default TLS 1.3 cipher suites. The same AES vs ChaCha20 logic as
|
||||
// cipherSuitesPreferenceOrder applies.
|
||||
var defaultCipherSuitesTLS13 = []uint16{
|
||||
TLS_AES_128_GCM_SHA256,
|
||||
TLS_AES_256_GCM_SHA384,
|
||||
TLS_CHACHA20_POLY1305_SHA256,
|
||||
}
|
||||
|
||||
var defaultCipherSuitesTLS13NoAES = []uint16{
|
||||
TLS_CHACHA20_POLY1305_SHA256,
|
||||
TLS_AES_128_GCM_SHA256,
|
||||
TLS_AES_256_GCM_SHA384,
|
||||
}
|
||||
|
||||
var (
|
||||
hasGCMAsmAMD64 = cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ
|
||||
hasGCMAsmARM64 = cpu.ARM64.HasAES && cpu.ARM64.HasPMULL
|
||||
// Keep in sync with crypto/aes/cipher_s390x.go.
|
||||
hasGCMAsmS390X = cpu.S390X.HasAES && cpu.S390X.HasAESCBC && cpu.S390X.HasAESCTR &&
|
||||
(cpu.S390X.HasGHASH || cpu.S390X.HasAESGCM)
|
||||
|
||||
hasAESGCMHardwareSupport = runtime.GOARCH == "amd64" && hasGCMAsmAMD64 ||
|
||||
runtime.GOARCH == "arm64" && hasGCMAsmARM64 ||
|
||||
runtime.GOARCH == "s390x" && hasGCMAsmS390X
|
||||
)
|
||||
|
||||
var aesgcmCiphers = map[uint16]bool{
|
||||
// TLS 1.2
|
||||
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: true,
|
||||
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: true,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: true,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: true,
|
||||
// TLS 1.3
|
||||
TLS_AES_128_GCM_SHA256: true,
|
||||
TLS_AES_256_GCM_SHA384: true,
|
||||
}
|
||||
|
||||
var nonAESGCMAEADCiphers = map[uint16]bool{
|
||||
// TLS 1.2
|
||||
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305: true,
|
||||
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305: true,
|
||||
// TLS 1.3
|
||||
TLS_CHACHA20_POLY1305_SHA256: true,
|
||||
}
|
||||
|
||||
// aesgcmPreferred returns whether the first known cipher in the preference list
|
||||
// is an AES-GCM cipher, implying the peer has hardware support for it.
|
||||
func aesgcmPreferred(ciphers []uint16) bool {
|
||||
for _, cID := range ciphers {
|
||||
if c := cipherSuiteByID(cID); c != nil {
|
||||
return aesgcmCiphers[cID]
|
||||
}
|
||||
if c := cipherSuiteTLS13ByID(cID); c != nil {
|
||||
return aesgcmCiphers[cID]
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func cipherRC4(key, iv []byte, isRead bool) any {
|
||||
cipher, _ := rc4.NewCipher(key)
|
||||
return cipher
|
||||
}
|
||||
|
||||
func cipher3DES(key, iv []byte, isRead bool) any {
|
||||
block, _ := des.NewTripleDESCipher(key)
|
||||
if isRead {
|
||||
return cipher.NewCBCDecrypter(block, iv)
|
||||
}
|
||||
return cipher.NewCBCEncrypter(block, iv)
|
||||
}
|
||||
|
||||
func cipherAES(key, iv []byte, isRead bool) any {
|
||||
block, _ := aes.NewCipher(key)
|
||||
if isRead {
|
||||
return cipher.NewCBCDecrypter(block, iv)
|
||||
}
|
||||
return cipher.NewCBCEncrypter(block, iv)
|
||||
}
|
||||
|
||||
// macSHA1 returns a SHA-1 based constant time MAC.
|
||||
func macSHA1(key []byte) hash.Hash {
|
||||
return hmac.New(newConstantTimeHash(sha1.New), key)
|
||||
}
|
||||
|
||||
// macSHA256 returns a SHA-256 based MAC. This is only supported in TLS 1.2 and
|
||||
// is currently only used in disabled-by-default cipher suites.
|
||||
func macSHA256(key []byte) hash.Hash {
|
||||
return hmac.New(sha256.New, key)
|
||||
}
|
||||
|
||||
type aead interface {
|
||||
cipher.AEAD
|
||||
|
||||
// explicitNonceLen returns the number of bytes of explicit nonce
|
||||
// included in each record. This is eight for older AEADs and
|
||||
// zero for modern ones.
|
||||
explicitNonceLen() int
|
||||
}
|
||||
|
||||
const (
|
||||
aeadNonceLength = 12
|
||||
noncePrefixLength = 4
|
||||
)
|
||||
|
||||
// prefixNonceAEAD wraps an AEAD and prefixes a fixed portion of the nonce to
|
||||
// each call.
|
||||
type prefixNonceAEAD struct {
|
||||
// nonce contains the fixed part of the nonce in the first four bytes.
|
||||
nonce [aeadNonceLength]byte
|
||||
aead cipher.AEAD
|
||||
}
|
||||
|
||||
func (f *prefixNonceAEAD) NonceSize() int { return aeadNonceLength - noncePrefixLength }
|
||||
func (f *prefixNonceAEAD) Overhead() int { return f.aead.Overhead() }
|
||||
func (f *prefixNonceAEAD) explicitNonceLen() int { return f.NonceSize() }
|
||||
|
||||
func (f *prefixNonceAEAD) Seal(out, nonce, plaintext, additionalData []byte) []byte {
|
||||
copy(f.nonce[4:], nonce)
|
||||
return f.aead.Seal(out, f.nonce[:], plaintext, additionalData)
|
||||
}
|
||||
|
||||
func (f *prefixNonceAEAD) Open(out, nonce, ciphertext, additionalData []byte) ([]byte, error) {
|
||||
copy(f.nonce[4:], nonce)
|
||||
return f.aead.Open(out, f.nonce[:], ciphertext, additionalData)
|
||||
}
|
||||
|
||||
// xoredNonceAEAD wraps an AEAD by XORing in a fixed pattern to the nonce
|
||||
// before each call.
|
||||
type xorNonceAEAD struct {
|
||||
nonceMask [aeadNonceLength]byte
|
||||
aead cipher.AEAD
|
||||
}
|
||||
|
||||
func (f *xorNonceAEAD) NonceSize() int { return 8 } // 64-bit sequence number
|
||||
func (f *xorNonceAEAD) Overhead() int { return f.aead.Overhead() }
|
||||
func (f *xorNonceAEAD) explicitNonceLen() int { return 0 }
|
||||
|
||||
func (f *xorNonceAEAD) Seal(out, nonce, plaintext, additionalData []byte) []byte {
|
||||
for i, b := range nonce {
|
||||
f.nonceMask[4+i] ^= b
|
||||
}
|
||||
result := f.aead.Seal(out, f.nonceMask[:], plaintext, additionalData)
|
||||
for i, b := range nonce {
|
||||
f.nonceMask[4+i] ^= b
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (f *xorNonceAEAD) Open(out, nonce, ciphertext, additionalData []byte) ([]byte, error) {
|
||||
for i, b := range nonce {
|
||||
f.nonceMask[4+i] ^= b
|
||||
}
|
||||
result, err := f.aead.Open(out, f.nonceMask[:], ciphertext, additionalData)
|
||||
for i, b := range nonce {
|
||||
f.nonceMask[4+i] ^= b
|
||||
}
|
||||
|
||||
return result, err
|
||||
}
|
||||
|
||||
func aeadAESGCM(key, noncePrefix []byte) aead {
|
||||
if len(noncePrefix) != noncePrefixLength {
|
||||
panic("tls: internal error: wrong nonce length")
|
||||
}
|
||||
aes, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
aead, err := cipher.NewGCM(aes)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ret := &prefixNonceAEAD{aead: aead}
|
||||
copy(ret.nonce[:], noncePrefix)
|
||||
return ret
|
||||
}
|
||||
|
||||
func aeadAESGCMTLS13(key, nonceMask []byte) aead {
|
||||
if len(nonceMask) != aeadNonceLength {
|
||||
panic("tls: internal error: wrong nonce length")
|
||||
}
|
||||
aes, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
aead, err := cipher.NewGCM(aes)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ret := &xorNonceAEAD{aead: aead}
|
||||
copy(ret.nonceMask[:], nonceMask)
|
||||
return ret
|
||||
}
|
||||
|
||||
func aeadChaCha20Poly1305(key, nonceMask []byte) aead {
|
||||
if len(nonceMask) != aeadNonceLength {
|
||||
panic("tls: internal error: wrong nonce length")
|
||||
}
|
||||
aead, err := chacha20poly1305.New(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ret := &xorNonceAEAD{aead: aead}
|
||||
copy(ret.nonceMask[:], nonceMask)
|
||||
return ret
|
||||
}
|
||||
|
||||
type constantTimeHash interface {
|
||||
hash.Hash
|
||||
ConstantTimeSum(b []byte) []byte
|
||||
}
|
||||
|
||||
// cthWrapper wraps any hash.Hash that implements ConstantTimeSum, and replaces
|
||||
// with that all calls to Sum. It's used to obtain a ConstantTimeSum-based HMAC.
|
||||
type cthWrapper struct {
|
||||
h constantTimeHash
|
||||
}
|
||||
|
||||
func (c *cthWrapper) Size() int { return c.h.Size() }
|
||||
func (c *cthWrapper) BlockSize() int { return c.h.BlockSize() }
|
||||
func (c *cthWrapper) Reset() { c.h.Reset() }
|
||||
func (c *cthWrapper) Write(p []byte) (int, error) { return c.h.Write(p) }
|
||||
func (c *cthWrapper) Sum(b []byte) []byte { return c.h.ConstantTimeSum(b) }
|
||||
|
||||
func newConstantTimeHash(h func() hash.Hash) func() hash.Hash {
|
||||
return func() hash.Hash {
|
||||
return &cthWrapper{h().(constantTimeHash)}
|
||||
}
|
||||
}
|
||||
|
||||
// tls10MAC implements the TLS 1.0 MAC function. RFC 2246, Section 6.2.3.
|
||||
func tls10MAC(h hash.Hash, out, seq, header, data, extra []byte) []byte {
|
||||
h.Reset()
|
||||
h.Write(seq)
|
||||
h.Write(header)
|
||||
h.Write(data)
|
||||
res := h.Sum(out)
|
||||
if extra != nil {
|
||||
h.Write(extra)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func rsaKA(version uint16) keyAgreement {
|
||||
return rsaKeyAgreement{}
|
||||
}
|
||||
|
||||
func ecdheECDSAKA(version uint16) keyAgreement {
|
||||
return &ecdheKeyAgreement{
|
||||
isRSA: false,
|
||||
version: version,
|
||||
}
|
||||
}
|
||||
|
||||
func ecdheRSAKA(version uint16) keyAgreement {
|
||||
return &ecdheKeyAgreement{
|
||||
isRSA: true,
|
||||
version: version,
|
||||
}
|
||||
}
|
||||
|
||||
// mutualCipherSuite returns a cipherSuite given a list of supported
|
||||
// ciphersuites and the id requested by the peer.
|
||||
func mutualCipherSuite(have []uint16, want uint16) *cipherSuite {
|
||||
for _, id := range have {
|
||||
if id == want {
|
||||
return cipherSuiteByID(id)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func cipherSuiteByID(id uint16) *cipherSuite {
|
||||
for _, cipherSuite := range cipherSuites {
|
||||
if cipherSuite.id == id {
|
||||
return cipherSuite
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func mutualCipherSuiteTLS13(have []uint16, want uint16) *cipherSuiteTLS13 {
|
||||
for _, id := range have {
|
||||
if id == want {
|
||||
return cipherSuiteTLS13ByID(id)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func cipherSuiteTLS13ByID(id uint16) *cipherSuiteTLS13 {
|
||||
for _, cipherSuite := range cipherSuitesTLS13 {
|
||||
if cipherSuite.id == id {
|
||||
return cipherSuite
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// A list of cipher suite IDs that are, or have been, implemented by this
|
||||
// package.
|
||||
//
|
||||
// See https://www.iana.org/assignments/tls-parameters/tls-parameters.xml
|
||||
const (
|
||||
// TLS 1.0 - 1.2 cipher suites.
|
||||
TLS_RSA_WITH_RC4_128_SHA uint16 = 0x0005
|
||||
TLS_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0x000a
|
||||
TLS_RSA_WITH_AES_128_CBC_SHA uint16 = 0x002f
|
||||
TLS_RSA_WITH_AES_256_CBC_SHA uint16 = 0x0035
|
||||
TLS_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0x003c
|
||||
TLS_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0x009c
|
||||
TLS_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0x009d
|
||||
TLS_ECDHE_ECDSA_WITH_RC4_128_SHA uint16 = 0xc007
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA uint16 = 0xc009
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA uint16 = 0xc00a
|
||||
TLS_ECDHE_RSA_WITH_RC4_128_SHA uint16 = 0xc011
|
||||
TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xc012
|
||||
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA uint16 = 0xc013
|
||||
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA uint16 = 0xc014
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 uint16 = 0xc023
|
||||
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0xc027
|
||||
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0xc02f
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 uint16 = 0xc02b
|
||||
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0xc030
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 uint16 = 0xc02c
|
||||
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xcca8
|
||||
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xcca9
|
||||
|
||||
// TLS 1.3 cipher suites.
|
||||
TLS_AES_128_GCM_SHA256 uint16 = 0x1301
|
||||
TLS_AES_256_GCM_SHA384 uint16 = 0x1302
|
||||
TLS_CHACHA20_POLY1305_SHA256 uint16 = 0x1303
|
||||
|
||||
// TLS_FALLBACK_SCSV isn't a standard cipher suite but an indicator
|
||||
// that the client is doing version fallback. See RFC 7507.
|
||||
TLS_FALLBACK_SCSV uint16 = 0x5600
|
||||
|
||||
// Legacy names for the corresponding cipher suites with the correct _SHA256
|
||||
// suffix, retained for backward compatibility.
|
||||
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305 = TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
|
||||
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305 = TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
|
||||
)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,116 +0,0 @@
|
||||
// Code generated by "stringer -type=SignatureScheme,CurveID,ClientAuthType -output=common_string.go"; DO NOT EDIT.
|
||||
|
||||
package tls
|
||||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[PKCS1WithSHA256-1025]
|
||||
_ = x[PKCS1WithSHA384-1281]
|
||||
_ = x[PKCS1WithSHA512-1537]
|
||||
_ = x[PSSWithSHA256-2052]
|
||||
_ = x[PSSWithSHA384-2053]
|
||||
_ = x[PSSWithSHA512-2054]
|
||||
_ = x[ECDSAWithP256AndSHA256-1027]
|
||||
_ = x[ECDSAWithP384AndSHA384-1283]
|
||||
_ = x[ECDSAWithP521AndSHA512-1539]
|
||||
_ = x[Ed25519-2055]
|
||||
_ = x[PKCS1WithSHA1-513]
|
||||
_ = x[ECDSAWithSHA1-515]
|
||||
}
|
||||
|
||||
const (
|
||||
_SignatureScheme_name_0 = "PKCS1WithSHA1"
|
||||
_SignatureScheme_name_1 = "ECDSAWithSHA1"
|
||||
_SignatureScheme_name_2 = "PKCS1WithSHA256"
|
||||
_SignatureScheme_name_3 = "ECDSAWithP256AndSHA256"
|
||||
_SignatureScheme_name_4 = "PKCS1WithSHA384"
|
||||
_SignatureScheme_name_5 = "ECDSAWithP384AndSHA384"
|
||||
_SignatureScheme_name_6 = "PKCS1WithSHA512"
|
||||
_SignatureScheme_name_7 = "ECDSAWithP521AndSHA512"
|
||||
_SignatureScheme_name_8 = "PSSWithSHA256PSSWithSHA384PSSWithSHA512Ed25519"
|
||||
)
|
||||
|
||||
var (
|
||||
_SignatureScheme_index_8 = [...]uint8{0, 13, 26, 39, 46}
|
||||
)
|
||||
|
||||
func (i SignatureScheme) String() string {
|
||||
switch {
|
||||
case i == 513:
|
||||
return _SignatureScheme_name_0
|
||||
case i == 515:
|
||||
return _SignatureScheme_name_1
|
||||
case i == 1025:
|
||||
return _SignatureScheme_name_2
|
||||
case i == 1027:
|
||||
return _SignatureScheme_name_3
|
||||
case i == 1281:
|
||||
return _SignatureScheme_name_4
|
||||
case i == 1283:
|
||||
return _SignatureScheme_name_5
|
||||
case i == 1537:
|
||||
return _SignatureScheme_name_6
|
||||
case i == 1539:
|
||||
return _SignatureScheme_name_7
|
||||
case 2052 <= i && i <= 2055:
|
||||
i -= 2052
|
||||
return _SignatureScheme_name_8[_SignatureScheme_index_8[i]:_SignatureScheme_index_8[i+1]]
|
||||
default:
|
||||
return "SignatureScheme(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
}
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[CurveP256-23]
|
||||
_ = x[CurveP384-24]
|
||||
_ = x[CurveP521-25]
|
||||
_ = x[X25519-29]
|
||||
}
|
||||
|
||||
const (
|
||||
_CurveID_name_0 = "CurveP256CurveP384CurveP521"
|
||||
_CurveID_name_1 = "X25519"
|
||||
)
|
||||
|
||||
var (
|
||||
_CurveID_index_0 = [...]uint8{0, 9, 18, 27}
|
||||
)
|
||||
|
||||
func (i CurveID) String() string {
|
||||
switch {
|
||||
case 23 <= i && i <= 25:
|
||||
i -= 23
|
||||
return _CurveID_name_0[_CurveID_index_0[i]:_CurveID_index_0[i+1]]
|
||||
case i == 29:
|
||||
return _CurveID_name_1
|
||||
default:
|
||||
return "CurveID(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
}
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[NoClientCert-0]
|
||||
_ = x[RequestClientCert-1]
|
||||
_ = x[RequireAnyClientCert-2]
|
||||
_ = x[VerifyClientCertIfGiven-3]
|
||||
_ = x[RequireAndVerifyClientCert-4]
|
||||
}
|
||||
|
||||
const _ClientAuthType_name = "NoClientCertRequestClientCertRequireAnyClientCertVerifyClientCertIfGivenRequireAndVerifyClientCert"
|
||||
|
||||
var _ClientAuthType_index = [...]uint8{0, 12, 29, 49, 72, 98}
|
||||
|
||||
func (i ClientAuthType) String() string {
|
||||
if i < 0 || i >= ClientAuthType(len(_ClientAuthType_index)-1) {
|
||||
return "ClientAuthType(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _ClientAuthType_name[_ClientAuthType_index[i]:_ClientAuthType_index[i+1]]
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,550 +0,0 @@
|
||||
// Copyright 2020-2021 Cloudflare, Inc. All rights reserved. Use of this source code
|
||||
// is governed by a BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
package tls
|
||||
|
||||
// Delegated Credentials for TLS
|
||||
// (https://tools.ietf.org/html/draft-ietf-tls-subcerts) is an IETF Internet
|
||||
// draft and proposed TLS extension. If the client or server supports this
|
||||
// extension, then the server or client may use a "delegated credential" as the
|
||||
// signing key in the handshake. A delegated credential is a short lived
|
||||
// public/secret key pair delegated to the peer by an entity trusted by the
|
||||
// corresponding peer. This allows a reverse proxy to terminate a TLS connection
|
||||
// on behalf of the entity. Credentials can't be revoked; in order to
|
||||
// mitigate risk in case the reverse proxy is compromised, the credential is only
|
||||
// valid for a short time (days, hours, or even minutes).
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/ed25519"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/cryptobyte"
|
||||
)
|
||||
|
||||
const (
|
||||
// In the absence of an application profile standard specifying otherwise,
|
||||
// the maximum validity period is set to 7 days.
|
||||
dcMaxTTLSeconds = 60 * 60 * 24 * 7
|
||||
dcMaxTTL = time.Duration(dcMaxTTLSeconds * time.Second)
|
||||
dcMaxPubLen = (1 << 24) - 1 // Bytes
|
||||
dcMaxSignatureLen = (1 << 16) - 1 // Bytes
|
||||
)
|
||||
|
||||
const (
|
||||
undefinedSignatureScheme SignatureScheme = 0x0000
|
||||
)
|
||||
|
||||
var extensionDelegatedCredential = []int{1, 3, 6, 1, 4, 1, 44363, 44}
|
||||
|
||||
// isValidForDelegation returns true if a certificate can be used for Delegated
|
||||
// Credentials.
|
||||
func isValidForDelegation(cert *x509.Certificate) bool {
|
||||
// Check that the digitalSignature key usage is set.
|
||||
// The certificate must contains the digitalSignature KeyUsage.
|
||||
if (cert.KeyUsage & x509.KeyUsageDigitalSignature) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check that the certificate has the DelegationUsage extension and that
|
||||
// it's marked as non-critical (See Section 4.2 of RFC5280).
|
||||
for _, extension := range cert.Extensions {
|
||||
if extension.Id.Equal(extensionDelegatedCredential) {
|
||||
if extension.Critical {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isExpired returns true if the credential has expired. The end of the validity
|
||||
// interval is defined as the delegator certificate's notBefore field ('start')
|
||||
// plus dc.cred.validTime seconds. This function simply checks that the current time
|
||||
// ('now') is before the end of the validity interval.
|
||||
func (dc *DelegatedCredential) isExpired(start, now time.Time) bool {
|
||||
end := start.Add(dc.cred.validTime)
|
||||
return !now.Before(end)
|
||||
}
|
||||
|
||||
// invalidTTL returns true if the credential's validity period is longer than the
|
||||
// maximum permitted. This is defined by the certificate's notBefore field
|
||||
// ('start') plus the dc.validTime, minus the current time ('now').
|
||||
func (dc *DelegatedCredential) invalidTTL(start, now time.Time) bool {
|
||||
return dc.cred.validTime > (now.Sub(start) + dcMaxTTL).Round(time.Second)
|
||||
}
|
||||
|
||||
// credential stores the public components of a Delegated Credential.
|
||||
type credential struct {
|
||||
// The amount of time for which the credential is valid. Specifically, the
|
||||
// the credential expires 'validTime' seconds after the 'notBefore' of the
|
||||
// delegation certificate. The delegator shall not issue Delegated
|
||||
// Credentials that are valid for more than 7 days from the current time.
|
||||
//
|
||||
// When this data structure is serialized, this value is converted to a
|
||||
// uint32 representing the duration in seconds.
|
||||
validTime time.Duration
|
||||
// The signature scheme associated with the credential public key.
|
||||
// This is expected to be the same as the CertificateVerify.algorithm
|
||||
// sent by the client or server.
|
||||
expCertVerfAlgo SignatureScheme
|
||||
// The credential's public key.
|
||||
publicKey crypto.PublicKey
|
||||
}
|
||||
|
||||
// DelegatedCredential stores a Delegated Credential with the credential and its
|
||||
// signature.
|
||||
type DelegatedCredential struct {
|
||||
// The serialized form of the Delegated Credential.
|
||||
raw []byte
|
||||
|
||||
// Cred stores the public components of a Delegated Credential.
|
||||
cred *credential
|
||||
|
||||
// The signature scheme used to sign the Delegated Credential.
|
||||
algorithm SignatureScheme
|
||||
|
||||
// The Credential's delegation: a signature that binds the credential to
|
||||
// the end-entity certificate's public key.
|
||||
signature []byte
|
||||
}
|
||||
|
||||
// marshalPublicKeyInfo returns a DER encoded PublicKeyInfo
|
||||
// from a Delegated Credential (as defined in the X.509 standard).
|
||||
// The following key types are currently supported: *ecdsa.PublicKey
|
||||
// and ed25519.PublicKey. Unsupported key types result in an error.
|
||||
// rsa.PublicKey is not supported as defined by the draft.
|
||||
func (cred *credential) marshalPublicKeyInfo() ([]byte, error) {
|
||||
switch cred.expCertVerfAlgo {
|
||||
case ECDSAWithP256AndSHA256,
|
||||
ECDSAWithP384AndSHA384,
|
||||
ECDSAWithP521AndSHA512,
|
||||
Ed25519:
|
||||
rawPub, err := x509.MarshalPKIXPublicKey(cred.publicKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return rawPub, nil
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("tls: unsupported signature scheme: 0x%04x", cred.expCertVerfAlgo)
|
||||
}
|
||||
}
|
||||
|
||||
// marshal encodes the credential struct of the Delegated Credential.
|
||||
func (cred *credential) marshal() ([]byte, error) {
|
||||
var b cryptobyte.Builder
|
||||
|
||||
b.AddUint32(uint32(cred.validTime / time.Second))
|
||||
b.AddUint16(uint16(cred.expCertVerfAlgo))
|
||||
|
||||
// Encode the public key
|
||||
rawPub, err := cred.marshalPublicKeyInfo()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Assert that the public key encoding is no longer than 2^24-1 bytes.
|
||||
if len(rawPub) > dcMaxPubLen {
|
||||
return nil, errors.New("tls: public key length exceeds 2^24-1 limit")
|
||||
}
|
||||
|
||||
b.AddUint24(uint32(len(rawPub)))
|
||||
b.AddBytes(rawPub)
|
||||
|
||||
raw := b.BytesOrPanic()
|
||||
return raw, nil
|
||||
}
|
||||
|
||||
// unmarshalCredential decodes serialized bytes and returns a credential, if possible.
|
||||
func unmarshalCredential(raw []byte) (*credential, error) {
|
||||
if len(raw) < 10 {
|
||||
return nil, errors.New("tls: Delegated Credential is not valid: invalid length")
|
||||
}
|
||||
|
||||
s := cryptobyte.String(raw)
|
||||
var t uint32
|
||||
if !s.ReadUint32(&t) {
|
||||
return nil, errors.New("tls: Delegated Credential is not valid")
|
||||
}
|
||||
validTime := time.Duration(t) * time.Second
|
||||
|
||||
var pubAlgo uint16
|
||||
if !s.ReadUint16(&pubAlgo) {
|
||||
return nil, errors.New("tls: Delegated Credential is not valid")
|
||||
}
|
||||
algo := SignatureScheme(pubAlgo)
|
||||
|
||||
var pubLen uint32
|
||||
s.ReadUint24(&pubLen)
|
||||
|
||||
pubKey, err := x509.ParsePKIXPublicKey(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &credential{validTime, algo, pubKey}, nil
|
||||
}
|
||||
|
||||
// getCredentialLen returns the number of bytes comprising the serialized
|
||||
// credential struct inside the Delegated Credential.
|
||||
func getCredentialLen(raw []byte) (int, error) {
|
||||
if len(raw) < 10 {
|
||||
return 0, errors.New("tls: Delegated Credential is not valid")
|
||||
}
|
||||
|
||||
var read []byte
|
||||
s := cryptobyte.String(raw)
|
||||
s.ReadBytes(&read, 6)
|
||||
|
||||
var pubLen uint32
|
||||
s.ReadUint24(&pubLen)
|
||||
if !(pubLen > 0) {
|
||||
return 0, errors.New("tls: Delegated Credential is not valid")
|
||||
}
|
||||
|
||||
raw = raw[6:]
|
||||
if len(raw) < int(pubLen) {
|
||||
return 0, errors.New("tls: Delegated Credential is not valid")
|
||||
}
|
||||
|
||||
return 9 + int(pubLen), nil
|
||||
}
|
||||
|
||||
// getHash maps the SignatureScheme to its corresponding hash function.
|
||||
func getHash(scheme SignatureScheme) crypto.Hash {
|
||||
switch scheme {
|
||||
case ECDSAWithP256AndSHA256:
|
||||
return crypto.SHA256
|
||||
case ECDSAWithP384AndSHA384:
|
||||
return crypto.SHA384
|
||||
case ECDSAWithP521AndSHA512:
|
||||
return crypto.SHA512
|
||||
case Ed25519:
|
||||
return directSigning
|
||||
case PKCS1WithSHA256, PSSWithSHA256:
|
||||
return crypto.SHA256
|
||||
case PSSWithSHA384:
|
||||
return crypto.SHA384
|
||||
case PSSWithSHA512:
|
||||
return crypto.SHA512
|
||||
default:
|
||||
return 0 // Unknown hash function
|
||||
}
|
||||
}
|
||||
|
||||
// getECDSACurve maps the SignatureScheme to its corresponding ecdsa elliptic.Curve.
|
||||
func getECDSACurve(scheme SignatureScheme) elliptic.Curve {
|
||||
switch scheme {
|
||||
case ECDSAWithP256AndSHA256:
|
||||
return elliptic.P256()
|
||||
case ECDSAWithP384AndSHA384:
|
||||
return elliptic.P384()
|
||||
case ECDSAWithP521AndSHA512:
|
||||
return elliptic.P521()
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// prepareDelegationSignatureInput returns the message that the delegator is going to sign.
|
||||
func prepareDelegationSignatureInput(hash crypto.Hash, cred *credential, dCert []byte, algo SignatureScheme, isClient bool) ([]byte, error) {
|
||||
header := make([]byte, 64)
|
||||
for i := range header {
|
||||
header[i] = 0x20
|
||||
}
|
||||
|
||||
var context string
|
||||
if !isClient {
|
||||
context = "TLS, server delegated credentials\x00"
|
||||
} else {
|
||||
context = "TLS, client delegated credentials\x00"
|
||||
}
|
||||
|
||||
rawCred, err := cred.marshal()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var rawAlgo [2]byte
|
||||
binary.BigEndian.PutUint16(rawAlgo[:], uint16(algo))
|
||||
|
||||
if hash == directSigning {
|
||||
b := &bytes.Buffer{}
|
||||
b.Write(header)
|
||||
io.WriteString(b, context)
|
||||
b.Write(dCert)
|
||||
b.Write(rawCred)
|
||||
b.Write(rawAlgo[:])
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
|
||||
h := hash.New()
|
||||
h.Write(header)
|
||||
io.WriteString(h, context)
|
||||
h.Write(dCert)
|
||||
h.Write(rawCred)
|
||||
h.Write(rawAlgo[:])
|
||||
return h.Sum(nil), nil
|
||||
}
|
||||
|
||||
// Extract the algorithm used to sign the Delegated Credential from the
|
||||
// end-entity (leaf) certificate.
|
||||
func getSignatureAlgorithm(cert *Certificate) (SignatureScheme, error) {
|
||||
switch sk := cert.PrivateKey.(type) {
|
||||
case *ecdsa.PrivateKey:
|
||||
pk := sk.Public().(*ecdsa.PublicKey)
|
||||
curveName := pk.Curve.Params().Name
|
||||
certAlg := cert.Leaf.PublicKeyAlgorithm
|
||||
if certAlg == x509.ECDSA && curveName == "P-256" {
|
||||
return ECDSAWithP256AndSHA256, nil
|
||||
} else if certAlg == x509.ECDSA && curveName == "P-384" {
|
||||
return ECDSAWithP384AndSHA384, nil
|
||||
} else if certAlg == x509.ECDSA && curveName == "P-521" {
|
||||
return ECDSAWithP521AndSHA512, nil
|
||||
} else {
|
||||
return undefinedSignatureScheme, fmt.Errorf("using curve %s for %s is not supported", curveName, cert.Leaf.SignatureAlgorithm)
|
||||
}
|
||||
case ed25519.PrivateKey:
|
||||
return Ed25519, nil
|
||||
case *rsa.PrivateKey:
|
||||
// If the certificate has the RSAEncryption OID there are a number of valid signature schemes that may sign the DC.
|
||||
// In the absence of better information, we make a reasonable choice.
|
||||
return PSSWithSHA256, nil
|
||||
default:
|
||||
return undefinedSignatureScheme, fmt.Errorf("tls: unsupported algorithm for signing Delegated Credential")
|
||||
}
|
||||
}
|
||||
|
||||
// NewDelegatedCredential creates a new Delegated Credential using 'cert' for
|
||||
// delegation, depending if the caller is the client or the server (defined by
|
||||
// 'isClient'). It generates a public/private key pair for the provided signature
|
||||
// algorithm ('pubAlgo') and it defines a validity interval (defined
|
||||
// by 'cert.Leaf.notBefore' and 'validTime'). It signs the Delegated Credential
|
||||
// using 'cert.PrivateKey'.
|
||||
func NewDelegatedCredential(cert *Certificate, pubAlgo SignatureScheme, validTime time.Duration, isClient bool) (*DelegatedCredential, crypto.PrivateKey, error) {
|
||||
// The granularity of DC validity is seconds.
|
||||
validTime = validTime.Round(time.Second)
|
||||
|
||||
// Parse the leaf certificate if needed.
|
||||
var err error
|
||||
if cert.Leaf == nil {
|
||||
if len(cert.Certificate[0]) == 0 {
|
||||
return nil, nil, errors.New("tls: missing leaf certificate for Delegated Credential")
|
||||
}
|
||||
cert.Leaf, err = x509.ParseCertificate(cert.Certificate[0])
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Check that the leaf certificate can be used for delegation.
|
||||
if !isValidForDelegation(cert.Leaf) {
|
||||
return nil, nil, errors.New("tls: certificate not authorized for delegation")
|
||||
}
|
||||
|
||||
sigAlgo, err := getSignatureAlgorithm(cert)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Generate the Delegated Credential key pair based on the provided scheme
|
||||
var privK crypto.PrivateKey
|
||||
var pubK crypto.PublicKey
|
||||
switch pubAlgo {
|
||||
case ECDSAWithP256AndSHA256,
|
||||
ECDSAWithP384AndSHA384,
|
||||
ECDSAWithP521AndSHA512:
|
||||
privK, err = ecdsa.GenerateKey(getECDSACurve(pubAlgo), rand.Reader)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
pubK = privK.(*ecdsa.PrivateKey).Public()
|
||||
case Ed25519:
|
||||
pubK, privK, err = ed25519.GenerateKey(rand.Reader)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
default:
|
||||
return nil, nil, fmt.Errorf("tls: unsupported algorithm for Delegated Credential: %s", pubAlgo)
|
||||
}
|
||||
|
||||
// Prepare the credential for signing
|
||||
hash := getHash(sigAlgo)
|
||||
credential := &credential{validTime, pubAlgo, pubK}
|
||||
values, err := prepareDelegationSignatureInput(hash, credential, cert.Leaf.Raw, sigAlgo, isClient)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var sig []byte
|
||||
switch sk := cert.PrivateKey.(type) {
|
||||
case *ecdsa.PrivateKey:
|
||||
opts := crypto.SignerOpts(hash)
|
||||
sig, err = sk.Sign(rand.Reader, values, opts)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
case ed25519.PrivateKey:
|
||||
opts := crypto.SignerOpts(hash)
|
||||
sig, err = sk.Sign(rand.Reader, values, opts)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
case *rsa.PrivateKey:
|
||||
opts := &rsa.PSSOptions{
|
||||
SaltLength: rsa.PSSSaltLengthEqualsHash,
|
||||
Hash: hash,
|
||||
}
|
||||
sig, err = rsa.SignPSS(rand.Reader, sk, hash, values, opts)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
default:
|
||||
return nil, nil, fmt.Errorf("tls: unsupported key type for Delegated Credential")
|
||||
}
|
||||
|
||||
if len(sig) > dcMaxSignatureLen {
|
||||
return nil, nil, errors.New("tls: unable to create a Delegated Credential")
|
||||
}
|
||||
|
||||
return &DelegatedCredential{
|
||||
cred: credential,
|
||||
algorithm: sigAlgo,
|
||||
signature: sig,
|
||||
}, privK, nil
|
||||
}
|
||||
|
||||
// Validate validates the Delegated Credential by checking that the signature is
|
||||
// valid, that it hasn't expired, and that the TTL is valid. It also checks that
|
||||
// certificate can be used for delegation.
|
||||
func (dc *DelegatedCredential) Validate(cert *x509.Certificate, isClient bool, now time.Time, certVerifyMsg *certificateVerifyMsg) bool {
|
||||
if dc.isExpired(cert.NotBefore, now) {
|
||||
return false
|
||||
}
|
||||
|
||||
if dc.invalidTTL(cert.NotBefore, now) {
|
||||
return false
|
||||
}
|
||||
|
||||
if dc.cred.expCertVerfAlgo != certVerifyMsg.signatureAlgorithm {
|
||||
return false
|
||||
}
|
||||
|
||||
if !isValidForDelegation(cert) {
|
||||
return false
|
||||
}
|
||||
|
||||
hash := getHash(dc.algorithm)
|
||||
in, err := prepareDelegationSignatureInput(hash, dc.cred, cert.Raw, dc.algorithm, isClient)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
switch dc.algorithm {
|
||||
case ECDSAWithP256AndSHA256,
|
||||
ECDSAWithP384AndSHA384,
|
||||
ECDSAWithP521AndSHA512:
|
||||
pk, ok := cert.PublicKey.(*ecdsa.PublicKey)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return ecdsa.VerifyASN1(pk, in, dc.signature)
|
||||
case Ed25519:
|
||||
pk, ok := cert.PublicKey.(ed25519.PublicKey)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return ed25519.Verify(pk, in, dc.signature)
|
||||
case PSSWithSHA256,
|
||||
PSSWithSHA384,
|
||||
PSSWithSHA512:
|
||||
pk, ok := cert.PublicKey.(*rsa.PublicKey)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
hash := getHash(dc.algorithm)
|
||||
return rsa.VerifyPSS(pk, hash, in, dc.signature, nil) == nil
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Marshal encodes a DelegatedCredential structure. It also sets dc.Raw to that
|
||||
// encoding.
|
||||
func (dc *DelegatedCredential) Marshal() ([]byte, error) {
|
||||
if len(dc.signature) > dcMaxSignatureLen {
|
||||
return nil, errors.New("tls: delegated credential is not valid")
|
||||
}
|
||||
if len(dc.signature) == 0 {
|
||||
return nil, errors.New("tls: delegated credential has no signature")
|
||||
}
|
||||
|
||||
raw, err := dc.cred.marshal()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var b cryptobyte.Builder
|
||||
b.AddBytes(raw)
|
||||
b.AddUint16(uint16(dc.algorithm))
|
||||
b.AddUint16(uint16(len(dc.signature)))
|
||||
b.AddBytes(dc.signature)
|
||||
|
||||
dc.raw = b.BytesOrPanic()
|
||||
return dc.raw, nil
|
||||
}
|
||||
|
||||
// UnmarshalDelegatedCredential decodes a DelegatedCredential structure.
|
||||
func UnmarshalDelegatedCredential(raw []byte) (*DelegatedCredential, error) {
|
||||
rawCredentialLen, err := getCredentialLen(raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
credential, err := unmarshalCredential(raw[:rawCredentialLen])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
raw = raw[rawCredentialLen:]
|
||||
if len(raw) < 4 {
|
||||
return nil, errors.New("tls: Delegated Credential is not valid")
|
||||
}
|
||||
|
||||
s := cryptobyte.String(raw)
|
||||
|
||||
var algo uint16
|
||||
if !s.ReadUint16(&algo) {
|
||||
return nil, errors.New("tls: Delegated Credential is not valid")
|
||||
}
|
||||
|
||||
var rawSignatureLen uint16
|
||||
if !s.ReadUint16(&rawSignatureLen) {
|
||||
return nil, errors.New("tls: Delegated Credential is not valid")
|
||||
}
|
||||
|
||||
var sig []byte
|
||||
if !s.ReadBytes(&sig, int(rawSignatureLen)) {
|
||||
return nil, errors.New("tls: Delegated Credential is not valid")
|
||||
}
|
||||
|
||||
return &DelegatedCredential{
|
||||
cred: credential,
|
||||
algorithm: SignatureScheme(algo),
|
||||
signature: sig,
|
||||
}, nil
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,164 +0,0 @@
|
||||
// Copyright 2020 Cloudflare, Inc. All rights reserved. Use of this source code
|
||||
// is governed by a BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
package tls
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/cloudflare/circl/hpke"
|
||||
"github.com/cloudflare/circl/kem"
|
||||
"golang.org/x/crypto/cryptobyte"
|
||||
)
|
||||
|
||||
// ECHConfig represents an ECH configuration.
|
||||
type ECHConfig struct {
|
||||
pk kem.PublicKey
|
||||
raw []byte
|
||||
|
||||
// Parsed from raw
|
||||
version uint16
|
||||
configId uint8
|
||||
rawPublicName []byte
|
||||
rawPublicKey []byte
|
||||
kemId uint16
|
||||
suites []hpkeSymmetricCipherSuite
|
||||
maxNameLen uint8
|
||||
ignoredExtensions []byte
|
||||
}
|
||||
|
||||
// UnmarshalECHConfigs parses a sequence of ECH configurations.
|
||||
func UnmarshalECHConfigs(raw []byte) ([]ECHConfig, error) {
|
||||
var (
|
||||
err error
|
||||
config ECHConfig
|
||||
t, contents cryptobyte.String
|
||||
)
|
||||
configs := make([]ECHConfig, 0)
|
||||
s := cryptobyte.String(raw)
|
||||
if !s.ReadUint16LengthPrefixed(&t) || !s.Empty() {
|
||||
return configs, errors.New("error parsing configs")
|
||||
}
|
||||
raw = raw[2:]
|
||||
ConfigsLoop:
|
||||
for !t.Empty() {
|
||||
l := len(t)
|
||||
if !t.ReadUint16(&config.version) ||
|
||||
!t.ReadUint16LengthPrefixed(&contents) {
|
||||
return nil, errors.New("error parsing config")
|
||||
}
|
||||
n := l - len(t)
|
||||
config.raw = raw[:n]
|
||||
raw = raw[n:]
|
||||
|
||||
if config.version != extensionECH {
|
||||
continue ConfigsLoop
|
||||
}
|
||||
if !readConfigContents(&contents, &config) {
|
||||
return nil, errors.New("error parsing config contents")
|
||||
}
|
||||
|
||||
kem := hpke.KEM(config.kemId)
|
||||
if !kem.IsValid() {
|
||||
continue ConfigsLoop
|
||||
}
|
||||
config.pk, err = kem.Scheme().UnmarshalBinaryPublicKey(config.rawPublicKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing public key: %s", err)
|
||||
}
|
||||
configs = append(configs, config)
|
||||
}
|
||||
return configs, nil
|
||||
}
|
||||
|
||||
func echMarshalConfigs(configs []ECHConfig) ([]byte, error) {
|
||||
var b cryptobyte.Builder
|
||||
b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
|
||||
for _, config := range configs {
|
||||
if config.raw == nil {
|
||||
panic("config.raw not set")
|
||||
}
|
||||
b.AddBytes(config.raw)
|
||||
}
|
||||
})
|
||||
return b.Bytes()
|
||||
}
|
||||
|
||||
func readConfigContents(contents *cryptobyte.String, config *ECHConfig) bool {
|
||||
var t cryptobyte.String
|
||||
if !contents.ReadUint8(&config.configId) ||
|
||||
!contents.ReadUint16(&config.kemId) ||
|
||||
!contents.ReadUint16LengthPrefixed(&t) ||
|
||||
!t.ReadBytes(&config.rawPublicKey, len(t)) ||
|
||||
!contents.ReadUint16LengthPrefixed(&t) ||
|
||||
len(t)%4 != 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
config.suites = nil
|
||||
for !t.Empty() {
|
||||
var kdfId, aeadId uint16
|
||||
if !t.ReadUint16(&kdfId) || !t.ReadUint16(&aeadId) {
|
||||
// This indicates an internal bug.
|
||||
panic("internal error while parsing contents.cipher_suites")
|
||||
}
|
||||
config.suites = append(config.suites, hpkeSymmetricCipherSuite{kdfId, aeadId})
|
||||
}
|
||||
|
||||
if !contents.ReadUint8(&config.maxNameLen) ||
|
||||
!contents.ReadUint8LengthPrefixed(&t) ||
|
||||
!t.ReadBytes(&config.rawPublicName, len(t)) ||
|
||||
!contents.ReadUint16LengthPrefixed(&t) ||
|
||||
!t.ReadBytes(&config.ignoredExtensions, len(t)) ||
|
||||
!contents.Empty() {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// setupSealer generates the client's HPKE context for use with the ECH
|
||||
// extension. It returns the context and corresponding encapsulated key.
|
||||
func (config *ECHConfig) setupSealer(rand io.Reader) (enc []byte, sealer hpke.Sealer, err error) {
|
||||
if config.raw == nil {
|
||||
panic("config.raw not set")
|
||||
}
|
||||
hpkeSuite, err := config.selectSuite()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
info := append(append([]byte(echHpkeInfoSetup), 0), config.raw...)
|
||||
sender, err := hpkeSuite.NewSender(config.pk, info)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return sender.Setup(rand)
|
||||
}
|
||||
|
||||
// isPeerCipherSuiteSupported returns true if this configuration indicates
|
||||
// support for the given ciphersuite.
|
||||
func (config *ECHConfig) isPeerCipherSuiteSupported(suite hpkeSymmetricCipherSuite) bool {
|
||||
for _, configSuite := range config.suites {
|
||||
if suite == configSuite {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// selectSuite returns the first ciphersuite indicated by this
|
||||
// configuration that is supported by the caller.
|
||||
func (config *ECHConfig) selectSuite() (hpke.Suite, error) {
|
||||
for _, suite := range config.suites {
|
||||
hpkeSuite, err := hpkeAssembleSuite(
|
||||
config.kemId,
|
||||
suite.kdfId,
|
||||
suite.aeadId,
|
||||
)
|
||||
if err == nil {
|
||||
return hpkeSuite, nil
|
||||
}
|
||||
}
|
||||
return hpke.Suite{}, errors.New("could not negotiate a ciphersuite")
|
||||
}
|
||||
@@ -1,302 +0,0 @@
|
||||
// Copyright 2020 Cloudflare, Inc. All rights reserved. Use of this source code
|
||||
// is governed by a BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
package tls
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/cloudflare/circl/hpke"
|
||||
"github.com/cloudflare/circl/kem"
|
||||
"golang.org/x/crypto/cryptobyte"
|
||||
)
|
||||
|
||||
// ECHProvider specifies the interface of an ECH service provider that decrypts
|
||||
// the ECH payload on behalf of the client-facing server. It also defines the
|
||||
// set of acceptable ECH configurations.
|
||||
type ECHProvider interface {
|
||||
// GetDecryptionContext attempts to construct the HPKE context used by the
|
||||
// client-facing server for decryption. (See draft-irtf-cfrg-hpke-07,
|
||||
// Section 5.2.)
|
||||
//
|
||||
// handle encodes the parameters of the client's "encrypted_client_hello"
|
||||
// extension that are needed to construct the context. Since
|
||||
// draft-ietf-tls-esni-10 these are the ECH cipher suite, the identity of
|
||||
// the ECH configuration, and the encapsulated key.
|
||||
//
|
||||
// version is the version of ECH indicated by the client.
|
||||
//
|
||||
// res.Status == ECHProviderStatusSuccess indicates the call was successful
|
||||
// and the caller may proceed. res.Context is set.
|
||||
//
|
||||
// res.Status == ECHProviderStatusReject indicates the caller must reject
|
||||
// ECH. res.RetryConfigs may be set.
|
||||
//
|
||||
// res.Status == ECHProviderStatusAbort indicates the caller should abort
|
||||
// the handshake. Note that, in some cases, it's appropriate to reject
|
||||
// rather than abort. In particular, aborting with "illegal_parameter" might
|
||||
// "stick out". res.Alert and res.Error are set.
|
||||
GetDecryptionContext(handle []byte, version uint16) (res ECHProviderResult)
|
||||
}
|
||||
|
||||
// ECHProviderStatus is the status of the ECH provider's response.
|
||||
type ECHProviderStatus uint
|
||||
|
||||
const (
|
||||
ECHProviderSuccess ECHProviderStatus = 0
|
||||
ECHProviderReject = 1
|
||||
ECHProviderAbort = 2
|
||||
|
||||
errHPKEInvalidPublicKey = "hpke: invalid KEM public key"
|
||||
)
|
||||
|
||||
// ECHProviderResult represents the result of invoking the ECH provider.
|
||||
type ECHProviderResult struct {
|
||||
Status ECHProviderStatus
|
||||
|
||||
// Alert is the TLS alert sent by the caller when aborting the handshake.
|
||||
Alert uint8
|
||||
|
||||
// Error is the error propagated by the caller when aborting the handshake.
|
||||
Error error
|
||||
|
||||
// RetryConfigs is the sequence of ECH configs to offer to the client for
|
||||
// retrying the handshake. This may be set in case of success or rejection.
|
||||
RetryConfigs []byte
|
||||
|
||||
// Context is the server's HPKE context. This is set if ECH is not rejected
|
||||
// by the provider and no error was reported. The data has the following
|
||||
// format (in TLS syntax):
|
||||
//
|
||||
// enum { sealer(0), opener(1) } HpkeRole;
|
||||
//
|
||||
// struct {
|
||||
// HpkeRole role;
|
||||
// HpkeKemId kem_id; // as defined in draft-irtf-cfrg-hpke-07
|
||||
// HpkeKdfId kdf_id; // as defined in draft-irtf-cfrg-hpke-07
|
||||
// HpkeAeadId aead_id; // as defined in draft-irtf-cfrg-hpke-07
|
||||
// opaque exporter_secret<0..255>;
|
||||
// opaque key<0..255>;
|
||||
// opaque base_nonce<0..255>;
|
||||
// opaque seq<0..255>;
|
||||
// } HpkeContext;
|
||||
Context []byte
|
||||
}
|
||||
|
||||
// EXP_ECHKeySet implements the ECHProvider interface for a sequence of ECH keys.
|
||||
//
|
||||
// NOTE: This API is EXPERIMENTAL and subject to change.
|
||||
type EXP_ECHKeySet struct {
|
||||
// The serialized ECHConfigs, in order of the server's preference.
|
||||
configs []byte
|
||||
|
||||
// Maps a configuration identifier to its secret key.
|
||||
sk map[uint8]EXP_ECHKey
|
||||
}
|
||||
|
||||
// EXP_NewECHKeySet constructs an EXP_ECHKeySet.
|
||||
func EXP_NewECHKeySet(keys []EXP_ECHKey) (*EXP_ECHKeySet, error) {
|
||||
if len(keys) > 255 {
|
||||
return nil, fmt.Errorf("tls: ech provider: unable to support more than 255 ECH configurations at once")
|
||||
}
|
||||
|
||||
keySet := new(EXP_ECHKeySet)
|
||||
keySet.sk = make(map[uint8]EXP_ECHKey)
|
||||
configs := make([]byte, 0)
|
||||
for _, key := range keys {
|
||||
if _, ok := keySet.sk[key.config.configId]; ok {
|
||||
return nil, fmt.Errorf("tls: ech provider: ECH config conflict for configId %d", key.config.configId)
|
||||
}
|
||||
|
||||
keySet.sk[key.config.configId] = key
|
||||
configs = append(configs, key.config.raw...)
|
||||
}
|
||||
|
||||
var b cryptobyte.Builder
|
||||
b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
|
||||
b.AddBytes(configs)
|
||||
})
|
||||
keySet.configs = b.BytesOrPanic()
|
||||
|
||||
return keySet, nil
|
||||
}
|
||||
|
||||
// GetDecryptionContext is required by the ECHProvider interface.
|
||||
func (keySet *EXP_ECHKeySet) GetDecryptionContext(rawHandle []byte, version uint16) (res ECHProviderResult) {
|
||||
// Propagate retry configurations regardless of the result. The caller sends
|
||||
// these to the clients only if it rejects.
|
||||
res.RetryConfigs = keySet.configs
|
||||
|
||||
// Ensure we know how to proceed, i.e., the caller has indicated a supported
|
||||
// version of ECH. Currently only draft-ietf-tls-esni-13 is supported.
|
||||
if version != extensionECH {
|
||||
res.Status = ECHProviderAbort
|
||||
res.Alert = uint8(alertInternalError)
|
||||
res.Error = errors.New("version not supported")
|
||||
return // Abort
|
||||
}
|
||||
|
||||
// Parse the handle.
|
||||
s := cryptobyte.String(rawHandle)
|
||||
handle := new(echContextHandle)
|
||||
if !echReadContextHandle(&s, handle) || !s.Empty() {
|
||||
// This is the result of a client-side error. However, aborting with
|
||||
// "illegal_parameter" would stick out, so we reject instead.
|
||||
res.Status = ECHProviderReject
|
||||
res.RetryConfigs = keySet.configs
|
||||
return // Reject
|
||||
}
|
||||
handle.raw = rawHandle
|
||||
|
||||
// Look up the secret key for the configuration indicated by the client.
|
||||
key, ok := keySet.sk[handle.configId]
|
||||
if !ok {
|
||||
res.Status = ECHProviderReject
|
||||
res.RetryConfigs = keySet.configs
|
||||
return // Reject
|
||||
}
|
||||
|
||||
// Ensure that support for the selected ciphersuite is indicated by the
|
||||
// configuration.
|
||||
suite := handle.suite
|
||||
if !key.config.isPeerCipherSuiteSupported(suite) {
|
||||
// This is the result of a client-side error. However, aborting with
|
||||
// "illegal_parameter" would stick out, so we reject instead.
|
||||
res.Status = ECHProviderReject
|
||||
res.RetryConfigs = keySet.configs
|
||||
return // Reject
|
||||
}
|
||||
|
||||
// Ensure the version indicated by the client matches the version supported
|
||||
// by the configuration.
|
||||
if version != key.config.version {
|
||||
// This is the result of a client-side error. However, aborting with
|
||||
// "illegal_parameter" would stick out, so we reject instead.
|
||||
res.Status = ECHProviderReject
|
||||
res.RetryConfigs = keySet.configs
|
||||
return // Reject
|
||||
}
|
||||
|
||||
// Compute the decryption context.
|
||||
opener, err := key.setupOpener(handle.enc, suite)
|
||||
if err != nil {
|
||||
if err.Error() == errHPKEInvalidPublicKey {
|
||||
// This occurs if the KEM algorithm used to generate handle.enc is
|
||||
// not the same as the KEM algorithm of the key. One way this can
|
||||
// happen is if the client sent a GREASE ECH extension with a
|
||||
// config_id that happens to match a known config, but which uses a
|
||||
// different KEM algorithm.
|
||||
res.Status = ECHProviderReject
|
||||
res.RetryConfigs = keySet.configs
|
||||
return // Reject
|
||||
}
|
||||
|
||||
res.Status = ECHProviderAbort
|
||||
res.Alert = uint8(alertInternalError)
|
||||
res.Error = err
|
||||
return // Abort
|
||||
}
|
||||
|
||||
// Serialize the decryption context.
|
||||
res.Context, err = opener.MarshalBinary()
|
||||
if err != nil {
|
||||
res.Status = ECHProviderAbort
|
||||
res.Alert = uint8(alertInternalError)
|
||||
res.Error = err
|
||||
return // Abort
|
||||
}
|
||||
|
||||
res.Status = ECHProviderSuccess
|
||||
return // Success
|
||||
}
|
||||
|
||||
// EXP_ECHKey represents an ECH key and its corresponding configuration. The
|
||||
// encoding of an ECH Key has the format defined below (in TLS syntax). Note
|
||||
// that the ECH standard does not specify this format.
|
||||
//
|
||||
// struct {
|
||||
// opaque sk<0..2^16-1>;
|
||||
// ECHConfig config<0..2^16>; // draft-ietf-tls-esni-13
|
||||
// } ECHKey;
|
||||
type EXP_ECHKey struct {
|
||||
sk kem.PrivateKey
|
||||
config ECHConfig
|
||||
}
|
||||
|
||||
// EXP_UnmarshalECHKeys parses a sequence of ECH keys.
|
||||
func EXP_UnmarshalECHKeys(raw []byte) ([]EXP_ECHKey, error) {
|
||||
var (
|
||||
err error
|
||||
key EXP_ECHKey
|
||||
sk, config, contents cryptobyte.String
|
||||
)
|
||||
s := cryptobyte.String(raw)
|
||||
keys := make([]EXP_ECHKey, 0)
|
||||
KeysLoop:
|
||||
for !s.Empty() {
|
||||
if !s.ReadUint16LengthPrefixed(&sk) ||
|
||||
!s.ReadUint16LengthPrefixed(&config) {
|
||||
return nil, errors.New("error parsing key")
|
||||
}
|
||||
|
||||
key.config.raw = config
|
||||
if !config.ReadUint16(&key.config.version) ||
|
||||
!config.ReadUint16LengthPrefixed(&contents) ||
|
||||
!config.Empty() {
|
||||
return nil, errors.New("error parsing config")
|
||||
}
|
||||
|
||||
if key.config.version != extensionECH {
|
||||
continue KeysLoop
|
||||
}
|
||||
if !readConfigContents(&contents, &key.config) {
|
||||
return nil, errors.New("error parsing config contents")
|
||||
}
|
||||
|
||||
for _, suite := range key.config.suites {
|
||||
if !hpke.KDF(suite.kdfId).IsValid() ||
|
||||
!hpke.AEAD(suite.aeadId).IsValid() {
|
||||
continue KeysLoop
|
||||
}
|
||||
}
|
||||
|
||||
kem := hpke.KEM(key.config.kemId)
|
||||
if !kem.IsValid() {
|
||||
continue KeysLoop
|
||||
}
|
||||
key.config.pk, err = kem.Scheme().UnmarshalBinaryPublicKey(key.config.rawPublicKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing public key: %s", err)
|
||||
}
|
||||
key.sk, err = kem.Scheme().UnmarshalBinaryPrivateKey(sk)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing secret key: %s", err)
|
||||
}
|
||||
|
||||
keys = append(keys, key)
|
||||
}
|
||||
return keys, nil
|
||||
}
|
||||
|
||||
// setupOpener computes the HPKE context used by the server in the ECH
|
||||
// extension.i
|
||||
func (key *EXP_ECHKey) setupOpener(enc []byte, suite hpkeSymmetricCipherSuite) (hpke.Opener, error) {
|
||||
if key.config.raw == nil {
|
||||
panic("raw config not set")
|
||||
}
|
||||
hpkeSuite, err := hpkeAssembleSuite(
|
||||
key.config.kemId,
|
||||
suite.kdfId,
|
||||
suite.aeadId,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
info := append(append([]byte(echHpkeInfoSetup), 0), key.config.raw...)
|
||||
receiver, err := hpkeSuite.NewReceiver(key.sk, info)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return receiver.Setup(enc)
|
||||
}
|
||||
@@ -1,194 +0,0 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build ignore
|
||||
|
||||
// Generate a self-signed X.509 certificate for a TLS server. Outputs to
|
||||
// 'cert.pem' and 'key.pem' and will overwrite existing files.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/ed25519"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"flag"
|
||||
"log"
|
||||
"math/big"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
circlSign "github.com/cloudflare/circl/sign"
|
||||
circlSchemes "github.com/cloudflare/circl/sign/schemes"
|
||||
)
|
||||
|
||||
var (
|
||||
host = flag.String("host", "", "Comma-separated hostnames and IPs to generate a certificate for")
|
||||
validFrom = flag.String("start-date", "", "Creation date formatted as Jan 1 15:04:05 2011")
|
||||
validFor = flag.Duration("duration", 365*24*time.Hour, "Duration that certificate is valid for")
|
||||
isCA = flag.Bool("ca", false, "whether this cert should be its own Certificate Authority")
|
||||
allowDC = flag.Bool("allowDC", false, "whether this cert can be used with Delegated Credentials")
|
||||
rsaBits = flag.Int("rsa-bits", 2048, "Size of RSA key to generate. Ignored if --ecdsa-curve is set")
|
||||
ecdsaCurve = flag.String("ecdsa-curve", "", "ECDSA curve to use to generate a key. Valid values are P224, P256 (recommended), P384, P521")
|
||||
ed25519Key = flag.Bool("ed25519", false, "Generate an Ed25519 key")
|
||||
circlKey = flag.String("github.com/cloudflare/circl", "", "Generate a key supported by Circl")
|
||||
)
|
||||
|
||||
func publicKey(priv any) any {
|
||||
switch k := priv.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
return &k.PublicKey
|
||||
case *ecdsa.PrivateKey:
|
||||
return &k.PublicKey
|
||||
case ed25519.PrivateKey:
|
||||
return k.Public().(ed25519.PublicKey)
|
||||
case circlSign.PrivateKey:
|
||||
return k.Public()
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
if len(*host) == 0 {
|
||||
log.Fatalf("Missing required --host parameter")
|
||||
}
|
||||
|
||||
var priv any
|
||||
var err error
|
||||
switch *ecdsaCurve {
|
||||
case "":
|
||||
if *ed25519Key {
|
||||
_, priv, err = ed25519.GenerateKey(rand.Reader)
|
||||
} else if *circlKey != "" {
|
||||
scheme := circlSchemes.ByName(*circlKey)
|
||||
if scheme == nil {
|
||||
log.Fatalf("No such Circl scheme: %s", *circlKey)
|
||||
}
|
||||
_, priv, err = scheme.GenerateKey()
|
||||
} else {
|
||||
priv, err = rsa.GenerateKey(rand.Reader, *rsaBits)
|
||||
}
|
||||
case "P224":
|
||||
priv, err = ecdsa.GenerateKey(elliptic.P224(), rand.Reader)
|
||||
case "P256":
|
||||
priv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
case "P384":
|
||||
priv, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
|
||||
case "P521":
|
||||
priv, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
|
||||
default:
|
||||
log.Fatalf("Unrecognized elliptic curve: %q", *ecdsaCurve)
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to generate private key: %v", err)
|
||||
}
|
||||
|
||||
// ECDSA, ED25519 and RSA subject keys should have the DigitalSignature
|
||||
// KeyUsage bits set in the x509.Certificate template
|
||||
keyUsage := x509.KeyUsageDigitalSignature
|
||||
// Only RSA subject keys should have the KeyEncipherment KeyUsage bits set. In
|
||||
// the context of TLS this KeyUsage is particular to RSA key exchange and
|
||||
// authentication.
|
||||
if _, isRSA := priv.(*rsa.PrivateKey); isRSA {
|
||||
keyUsage |= x509.KeyUsageKeyEncipherment
|
||||
}
|
||||
|
||||
var notBefore time.Time
|
||||
if len(*validFrom) == 0 {
|
||||
notBefore = time.Now()
|
||||
} else {
|
||||
notBefore, err = time.Parse("Jan 2 15:04:05 2006", *validFrom)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to parse creation date: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
notAfter := notBefore.Add(*validFor)
|
||||
|
||||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to generate serial number: %v", err)
|
||||
}
|
||||
|
||||
template := x509.Certificate{
|
||||
SerialNumber: serialNumber,
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{"Acme Co"},
|
||||
},
|
||||
NotBefore: notBefore,
|
||||
NotAfter: notAfter,
|
||||
|
||||
KeyUsage: keyUsage,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||
BasicConstraintsValid: true,
|
||||
}
|
||||
|
||||
hosts := strings.Split(*host, ",")
|
||||
for _, h := range hosts {
|
||||
if ip := net.ParseIP(h); ip != nil {
|
||||
template.IPAddresses = append(template.IPAddresses, ip)
|
||||
} else {
|
||||
template.DNSNames = append(template.DNSNames, h)
|
||||
}
|
||||
}
|
||||
|
||||
if *isCA {
|
||||
if *allowDC {
|
||||
log.Fatal("Failed to create certificate: ca is not allowed with the dc flag")
|
||||
}
|
||||
|
||||
template.IsCA = true
|
||||
template.KeyUsage |= x509.KeyUsageCertSign
|
||||
}
|
||||
|
||||
if *allowDC {
|
||||
template.AllowDC = true
|
||||
template.KeyUsage |= x509.KeyUsageDigitalSignature
|
||||
}
|
||||
|
||||
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(priv), priv)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create certificate: %v", err)
|
||||
}
|
||||
|
||||
certOut, err := os.Create("cert.pem")
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to open cert.pem for writing: %v", err)
|
||||
}
|
||||
if err := pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}); err != nil {
|
||||
log.Fatalf("Failed to write data to cert.pem: %v", err)
|
||||
}
|
||||
if err := certOut.Close(); err != nil {
|
||||
log.Fatalf("Error closing cert.pem: %v", err)
|
||||
}
|
||||
log.Print("wrote cert.pem\n")
|
||||
|
||||
keyOut, err := os.OpenFile("key.pem", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to open key.pem for writing: %v", err)
|
||||
return
|
||||
}
|
||||
privBytes, err := x509.MarshalPKCS8PrivateKey(priv)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to marshal private key: %v", err)
|
||||
}
|
||||
if err := pem.Encode(keyOut, &pem.Block{Type: "PRIVATE KEY", Bytes: privBytes}); err != nil {
|
||||
log.Fatalf("Failed to write data to key.pem: %v", err)
|
||||
}
|
||||
if err := keyOut.Close(); err != nil {
|
||||
log.Fatalf("Error closing key.pem: %v", err)
|
||||
}
|
||||
log.Print("wrote key.pem\n")
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
// Copyright 2022 Cloudflare, Inc. All rights reserved. Use of this source code
|
||||
// is governed by a BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
//go:build ignore
|
||||
|
||||
// Generate a delegated credential with the given signature scheme, signed with
|
||||
// the given x.509 key pair. Outputs to 'dc.cred' and 'dckey.pem' and will
|
||||
// overwrite existing files.
|
||||
|
||||
// Example usage:
|
||||
// generate_delegated_credential -cert-path cert.pem -key-path key.pem -signature-scheme Ed25519 -duration 24h
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/ed25519"
|
||||
"crypto/rsa"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
circlSign "github.com/cloudflare/circl/sign"
|
||||
)
|
||||
|
||||
var (
|
||||
validFor = flag.Duration("duration", 5*24*time.Hour, "Duration that credential is valid for")
|
||||
signatureScheme = flag.String("signature-scheme", "", "The signature scheme used by the DC")
|
||||
certPath = flag.String("cert-path", "./cert.pem", "Path to signing cert")
|
||||
keyPath = flag.String("key-path", "./key.pem", "Path to signing key")
|
||||
isClient = flag.Bool("client-dc", false, "Create a client Delegated Credential")
|
||||
outPath = flag.String("out-path", "./", "Path to output directory")
|
||||
)
|
||||
|
||||
var SigStringMap = map[string]tls.SignatureScheme{
|
||||
// ECDSA algorithms. Only constrained to a specific curve in TLS 1.3.
|
||||
"ECDSAWithP256AndSHA256": tls.ECDSAWithP256AndSHA256,
|
||||
"ECDSAWithP384AndSHA384": tls.ECDSAWithP384AndSHA384,
|
||||
"ECDSAWithP521AndSHA512": tls.ECDSAWithP521AndSHA512,
|
||||
|
||||
// EdDSA algorithms.
|
||||
"Ed25519": tls.Ed25519,
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
sa := SigStringMap[*signatureScheme]
|
||||
|
||||
cert, err := tls.LoadX509KeyPair(*certPath, *keyPath)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to load certificate and key: %v", err)
|
||||
}
|
||||
cert.Leaf, err = x509.ParseCertificate(cert.Certificate[0])
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to parse leaf certificate: %v", err)
|
||||
}
|
||||
|
||||
validTime := time.Since(cert.Leaf.NotBefore) + *validFor
|
||||
dc, priv, err := tls.NewDelegatedCredential(&cert, sa, validTime, *isClient)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create a DC: %v\n", err)
|
||||
}
|
||||
dcBytes, err := dc.Marshal()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to marshal DC: %v\n", err)
|
||||
}
|
||||
|
||||
DCOut, err := os.Create(filepath.Join(*outPath, "dc.cred"))
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to open dc.cred for writing: %v", err)
|
||||
}
|
||||
|
||||
DCOut.Write(dcBytes)
|
||||
if err := DCOut.Close(); err != nil {
|
||||
log.Fatalf("Error closing dc.cred: %v", err)
|
||||
}
|
||||
log.Print("wrote dc.cred\n")
|
||||
|
||||
derBytes, err := x509.MarshalPKCS8PrivateKey(priv)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to marshal DC private key: %v\n", err)
|
||||
}
|
||||
|
||||
DCKeyOut, err := os.Create(filepath.Join(*outPath, "dckey.pem"))
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to open dckey.pem for writing: %v", err)
|
||||
}
|
||||
|
||||
if err := pem.Encode(DCKeyOut, &pem.Block{Type: "PRIVATE KEY", Bytes: derBytes}); err != nil {
|
||||
log.Fatalf("Failed to write data to dckey.pem: %v\n", err)
|
||||
}
|
||||
if err := DCKeyOut.Close(); err != nil {
|
||||
log.Fatalf("Error closing dckey.pem: %v\n", err)
|
||||
}
|
||||
log.Print("wrote dckey.pem\n")
|
||||
|
||||
fmt.Println("Success")
|
||||
}
|
||||
|
||||
// Copied from tls.go, because it's private.
|
||||
func parsePrivateKey(der []byte) (crypto.PrivateKey, error) {
|
||||
if key, err := x509.ParsePKCS1PrivateKey(der); err == nil {
|
||||
return key, nil
|
||||
}
|
||||
if key, err := x509.ParsePKCS8PrivateKey(der); err == nil {
|
||||
switch key := key.(type) {
|
||||
case *rsa.PrivateKey, *ecdsa.PrivateKey, ed25519.PrivateKey, circlSign.PrivateKey:
|
||||
return key, nil
|
||||
default:
|
||||
return nil, errors.New("tls: found unknown private key type in PKCS#8 wrapping")
|
||||
}
|
||||
}
|
||||
if key, err := x509.ParseECPrivateKey(der); err == nil {
|
||||
return key, nil
|
||||
}
|
||||
|
||||
return nil, errors.New("tls: failed to parse private key")
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user