mirror of
https://github.com/SagerNet/sing-box.git
synced 2024-11-25 10:01:30 +00:00
Improve tls dialer and listener
This commit is contained in:
parent
32e2730ec6
commit
1f05420745
|
@ -59,7 +59,61 @@ func NewTLS(dialer N.Dialer, serverAddress string, options option.OutboundTLSOpt
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if len(options.ALPN) > 0 {
|
||||||
|
tlsConfig.NextProtos = options.ALPN
|
||||||
|
}
|
||||||
|
if 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 := option.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 {
|
||||||
|
var certPool *x509.CertPool
|
||||||
|
if options.DisableSystemRoot {
|
||||||
|
certPool = x509.NewCertPool()
|
||||||
|
} else {
|
||||||
|
var err error
|
||||||
|
certPool, err = x509.SystemCertPool()
|
||||||
|
if err != nil {
|
||||||
|
return nil, E.Cause(err, "load system cert pool")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !certPool.AppendCertsFromPEM([]byte(options.Certificate)) {
|
||||||
|
return nil, E.New("failed to parse certificate:\n\n", options.Certificate)
|
||||||
|
}
|
||||||
|
tlsConfig.RootCAs = certPool
|
||||||
|
}
|
||||||
return &TLSDialer{
|
return &TLSDialer{
|
||||||
dialer: dialer,
|
dialer: dialer,
|
||||||
config: &tlsConfig,
|
config: &tlsConfig,
|
||||||
|
@ -75,7 +129,7 @@ func (d *TLSDialer) DialContext(ctx context.Context, network string, destination
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
tlsConn := tls.Client(conn, d.config)
|
tlsConn := tls.Client(conn, d.config)
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTCPTimeout)
|
ctx, cancel := context.WithTimeout(ctx, C.DefaultTCPTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
err = tlsConn.HandshakeContext(ctx)
|
err = tlsConn.HandshakeContext(ctx)
|
||||||
return tlsConn, err
|
return tlsConn, err
|
||||||
|
|
|
@ -27,7 +27,7 @@ func New(ctx context.Context, router adapter.Router, logger log.ContextLogger, o
|
||||||
case C.TypeSocks:
|
case C.TypeSocks:
|
||||||
return NewSocks(ctx, router, logger, options.Tag, options.SocksOptions), nil
|
return NewSocks(ctx, router, logger, options.Tag, options.SocksOptions), nil
|
||||||
case C.TypeHTTP:
|
case C.TypeHTTP:
|
||||||
return NewHTTP(ctx, router, logger, options.Tag, options.HTTPOptions), nil
|
return NewHTTP(ctx, router, logger, options.Tag, options.HTTPOptions)
|
||||||
case C.TypeMixed:
|
case C.TypeMixed:
|
||||||
return NewMixed(ctx, router, logger, options.Tag, options.MixedOptions), nil
|
return NewMixed(ctx, router, logger, options.Tag, options.MixedOptions), nil
|
||||||
case C.TypeShadowsocks:
|
case C.TypeShadowsocks:
|
||||||
|
|
|
@ -3,12 +3,14 @@ package inbound
|
||||||
import (
|
import (
|
||||||
std_bufio "bufio"
|
std_bufio "bufio"
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/common/auth"
|
"github.com/sagernet/sing/common/auth"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
@ -20,11 +22,12 @@ var _ adapter.Inbound = (*HTTP)(nil)
|
||||||
type HTTP struct {
|
type HTTP struct {
|
||||||
myInboundAdapter
|
myInboundAdapter
|
||||||
authenticator auth.Authenticator
|
authenticator auth.Authenticator
|
||||||
|
tlsConfig *tls.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHTTP(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.HTTPMixedInboundOptions) *HTTP {
|
func NewHTTP(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.HTTPMixedInboundOptions) (*HTTP, error) {
|
||||||
inbound := &HTTP{
|
inbound := &HTTP{
|
||||||
myInboundAdapter{
|
myInboundAdapter: myInboundAdapter{
|
||||||
protocol: C.TypeHTTP,
|
protocol: C.TypeHTTP,
|
||||||
network: []string{C.NetworkTCP},
|
network: []string{C.NetworkTCP},
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
|
@ -34,13 +37,23 @@ func NewHTTP(ctx context.Context, router adapter.Router, logger log.ContextLogge
|
||||||
listenOptions: options.ListenOptions,
|
listenOptions: options.ListenOptions,
|
||||||
setSystemProxy: options.SetSystemProxy,
|
setSystemProxy: options.SetSystemProxy,
|
||||||
},
|
},
|
||||||
auth.NewAuthenticator(options.Users),
|
authenticator: auth.NewAuthenticator(options.Users),
|
||||||
|
}
|
||||||
|
if options.TLS != nil {
|
||||||
|
tlsConfig, err := NewTLSConfig(common.PtrValueOrDefault(options.TLS))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
inbound.tlsConfig = tlsConfig
|
||||||
}
|
}
|
||||||
inbound.connHandler = inbound
|
inbound.connHandler = inbound
|
||||||
return inbound
|
return inbound, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *HTTP) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
func (h *HTTP) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||||
|
if h.tlsConfig != nil {
|
||||||
|
conn = tls.Server(conn, h.tlsConfig)
|
||||||
|
}
|
||||||
return http.HandleConnection(ctx, conn, std_bufio.NewReader(conn), h.authenticator, h.upstreamUserHandler(metadata), M.Metadata{})
|
return http.HandleConnection(ctx, conn, std_bufio.NewReader(conn), h.authenticator, h.upstreamUserHandler(metadata), M.Metadata{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
80
inbound/tls.go
Normal file
80
inbound/tls.go
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
package inbound
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/option"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewTLSConfig(options option.InboundTLSOptions) (*tls.Config, error) {
|
||||||
|
if !options.Enabled {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
var tlsConfig tls.Config
|
||||||
|
if options.ServerName != "" {
|
||||||
|
tlsConfig.ServerName = options.ServerName
|
||||||
|
}
|
||||||
|
if len(options.ALPN) > 0 {
|
||||||
|
tlsConfig.NextProtos = options.ALPN
|
||||||
|
}
|
||||||
|
if 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 := option.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
|
||||||
|
}
|
||||||
|
var key []byte
|
||||||
|
if options.Key != "" {
|
||||||
|
key = []byte(options.Key)
|
||||||
|
} else if options.KeyPath != "" {
|
||||||
|
content, err := os.ReadFile(options.KeyPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, E.Cause(err, "read key")
|
||||||
|
}
|
||||||
|
key = content
|
||||||
|
}
|
||||||
|
if certificate == nil {
|
||||||
|
return nil, E.New("missing certificate")
|
||||||
|
}
|
||||||
|
if key == nil {
|
||||||
|
return nil, E.New("missing key")
|
||||||
|
}
|
||||||
|
keyPair, err := tls.X509KeyPair(certificate, key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, E.Cause(err, "parse x509 key pair")
|
||||||
|
}
|
||||||
|
tlsConfig.Certificates = []tls.Certificate{keyPair}
|
||||||
|
return &tlsConfig, nil
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ package inbound
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
@ -22,6 +23,7 @@ type VMess struct {
|
||||||
myInboundAdapter
|
myInboundAdapter
|
||||||
service *vmess.Service[int]
|
service *vmess.Service[int]
|
||||||
users []option.VMessUser
|
users []option.VMessUser
|
||||||
|
tlsConfig *tls.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.VMessInboundOptions) (*VMess, error) {
|
func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.VMessInboundOptions) (*VMess, error) {
|
||||||
|
@ -46,12 +48,21 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if options.TLS != nil {
|
||||||
|
inbound.tlsConfig, err = NewTLSConfig(common.PtrValueOrDefault(options.TLS))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
inbound.service = service
|
inbound.service = service
|
||||||
inbound.connHandler = inbound
|
inbound.connHandler = inbound
|
||||||
return inbound, nil
|
return inbound, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *VMess) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
func (h *VMess) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||||
|
if h.tlsConfig != nil {
|
||||||
|
conn = tls.Server(conn, h.tlsConfig)
|
||||||
|
}
|
||||||
return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata))
|
return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -128,12 +128,14 @@ type HTTPMixedInboundOptions struct {
|
||||||
ListenOptions
|
ListenOptions
|
||||||
Users []auth.User `json:"users,omitempty"`
|
Users []auth.User `json:"users,omitempty"`
|
||||||
SetSystemProxy bool `json:"set_system_proxy,omitempty"`
|
SetSystemProxy bool `json:"set_system_proxy,omitempty"`
|
||||||
|
TLS *InboundTLSOptions `json:"tls,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o HTTPMixedInboundOptions) Equals(other HTTPMixedInboundOptions) bool {
|
func (o HTTPMixedInboundOptions) Equals(other HTTPMixedInboundOptions) bool {
|
||||||
return o.ListenOptions == other.ListenOptions &&
|
return o.ListenOptions == other.ListenOptions &&
|
||||||
common.ComparableSliceEquals(o.Users, other.Users) &&
|
common.ComparableSliceEquals(o.Users, other.Users) &&
|
||||||
o.SetSystemProxy == other.SetSystemProxy
|
o.SetSystemProxy == other.SetSystemProxy &&
|
||||||
|
common.PtrEquals(o.TLS, other.TLS)
|
||||||
}
|
}
|
||||||
|
|
||||||
type DirectInboundOptions struct {
|
type DirectInboundOptions struct {
|
||||||
|
@ -175,11 +177,13 @@ type ShadowsocksDestination struct {
|
||||||
type VMessInboundOptions struct {
|
type VMessInboundOptions struct {
|
||||||
ListenOptions
|
ListenOptions
|
||||||
Users []VMessUser `json:"users,omitempty"`
|
Users []VMessUser `json:"users,omitempty"`
|
||||||
|
TLS *InboundTLSOptions `json:"tls,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o VMessInboundOptions) Equals(other VMessInboundOptions) bool {
|
func (o VMessInboundOptions) Equals(other VMessInboundOptions) bool {
|
||||||
return o.ListenOptions == other.ListenOptions &&
|
return o.ListenOptions == other.ListenOptions &&
|
||||||
common.ComparableSliceEquals(o.Users, other.Users)
|
common.ComparableSliceEquals(o.Users, other.Users) &&
|
||||||
|
common.PtrEquals(o.TLS, other.TLS)
|
||||||
}
|
}
|
||||||
|
|
||||||
type VMessUser struct {
|
type VMessUser struct {
|
||||||
|
|
|
@ -140,13 +140,6 @@ type HTTPOutboundOptions struct {
|
||||||
TLSOptions *OutboundTLSOptions `json:"tls,omitempty"`
|
TLSOptions *OutboundTLSOptions `json:"tls,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
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"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ShadowsocksOutboundOptions struct {
|
type ShadowsocksOutboundOptions struct {
|
||||||
OutboundDialerOptions
|
OutboundDialerOptions
|
||||||
ServerOptions
|
ServerOptions
|
||||||
|
@ -165,6 +158,11 @@ type VMessOutboundOptions struct {
|
||||||
AuthenticatedLength bool `json:"authenticated_length,omitempty"`
|
AuthenticatedLength bool `json:"authenticated_length,omitempty"`
|
||||||
Network NetworkList `json:"network,omitempty"`
|
Network NetworkList `json:"network,omitempty"`
|
||||||
TLSOptions *OutboundTLSOptions `json:"tls,omitempty"`
|
TLSOptions *OutboundTLSOptions `json:"tls,omitempty"`
|
||||||
|
TransportOptions *VMessTransportOptions `json:"transport,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type VMessTransportOptions struct {
|
||||||
|
Network string `json:"network,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SelectorOutboundOptions struct {
|
type SelectorOutboundOptions struct {
|
||||||
|
|
77
option/tls.go
Normal file
77
option/tls.go
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
package option
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing/common"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
)
|
||||||
|
|
||||||
|
type InboundTLSOptions struct {
|
||||||
|
Enabled bool `json:"enabled,omitempty"`
|
||||||
|
ServerName string `json:"server_name,omitempty"`
|
||||||
|
ALPN []string `json:"alpn,omitempty"`
|
||||||
|
MinVersion string `json:"min_version,omitempty"`
|
||||||
|
MaxVersion string `json:"max_version,omitempty"`
|
||||||
|
CipherSuites []string `json:"cipher_suites,omitempty"`
|
||||||
|
Certificate string `json:"certificate,omitempty"`
|
||||||
|
CertificatePath string `json:"certificate_path,omitempty"`
|
||||||
|
Key string `json:"key,omitempty"`
|
||||||
|
KeyPath string `json:"key_path,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o InboundTLSOptions) Equals(other InboundTLSOptions) bool {
|
||||||
|
return o.Enabled == other.Enabled &&
|
||||||
|
o.ServerName == other.ServerName &&
|
||||||
|
common.ComparableSliceEquals(o.ALPN, other.ALPN) &&
|
||||||
|
o.MinVersion == other.MinVersion &&
|
||||||
|
o.MaxVersion == other.MaxVersion &&
|
||||||
|
common.ComparableSliceEquals(o.CipherSuites, other.CipherSuites) &&
|
||||||
|
o.Certificate == other.Certificate &&
|
||||||
|
o.CertificatePath == other.CertificatePath &&
|
||||||
|
o.Key == other.Key &&
|
||||||
|
o.KeyPath == other.KeyPath
|
||||||
|
}
|
||||||
|
|
||||||
|
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 []string `json:"alpn,omitempty"`
|
||||||
|
MinVersion string `json:"min_version,omitempty"`
|
||||||
|
MaxVersion string `json:"max_version,omitempty"`
|
||||||
|
CipherSuites []string `json:"cipher_suites,omitempty"`
|
||||||
|
DisableSystemRoot bool `json:"disable_system_root,omitempty"`
|
||||||
|
Certificate string `json:"certificate,omitempty"`
|
||||||
|
CertificatePath string `json:"certificate_path,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o OutboundTLSOptions) Equals(other OutboundTLSOptions) bool {
|
||||||
|
return o.Enabled == other.Enabled &&
|
||||||
|
o.DisableSNI == other.DisableSNI &&
|
||||||
|
o.ServerName == other.ServerName &&
|
||||||
|
o.Insecure == other.Insecure &&
|
||||||
|
common.ComparableSliceEquals(o.ALPN, other.ALPN) &&
|
||||||
|
o.MinVersion == other.MinVersion &&
|
||||||
|
o.MaxVersion == other.MaxVersion &&
|
||||||
|
common.ComparableSliceEquals(o.CipherSuites, other.CipherSuites) &&
|
||||||
|
o.DisableSystemRoot == other.DisableSystemRoot &&
|
||||||
|
o.Certificate == other.Certificate &&
|
||||||
|
o.CertificatePath == other.CertificatePath
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue