mirror of
https://gitclone.com/github.com/MetaCubeX/Clash.Meta
synced 2025-05-18 16:08:04 +08:00
feat: inbound support shadow-tls
This commit is contained in:
parent
52ad793d11
commit
99aa1b0de1
@ -84,11 +84,12 @@ type gostObfsOption struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type shadowTLSOption struct {
|
type shadowTLSOption struct {
|
||||||
Password string `obfs:"password,omitempty"`
|
Password string `obfs:"password,omitempty"`
|
||||||
Host string `obfs:"host"`
|
Host string `obfs:"host"`
|
||||||
Fingerprint string `obfs:"fingerprint,omitempty"`
|
Fingerprint string `obfs:"fingerprint,omitempty"`
|
||||||
SkipCertVerify bool `obfs:"skip-cert-verify,omitempty"`
|
SkipCertVerify bool `obfs:"skip-cert-verify,omitempty"`
|
||||||
Version int `obfs:"version,omitempty"`
|
Version int `obfs:"version,omitempty"`
|
||||||
|
ALPN []string `obfs:"alpn,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type restlsOption struct {
|
type restlsOption struct {
|
||||||
@ -342,6 +343,12 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) {
|
|||||||
SkipCertVerify: opt.SkipCertVerify,
|
SkipCertVerify: opt.SkipCertVerify,
|
||||||
Version: opt.Version,
|
Version: opt.Version,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if opt.ALPN != nil { // structure's Decode will ensure value not nil when input has value even it was set an empty array
|
||||||
|
shadowTLSOpt.ALPN = opt.ALPN
|
||||||
|
} else {
|
||||||
|
shadowTLSOpt.ALPN = shadowtls.DefaultALPN
|
||||||
|
}
|
||||||
} else if option.Plugin == restls.Mode {
|
} else if option.Plugin == restls.Mode {
|
||||||
obfsMode = restls.Mode
|
obfsMode = restls.Mode
|
||||||
restlsOpt := &restlsOption{}
|
restlsOpt := &restlsOption{}
|
||||||
|
@ -448,6 +448,7 @@ proxies: # socks5
|
|||||||
host: "cloud.tencent.com"
|
host: "cloud.tencent.com"
|
||||||
password: "shadow_tls_password"
|
password: "shadow_tls_password"
|
||||||
version: 2 # support 1/2/3
|
version: 2 # support 1/2/3
|
||||||
|
# alpn: ["h2","http/1.1"]
|
||||||
|
|
||||||
- name: "ss5"
|
- name: "ss5"
|
||||||
type: ss
|
type: ss
|
||||||
@ -1179,6 +1180,15 @@ listeners:
|
|||||||
# proxy: proxy # 如果不为空则直接将该入站流量交由指定 proxy 处理 (当 proxy 不为空时,这里的 proxy 名称必须合法,否则会出错)
|
# proxy: proxy # 如果不为空则直接将该入站流量交由指定 proxy 处理 (当 proxy 不为空时,这里的 proxy 名称必须合法,否则会出错)
|
||||||
password: vlmpIPSyHH6f4S8WVPdRIHIlzmB+GIRfoH3aNJ/t9Gg=
|
password: vlmpIPSyHH6f4S8WVPdRIHIlzmB+GIRfoH3aNJ/t9Gg=
|
||||||
cipher: 2022-blake3-aes-256-gcm
|
cipher: 2022-blake3-aes-256-gcm
|
||||||
|
# shadow-tls:
|
||||||
|
# enable: false # 设置为true时开启
|
||||||
|
# version: 3 # 支持v1/v2/v3
|
||||||
|
# password: password # v2设置项
|
||||||
|
# users: # v3设置项
|
||||||
|
# - name: 1
|
||||||
|
# password: password
|
||||||
|
# handshake:
|
||||||
|
# dest: test.com:443
|
||||||
|
|
||||||
- name: vmess-in-1
|
- name: vmess-in-1
|
||||||
type: vmess
|
type: vmess
|
||||||
|
@ -13,6 +13,7 @@ type ShadowsocksServer struct {
|
|||||||
Cipher string
|
Cipher string
|
||||||
Udp bool
|
Udp bool
|
||||||
MuxOption sing.MuxOption `yaml:"mux-option" json:"mux-option,omitempty"`
|
MuxOption sing.MuxOption `yaml:"mux-option" json:"mux-option,omitempty"`
|
||||||
|
ShadowTLS ShadowTLS `yaml:"shadow-tls" json:"shadow-tls,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t ShadowsocksServer) String() string {
|
func (t ShadowsocksServer) String() string {
|
||||||
|
22
listener/config/shadowtls.go
Normal file
22
listener/config/shadowtls.go
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
type ShadowTLS struct {
|
||||||
|
Enable bool
|
||||||
|
Version int
|
||||||
|
Password string
|
||||||
|
Users []ShadowTLSUser
|
||||||
|
Handshake ShadowTLSHandshakeOptions
|
||||||
|
HandshakeForServerName map[string]ShadowTLSHandshakeOptions
|
||||||
|
StrictMode bool
|
||||||
|
WildcardSNI string
|
||||||
|
}
|
||||||
|
|
||||||
|
type ShadowTLSUser struct {
|
||||||
|
Name string
|
||||||
|
Password string
|
||||||
|
}
|
||||||
|
|
||||||
|
type ShadowTLSHandshakeOptions struct {
|
||||||
|
Dest string
|
||||||
|
Proxy string
|
||||||
|
}
|
@ -17,6 +17,7 @@ import (
|
|||||||
N "github.com/metacubex/mihomo/common/net"
|
N "github.com/metacubex/mihomo/common/net"
|
||||||
"github.com/metacubex/mihomo/common/utils"
|
"github.com/metacubex/mihomo/common/utils"
|
||||||
"github.com/metacubex/mihomo/component/ca"
|
"github.com/metacubex/mihomo/component/ca"
|
||||||
|
"github.com/metacubex/mihomo/component/dialer"
|
||||||
"github.com/metacubex/mihomo/component/generater"
|
"github.com/metacubex/mihomo/component/generater"
|
||||||
C "github.com/metacubex/mihomo/constant"
|
C "github.com/metacubex/mihomo/constant"
|
||||||
|
|
||||||
@ -36,6 +37,7 @@ var tlsClientConfig, _ = ca.GetTLSConfig(nil, tlsFingerprint, "", "")
|
|||||||
var realityPrivateKey, realityPublickey string
|
var realityPrivateKey, realityPublickey string
|
||||||
var realityDest = "itunes.apple.com"
|
var realityDest = "itunes.apple.com"
|
||||||
var realityShortid = "10f897e26c4b9478"
|
var realityShortid = "10f897e26c4b9478"
|
||||||
|
var realityRealDial = false
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
rand.Read(httpData)
|
rand.Read(httpData)
|
||||||
@ -205,6 +207,14 @@ func NewHttpTestTunnel() *TestTunnel {
|
|||||||
if metadata.DstPort == 443 {
|
if metadata.DstPort == 443 {
|
||||||
tlsConn := tls.Server(c, tlsConfig.Clone())
|
tlsConn := tls.Server(c, tlsConfig.Clone())
|
||||||
if metadata.Host == realityDest { // ignore the tls handshake error for realityDest
|
if metadata.Host == realityDest { // ignore the tls handshake error for realityDest
|
||||||
|
if realityRealDial {
|
||||||
|
rconn, err := dialer.DialContext(ctx, "tcp", metadata.RemoteAddress())
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
N.Relay(rconn, tlsConn)
|
||||||
|
return
|
||||||
|
}
|
||||||
ctx, cancel := context.WithTimeout(ctx, C.DefaultTLSTimeout)
|
ctx, cancel := context.WithTimeout(ctx, C.DefaultTLSTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
if err := tlsConn.HandshakeContext(ctx); err != nil {
|
if err := tlsConn.HandshakeContext(ctx); err != nil {
|
||||||
|
@ -15,6 +15,7 @@ type ShadowSocksOption struct {
|
|||||||
Cipher string `inbound:"cipher"`
|
Cipher string `inbound:"cipher"`
|
||||||
UDP bool `inbound:"udp,omitempty"`
|
UDP bool `inbound:"udp,omitempty"`
|
||||||
MuxOption MuxOption `inbound:"mux-option,omitempty"`
|
MuxOption MuxOption `inbound:"mux-option,omitempty"`
|
||||||
|
ShadowTLS ShadowTLS `inbound:"shadow-tls,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o ShadowSocksOption) Equal(config C.InboundConfig) bool {
|
func (o ShadowSocksOption) Equal(config C.InboundConfig) bool {
|
||||||
@ -43,6 +44,7 @@ func NewShadowSocks(options *ShadowSocksOption) (*ShadowSocks, error) {
|
|||||||
Cipher: options.Cipher,
|
Cipher: options.Cipher,
|
||||||
Udp: options.UDP,
|
Udp: options.UDP,
|
||||||
MuxOption: options.MuxOption.Build(),
|
MuxOption: options.MuxOption.Build(),
|
||||||
|
ShadowTLS: options.ShadowTLS.Build(),
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
@ -3,12 +3,14 @@ package inbound_test
|
|||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/metacubex/mihomo/adapter/outbound"
|
"github.com/metacubex/mihomo/adapter/outbound"
|
||||||
"github.com/metacubex/mihomo/listener/inbound"
|
"github.com/metacubex/mihomo/listener/inbound"
|
||||||
|
shadowtls "github.com/metacubex/mihomo/transport/sing-shadowtls"
|
||||||
|
|
||||||
shadowsocks "github.com/metacubex/sing-shadowsocks"
|
shadowsocks "github.com/metacubex/sing-shadowsocks"
|
||||||
"github.com/metacubex/sing-shadowsocks/shadowaead"
|
"github.com/metacubex/sing-shadowsocks/shadowaead"
|
||||||
@ -18,6 +20,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var shadowsocksCipherList = []string{shadowsocks.MethodNone}
|
var shadowsocksCipherList = []string{shadowsocks.MethodNone}
|
||||||
|
var shadowsocksCipherListShort = []string{shadowsocks.MethodNone}
|
||||||
var shadowsocksPassword32 string
|
var shadowsocksPassword32 string
|
||||||
var shadowsocksPassword16 string
|
var shadowsocksPassword16 string
|
||||||
|
|
||||||
@ -25,15 +28,17 @@ func init() {
|
|||||||
shadowsocksCipherList = append(shadowsocksCipherList, shadowaead.List...)
|
shadowsocksCipherList = append(shadowsocksCipherList, shadowaead.List...)
|
||||||
shadowsocksCipherList = append(shadowsocksCipherList, shadowaead_2022.List...)
|
shadowsocksCipherList = append(shadowsocksCipherList, shadowaead_2022.List...)
|
||||||
shadowsocksCipherList = append(shadowsocksCipherList, shadowstream.List...)
|
shadowsocksCipherList = append(shadowsocksCipherList, shadowstream.List...)
|
||||||
|
shadowsocksCipherListShort = append(shadowsocksCipherListShort, shadowaead.List[0])
|
||||||
|
shadowsocksCipherListShort = append(shadowsocksCipherListShort, shadowaead_2022.List[0])
|
||||||
passwordBytes := make([]byte, 32)
|
passwordBytes := make([]byte, 32)
|
||||||
rand.Read(passwordBytes)
|
rand.Read(passwordBytes)
|
||||||
shadowsocksPassword32 = base64.StdEncoding.EncodeToString(passwordBytes)
|
shadowsocksPassword32 = base64.StdEncoding.EncodeToString(passwordBytes)
|
||||||
shadowsocksPassword16 = base64.StdEncoding.EncodeToString(passwordBytes[:16])
|
shadowsocksPassword16 = base64.StdEncoding.EncodeToString(passwordBytes[:16])
|
||||||
}
|
}
|
||||||
|
|
||||||
func testInboundShadowSocks(t *testing.T, inboundOptions inbound.ShadowSocksOption, outboundOptions outbound.ShadowSocksOption) {
|
func testInboundShadowSocks(t *testing.T, inboundOptions inbound.ShadowSocksOption, outboundOptions outbound.ShadowSocksOption, cipherList []string) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
for _, cipher := range shadowsocksCipherList {
|
for _, cipher := range cipherList {
|
||||||
cipher := cipher
|
cipher := cipher
|
||||||
t.Run(cipher, func(t *testing.T) {
|
t.Run(cipher, func(t *testing.T) {
|
||||||
inboundOptions, outboundOptions := inboundOptions, outboundOptions // don't modify outside options value
|
inboundOptions, outboundOptions := inboundOptions, outboundOptions // don't modify outside options value
|
||||||
@ -94,5 +99,53 @@ func testInboundShadowSocks0(t *testing.T, inboundOptions inbound.ShadowSocksOpt
|
|||||||
func TestInboundShadowSocks_Basic(t *testing.T) {
|
func TestInboundShadowSocks_Basic(t *testing.T) {
|
||||||
inboundOptions := inbound.ShadowSocksOption{}
|
inboundOptions := inbound.ShadowSocksOption{}
|
||||||
outboundOptions := outbound.ShadowSocksOption{}
|
outboundOptions := outbound.ShadowSocksOption{}
|
||||||
testInboundShadowSocks(t, inboundOptions, outboundOptions)
|
testInboundShadowSocks(t, inboundOptions, outboundOptions, shadowsocksCipherList)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInboundShadowSocks_ShadowTlsv1(t *testing.T) {
|
||||||
|
inboundOptions := inbound.ShadowSocksOption{
|
||||||
|
ShadowTLS: inbound.ShadowTLS{
|
||||||
|
Enable: true,
|
||||||
|
Version: 1,
|
||||||
|
Handshake: inbound.ShadowTLSHandshakeOptions{Dest: net.JoinHostPort(realityDest, "443")},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
outboundOptions := outbound.ShadowSocksOption{
|
||||||
|
Plugin: shadowtls.Mode,
|
||||||
|
PluginOpts: map[string]any{"host": realityDest, "fingerprint": tlsFingerprint, "version": 1},
|
||||||
|
}
|
||||||
|
testInboundShadowSocks(t, inboundOptions, outboundOptions, shadowsocksCipherListShort)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInboundShadowSocks_ShadowTlsv2(t *testing.T) {
|
||||||
|
inboundOptions := inbound.ShadowSocksOption{
|
||||||
|
ShadowTLS: inbound.ShadowTLS{
|
||||||
|
Enable: true,
|
||||||
|
Version: 2,
|
||||||
|
Password: shadowsocksPassword16,
|
||||||
|
Handshake: inbound.ShadowTLSHandshakeOptions{Dest: net.JoinHostPort(realityDest, "443")},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
outboundOptions := outbound.ShadowSocksOption{
|
||||||
|
Plugin: shadowtls.Mode,
|
||||||
|
PluginOpts: map[string]any{"host": realityDest, "password": shadowsocksPassword16, "fingerprint": tlsFingerprint, "version": 2},
|
||||||
|
}
|
||||||
|
outboundOptions.PluginOpts["alpn"] = []string{"http/1.1"} // shadowtls v2 work confuse with http/2 server, so we set alpn to http/1.1 to pass the test
|
||||||
|
testInboundShadowSocks(t, inboundOptions, outboundOptions, shadowsocksCipherListShort)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInboundShadowSocks_ShadowTlsv3(t *testing.T) {
|
||||||
|
inboundOptions := inbound.ShadowSocksOption{
|
||||||
|
ShadowTLS: inbound.ShadowTLS{
|
||||||
|
Enable: true,
|
||||||
|
Version: 3,
|
||||||
|
Users: []inbound.ShadowTLSUser{{Name: "test", Password: shadowsocksPassword16}},
|
||||||
|
Handshake: inbound.ShadowTLSHandshakeOptions{Dest: net.JoinHostPort(realityDest, "443")},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
outboundOptions := outbound.ShadowSocksOption{
|
||||||
|
Plugin: shadowtls.Mode,
|
||||||
|
PluginOpts: map[string]any{"host": realityDest, "password": shadowsocksPassword16, "fingerprint": tlsFingerprint, "version": 3},
|
||||||
|
}
|
||||||
|
testInboundShadowSocks(t, inboundOptions, outboundOptions, shadowsocksCipherListShort)
|
||||||
}
|
}
|
||||||
|
58
listener/inbound/shadowtls.go
Normal file
58
listener/inbound/shadowtls.go
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
package inbound
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/metacubex/mihomo/common/utils"
|
||||||
|
LC "github.com/metacubex/mihomo/listener/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ShadowTLS struct {
|
||||||
|
Enable bool `inbound:"enable"`
|
||||||
|
Version int `inbound:"version,omitempty"`
|
||||||
|
Password string `inbound:"password,omitempty"`
|
||||||
|
Users []ShadowTLSUser `inbound:"users,omitempty"`
|
||||||
|
Handshake ShadowTLSHandshakeOptions `inbound:"handshake,omitempty"`
|
||||||
|
HandshakeForServerName map[string]ShadowTLSHandshakeOptions `inbound:"handshake-for-server-name,omitempty"`
|
||||||
|
StrictMode bool `inbound:"strict-mode,omitempty"`
|
||||||
|
WildcardSNI string `inbound:"wildcard-sni,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ShadowTLSUser struct {
|
||||||
|
Name string `inbound:"name,omitempty"`
|
||||||
|
Password string `inbound:"password,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ShadowTLSHandshakeOptions struct {
|
||||||
|
Dest string `inbound:"dest"`
|
||||||
|
Proxy string `inbound:"proxy,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c ShadowTLS) Build() LC.ShadowTLS {
|
||||||
|
handshakeForServerName := make(map[string]LC.ShadowTLSHandshakeOptions)
|
||||||
|
for k, v := range c.HandshakeForServerName {
|
||||||
|
handshakeForServerName[k] = v.Build()
|
||||||
|
}
|
||||||
|
return LC.ShadowTLS{
|
||||||
|
Enable: c.Enable,
|
||||||
|
Version: c.Version,
|
||||||
|
Password: c.Password,
|
||||||
|
Users: utils.Map(c.Users, ShadowTLSUser.Build),
|
||||||
|
Handshake: c.Handshake.Build(),
|
||||||
|
HandshakeForServerName: handshakeForServerName,
|
||||||
|
StrictMode: c.StrictMode,
|
||||||
|
WildcardSNI: c.WildcardSNI,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c ShadowTLSUser) Build() LC.ShadowTLSUser {
|
||||||
|
return LC.ShadowTLSUser{
|
||||||
|
Name: c.Name,
|
||||||
|
Password: c.Password,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c ShadowTLSHandshakeOptions) Build() LC.ShadowTLSHandshakeOptions {
|
||||||
|
return LC.ShadowTLSHandshakeOptions{
|
||||||
|
Dest: c.Dest,
|
||||||
|
Proxy: c.Proxy,
|
||||||
|
}
|
||||||
|
}
|
35
listener/sing/dialer.go
Normal file
35
listener/sing/dialer.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package sing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
C "github.com/metacubex/mihomo/constant"
|
||||||
|
"github.com/metacubex/mihomo/listener/inner"
|
||||||
|
|
||||||
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Dialer struct {
|
||||||
|
t C.Tunnel
|
||||||
|
proxy string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Dialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||||
|
if network != "tcp" && network != "tcp4" && network != "tcp6" {
|
||||||
|
return nil, fmt.Errorf("unsupported network %s", network)
|
||||||
|
}
|
||||||
|
return inner.HandleTcp(d.t, destination.String(), d.proxy)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Dialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||||
|
return nil, fmt.Errorf("unsupported ListenPacket")
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ N.Dialer = (*Dialer)(nil)
|
||||||
|
|
||||||
|
func NewDialer(t C.Tunnel, proxy string) (d *Dialer) {
|
||||||
|
return &Dialer{t, proxy}
|
||||||
|
}
|
@ -18,6 +18,7 @@ import (
|
|||||||
shadowsocks "github.com/metacubex/sing-shadowsocks"
|
shadowsocks "github.com/metacubex/sing-shadowsocks"
|
||||||
"github.com/metacubex/sing-shadowsocks/shadowaead"
|
"github.com/metacubex/sing-shadowsocks/shadowaead"
|
||||||
"github.com/metacubex/sing-shadowsocks/shadowaead_2022"
|
"github.com/metacubex/sing-shadowsocks/shadowaead_2022"
|
||||||
|
shadowtls "github.com/metacubex/sing-shadowtls"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/common/buf"
|
"github.com/sagernet/sing/common/buf"
|
||||||
"github.com/sagernet/sing/common/bufio"
|
"github.com/sagernet/sing/common/bufio"
|
||||||
@ -31,10 +32,24 @@ type Listener struct {
|
|||||||
listeners []net.Listener
|
listeners []net.Listener
|
||||||
udpListeners []net.PacketConn
|
udpListeners []net.PacketConn
|
||||||
service shadowsocks.Service
|
service shadowsocks.Service
|
||||||
|
shadowTLS *shadowtls.Service
|
||||||
}
|
}
|
||||||
|
|
||||||
var _listener *Listener
|
var _listener *Listener
|
||||||
|
|
||||||
|
// shadowTLSService is a wrapper for shadowsocks.Service to support shadowTLS.
|
||||||
|
type shadowTLSService struct {
|
||||||
|
shadowsocks.Service
|
||||||
|
shadowTLS *shadowtls.Service
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *shadowTLSService) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
||||||
|
if s.shadowTLS != nil {
|
||||||
|
return s.shadowTLS.NewConnection(ctx, conn, metadata)
|
||||||
|
}
|
||||||
|
return s.Service.NewConnection(ctx, conn, metadata)
|
||||||
|
}
|
||||||
|
|
||||||
func New(config LC.ShadowsocksServer, tunnel C.Tunnel, additions ...inbound.Addition) (C.MultiAddrListener, error) {
|
func New(config LC.ShadowsocksServer, tunnel C.Tunnel, additions ...inbound.Addition) (C.MultiAddrListener, error) {
|
||||||
var sl *Listener
|
var sl *Listener
|
||||||
var err error
|
var err error
|
||||||
@ -60,7 +75,8 @@ func New(config LC.ShadowsocksServer, tunnel C.Tunnel, additions ...inbound.Addi
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
sl = &Listener{false, config, nil, nil, nil}
|
sl = &Listener{}
|
||||||
|
sl.config = config
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case config.Cipher == shadowsocks.MethodNone:
|
case config.Cipher == shadowsocks.MethodNone:
|
||||||
@ -77,6 +93,51 @@ func New(config LC.ShadowsocksServer, tunnel C.Tunnel, additions ...inbound.Addi
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if config.ShadowTLS.Enable {
|
||||||
|
buildHandshake := func(handshake LC.ShadowTLSHandshakeOptions) (handshakeConfig shadowtls.HandshakeConfig) {
|
||||||
|
handshakeConfig.Server = M.ParseSocksaddr(handshake.Dest)
|
||||||
|
handshakeConfig.Dialer = sing.NewDialer(tunnel, handshake.Proxy)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var handshakeForServerName map[string]shadowtls.HandshakeConfig
|
||||||
|
if config.ShadowTLS.Version > 1 {
|
||||||
|
handshakeForServerName = make(map[string]shadowtls.HandshakeConfig)
|
||||||
|
for serverName, serverOptions := range config.ShadowTLS.HandshakeForServerName {
|
||||||
|
handshakeForServerName[serverName] = buildHandshake(serverOptions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var wildcardSNI shadowtls.WildcardSNI
|
||||||
|
switch config.ShadowTLS.WildcardSNI {
|
||||||
|
case "authed":
|
||||||
|
wildcardSNI = shadowtls.WildcardSNIAuthed
|
||||||
|
case "all":
|
||||||
|
wildcardSNI = shadowtls.WildcardSNIAll
|
||||||
|
default:
|
||||||
|
wildcardSNI = shadowtls.WildcardSNIOff
|
||||||
|
}
|
||||||
|
var shadowTLS *shadowtls.Service
|
||||||
|
shadowTLS, err = shadowtls.NewService(shadowtls.ServiceConfig{
|
||||||
|
Version: config.ShadowTLS.Version,
|
||||||
|
Password: config.ShadowTLS.Password,
|
||||||
|
Users: common.Map(config.ShadowTLS.Users, func(it LC.ShadowTLSUser) shadowtls.User {
|
||||||
|
return shadowtls.User{Name: it.Name, Password: it.Password}
|
||||||
|
}),
|
||||||
|
Handshake: buildHandshake(config.ShadowTLS.Handshake),
|
||||||
|
HandshakeForServerName: handshakeForServerName,
|
||||||
|
StrictMode: config.ShadowTLS.StrictMode,
|
||||||
|
WildcardSNI: wildcardSNI,
|
||||||
|
Handler: sl.service,
|
||||||
|
Logger: log.SingLogger,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
sl.service = &shadowTLSService{
|
||||||
|
Service: sl.service,
|
||||||
|
shadowTLS: shadowTLS,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for _, addr := range strings.Split(config.Listen, ",") {
|
for _, addr := range strings.Split(config.Listen, ",") {
|
||||||
addr := addr
|
addr := addr
|
||||||
|
|
||||||
|
@ -28,11 +28,12 @@ type ShadowTLSOption struct {
|
|||||||
ClientFingerprint string
|
ClientFingerprint string
|
||||||
SkipCertVerify bool
|
SkipCertVerify bool
|
||||||
Version int
|
Version int
|
||||||
|
ALPN []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewShadowTLS(ctx context.Context, conn net.Conn, option *ShadowTLSOption) (net.Conn, error) {
|
func NewShadowTLS(ctx context.Context, conn net.Conn, option *ShadowTLSOption) (net.Conn, error) {
|
||||||
tlsConfig := &tls.Config{
|
tlsConfig := &tls.Config{
|
||||||
NextProtos: DefaultALPN,
|
NextProtos: option.ALPN,
|
||||||
MinVersion: tls.VersionTLS12,
|
MinVersion: tls.VersionTLS12,
|
||||||
InsecureSkipVerify: option.SkipCertVerify,
|
InsecureSkipVerify: option.SkipCertVerify,
|
||||||
ServerName: option.Host,
|
ServerName: option.Host,
|
||||||
|
Loading…
Reference in New Issue
Block a user