feat: add fingerprint param

This commit is contained in:
Skyxim 2022-07-11 13:42:28 +08:00
parent ab8e9e7d7a
commit a8ce283727
16 changed files with 193 additions and 30 deletions

View file

@ -36,6 +36,7 @@ type HttpOption struct {
TLS bool `proxy:"tls,omitempty"`
SNI string `proxy:"sni,omitempty"`
SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"`
Fingerprint string `proxy:"fingerprint,omitempty"`
Headers map[string]string `proxy:"headers,omitempty"`
}
@ -127,16 +128,26 @@ func (h *Http) shakeHand(metadata *C.Metadata, rw io.ReadWriter) error {
return fmt.Errorf("can not connect remote err code: %d", resp.StatusCode)
}
func NewHttp(option HttpOption) *Http {
func NewHttp(option HttpOption) (*Http, error) {
var tlsConfig *tls.Config
if option.TLS {
sni := option.Server
if option.SNI != "" {
sni = option.SNI
}
tlsConfig = &tls.Config{
InsecureSkipVerify: option.SkipCertVerify,
ServerName: sni,
if len(option.Fingerprint) == 0 {
tlsConfig = tlsC.GetGlobalFingerprintTLCConfig(&tls.Config{
InsecureSkipVerify: option.SkipCertVerify,
ServerName: sni,
})
} else {
var err error
if tlsConfig, err = tlsC.GetSpecifiedFingerprintTLSConfig(&tls.Config{
InsecureSkipVerify: option.SkipCertVerify,
ServerName: sni,
}, option.Fingerprint); err != nil {
return nil, err
}
}
}
@ -150,7 +161,7 @@ func NewHttp(option HttpOption) *Http {
},
user: option.UserName,
pass: option.Password,
tlsConfig: tlsC.MixinTLSConfig(tlsConfig),
tlsConfig: tlsConfig,
option: &option,
}
}, nil
}

View file

@ -87,6 +87,7 @@ type HysteriaOption struct {
Obfs string `proxy:"obfs,omitempty"`
SNI string `proxy:"sni,omitempty"`
SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"`
Fingerprint string `proxy:"fingerprint,omitempty"`
ALPN string `proxy:"alpn,omitempty"`
CustomCA string `proxy:"ca,omitempty"`
CustomCAString string `proxy:"ca_str,omitempty"`
@ -122,11 +123,22 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) {
if option.SNI != "" {
serverName = option.SNI
}
tlsConfig := tlsC.MixinTLSConfig(&tls.Config{
tlsConfig := &tls.Config{
ServerName: serverName,
InsecureSkipVerify: option.SkipCertVerify,
MinVersion: tls.VersionTLS13,
})
}
if len(option.Fingerprint) != 0 {
var err error
tlsConfig, err = tlsC.GetSpecifiedFingerprintTLSConfig(tlsConfig, option.Fingerprint)
if err != nil {
return nil, err
}
} else {
tlsConfig = tlsC.GetGlobalFingerprintTLCConfig(tlsConfig)
}
if len(option.ALPN) > 0 {
tlsConfig.NextProtos = []string{option.ALPN}
} else {

View file

@ -60,6 +60,7 @@ type v2rayObfsOption struct {
Host string `obfs:"host,omitempty"`
Path string `obfs:"path,omitempty"`
TLS bool `obfs:"tls,omitempty"`
Fingerprint string `obfs:"fingerprint,omitempty"`
Headers map[string]string `obfs:"headers,omitempty"`
SkipCertVerify bool `obfs:"skip-cert-verify,omitempty"`
Mux bool `obfs:"mux,omitempty"`

View file

@ -34,6 +34,7 @@ type Socks5Option struct {
TLS bool `proxy:"tls,omitempty"`
UDP bool `proxy:"udp,omitempty"`
SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"`
Fingerprint string `proxy:"fingerprint,omitempty"`
}
// StreamConn implements C.ProxyAdapter
@ -139,13 +140,22 @@ func (ss *Socks5) ListenPacketContext(ctx context.Context, metadata *C.Metadata,
return newPacketConn(&socksPacketConn{PacketConn: pc, rAddr: bindUDPAddr, tcpConn: c}, ss), nil
}
func NewSocks5(option Socks5Option) *Socks5 {
func NewSocks5(option Socks5Option) (*Socks5, error) {
var tlsConfig *tls.Config
if option.TLS {
tlsConfig = &tls.Config{
InsecureSkipVerify: option.SkipCertVerify,
ServerName: option.Server,
}
if len(option.Fingerprint) == 0 {
tlsConfig = tlsC.GetGlobalFingerprintTLCConfig(tlsConfig)
} else {
var err error
if tlsConfig, err = tlsC.GetSpecifiedFingerprintTLSConfig(tlsConfig, option.Fingerprint); err != nil {
return nil, err
}
}
}
return &Socks5{
@ -161,8 +171,8 @@ func NewSocks5(option Socks5Option) *Socks5 {
pass: option.Password,
tls: option.TLS,
skipCertVerify: option.SkipCertVerify,
tlsConfig: tlsC.MixinTLSConfig(tlsConfig),
}
tlsConfig: tlsConfig,
}, nil
}
type socksPacketConn struct {

View file

@ -36,6 +36,7 @@ type TrojanOption struct {
ALPN []string `proxy:"alpn,omitempty"`
SNI string `proxy:"sni,omitempty"`
SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"`
Fingerprint string `proxy:"fingerprint,omitempty"`
UDP bool `proxy:"udp,omitempty"`
Network string `proxy:"network,omitempty"`
GrpcOpts GrpcOptions `proxy:"grpc-opts,omitempty"`
@ -189,6 +190,7 @@ func NewTrojan(option TrojanOption) (*Trojan, error) {
ServerName: option.Server,
SkipCertVerify: option.SkipCertVerify,
FlowShow: option.FlowShow,
Fingerprint: option.Fingerprint,
}
if option.Network != "ws" && len(option.Flow) >= 16 {
@ -228,12 +230,21 @@ func NewTrojan(option TrojanOption) (*Trojan, error) {
return c, nil
}
tlsConfig := tlsC.MixinTLSConfig(&tls.Config{
tlsConfig := &tls.Config{
NextProtos: option.ALPN,
MinVersion: tls.VersionTLS12,
InsecureSkipVerify: tOption.SkipCertVerify,
ServerName: tOption.ServerName,
})
}
if len(option.Fingerprint) == 0 {
tlsConfig = tlsC.GetGlobalFingerprintTLCConfig(tlsConfig)
} else {
var err error
if tlsConfig, err = tlsC.GetSpecifiedFingerprintTLSConfig(tlsConfig, option.Fingerprint); err != nil {
return nil, err
}
}
if t.option.Flow != "" {
t.transport = gun.NewHTTP2XTLSClient(dialFn, tlsConfig)

View file

@ -56,6 +56,7 @@ type VlessOption struct {
WSPath string `proxy:"ws-path,omitempty"`
WSHeaders map[string]string `proxy:"ws-headers,omitempty"`
SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"`
Fingerprint string `proxy:"fingerprint,omitempty"`
ServerName string `proxy:"servername,omitempty"`
}
@ -81,12 +82,19 @@ func (v *Vless) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
}
if v.option.TLS {
wsOpts.TLS = true
wsOpts.TLSConfig = tlsC.MixinTLSConfig(&tls.Config{
tlsConfig := &tls.Config{
MinVersion: tls.VersionTLS12,
ServerName: host,
InsecureSkipVerify: v.option.SkipCertVerify,
NextProtos: []string{"http/1.1"},
})
}
if len(v.option.Fingerprint) == 0 {
wsOpts.TLSConfig = tlsC.GetGlobalFingerprintTLCConfig(tlsConfig)
} else {
wsOpts.TLSConfig, err = tlsC.GetSpecifiedFingerprintTLSConfig(tlsConfig, v.option.Fingerprint)
}
if v.option.ServerName != "" {
wsOpts.TLSConfig.ServerName = v.option.ServerName
} else if host := wsOpts.Headers.Get("Host"); host != "" {
@ -153,6 +161,7 @@ func (v *Vless) streamTLSOrXTLSConn(conn net.Conn, isH2 bool) (net.Conn, error)
xtlsOpts := vless.XTLSConfig{
Host: host,
SkipCertVerify: v.option.SkipCertVerify,
FingerPrint: v.option.Fingerprint,
}
if isH2 {
@ -169,6 +178,7 @@ func (v *Vless) streamTLSOrXTLSConn(conn net.Conn, isH2 bool) (net.Conn, error)
tlsOpts := vmess.TLSConfig{
Host: host,
SkipCertVerify: v.option.SkipCertVerify,
FingerPrint: v.option.Fingerprint,
}
if isH2 {
@ -437,7 +447,7 @@ func NewVless(option VlessOption) (*Vless, error) {
ServiceName: v.option.GrpcOpts.GrpcServiceName,
Host: v.option.ServerName,
}
tlsConfig := tlsC.MixinTLSConfig(&tls.Config{
tlsConfig := tlsC.GetGlobalFingerprintTLCConfig(&tls.Config{
InsecureSkipVerify: v.option.SkipCertVerify,
ServerName: v.option.ServerName,
})

View file

@ -6,6 +6,7 @@ import (
"errors"
"fmt"
tlsC "github.com/Dreamacro/clash/component/tls"
vmess "github.com/sagernet/sing-vmess"
"net"
"net/http"
"strconv"
@ -17,7 +18,6 @@ import (
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/transport/gun"
clashVMess "github.com/Dreamacro/clash/transport/vmess"
"github.com/sagernet/sing-vmess"
"github.com/sagernet/sing-vmess/packetaddr"
M "github.com/sagernet/sing/common/metadata"
)
@ -45,6 +45,7 @@ type VmessOption struct {
Network string `proxy:"network,omitempty"`
TLS bool `proxy:"tls,omitempty"`
SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"`
Fingerprint string `proxy:"fingerprint,omitempty"`
ServerName string `proxy:"servername,omitempty"`
HTTPOpts HTTPOptions `proxy:"http-opts,omitempty"`
HTTP2Opts HTTP2Options `proxy:"h2-opts,omitempty"`
@ -100,11 +101,21 @@ func (v *Vmess) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
if v.option.TLS {
wsOpts.TLS = true
wsOpts.TLSConfig = tlsC.MixinTLSConfig(&tls.Config{
tlsConfig := &tls.Config{
ServerName: host,
InsecureSkipVerify: v.option.SkipCertVerify,
NextProtos: []string{"http/1.1"},
})
}
if len(v.option.Fingerprint) == 0 {
wsOpts.TLSConfig = tlsC.GetGlobalFingerprintTLCConfig(tlsConfig)
} else {
var err error
if wsOpts.TLSConfig, err = tlsC.GetSpecifiedFingerprintTLSConfig(tlsConfig, v.option.Fingerprint); err != nil {
return nil, err
}
}
if v.option.ServerName != "" {
wsOpts.TLSConfig.ServerName = v.option.ServerName
} else if host := wsOpts.Headers.Get("Host"); host != "" {

View file

@ -40,14 +40,14 @@ func ParseProxy(mapping map[string]any) (C.Proxy, error) {
if err != nil {
break
}
proxy = outbound.NewSocks5(*socksOption)
proxy, err = outbound.NewSocks5(*socksOption)
case "http":
httpOption := &outbound.HttpOption{}
err = decoder.Decode(mapping, httpOption)
if err != nil {
break
}
proxy = outbound.NewHttp(*httpOption)
proxy, err = outbound.NewHttp(*httpOption)
case "vmess":
vmessOption := &outbound.VmessOption{
HTTPOpts: outbound.HTTPOptions{

View file

@ -7,6 +7,7 @@ import (
"crypto/x509"
"encoding/hex"
"fmt"
xtls "github.com/xtls/go"
"strings"
"sync"
"time"
@ -75,11 +76,11 @@ func convertFingerprint(fingerprint string) (*[32]byte, error) {
}
func GetDefaultTLSConfig() *tls.Config {
return MixinTLSConfig(nil)
return GetGlobalFingerprintTLCConfig(nil)
}
// GetTLSConfigWithSpecifiedFingerprint specified fingerprint
func GetTLSConfigWithSpecifiedFingerprint(tlsConfig *tls.Config, fingerprint string) (*tls.Config, error) {
// GetSpecifiedFingerprintTLSConfig specified fingerprint
func GetSpecifiedFingerprintTLSConfig(tlsConfig *tls.Config, fingerprint string) (*tls.Config, error) {
if fingerprintBytes, err := convertFingerprint(fingerprint); err != nil {
return nil, err
} else {
@ -96,7 +97,7 @@ func GetTLSConfigWithSpecifiedFingerprint(tlsConfig *tls.Config, fingerprint str
}
}
func MixinTLSConfig(tlsConfig *tls.Config) *tls.Config {
func GetGlobalFingerprintTLCConfig(tlsConfig *tls.Config) *tls.Config {
if tlsConfig == nil {
return &tls.Config{
InsecureSkipVerify: true,
@ -108,3 +109,34 @@ func MixinTLSConfig(tlsConfig *tls.Config) *tls.Config {
tlsConfig.InsecureSkipVerify = true
return tlsConfig
}
// GetSpecifiedFingerprintXTLSConfig specified fingerprint
func GetSpecifiedFingerprintXTLSConfig(tlsConfig *xtls.Config, fingerprint string) (*xtls.Config, error) {
if fingerprintBytes, err := convertFingerprint(fingerprint); err != nil {
return nil, err
} else {
if tlsConfig == nil {
return &xtls.Config{
InsecureSkipVerify: true,
VerifyPeerCertificate: verifyPeerCertificateAndFingerprints([][32]byte{*fingerprintBytes}, false),
}, nil
} else {
tlsConfig.VerifyPeerCertificate = verifyPeerCertificateAndFingerprints([][32]byte{*fingerprintBytes}, tlsConfig.InsecureSkipVerify)
tlsConfig.InsecureSkipVerify = true
return tlsConfig, nil
}
}
}
func GetGlobalFingerprintXTLCConfig(tlsConfig *xtls.Config) *xtls.Config {
if tlsConfig == nil {
return &xtls.Config{
InsecureSkipVerify: true,
VerifyPeerCertificate: verifyPeerCertificateAndFingerprints(globalFingerprints, false),
}
}
tlsConfig.VerifyPeerCertificate = verifyPeerCertificateAndFingerprints(globalFingerprints, tlsConfig.InsecureSkipVerify)
tlsConfig.InsecureSkipVerify = true
return tlsConfig
}

View file

@ -78,7 +78,7 @@ func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error)
ch := make(chan result, 1)
go func() {
if strings.HasSuffix(c.Client.Net, "tls") {
conn = tls.Client(conn, tlsC.MixinTLSConfig(c.Client.TLSConfig))
conn = tls.Client(conn, tlsC.GetGlobalFingerprintTLCConfig(c.Client.TLSConfig))
}
msg, _, err := c.Client.ExchangeWithConn(m, &D.Conn{

View file

@ -129,7 +129,7 @@ func (dc *quicClient) getSession(ctx context.Context) (quic.Connection, error) {
}
func (dc *quicClient) openSession(ctx context.Context) (quic.Connection, error) {
tlsConfig := tlsC.MixinTLSConfig(
tlsConfig := tlsC.GetGlobalFingerprintTLCConfig(
&tls.Config{
InsecureSkipVerify: false,
NextProtos: []string{

View file

@ -178,6 +178,7 @@ proxies:
plugin-opts:
mode: websocket # no QUIC now
# tls: true # wss
# fingerprint: xxxx
# skip-cert-verify: true
# host: bing.com
# path: "/"
@ -196,6 +197,7 @@ proxies:
cipher: auto
# udp: true
# tls: true
# fingerprint: xxxx
# skip-cert-verify: true
# servername: example.com # priority over wss host
# network: ws
@ -215,6 +217,7 @@ proxies:
cipher: auto
network: h2
tls: true
# fingerprint: xxxx
h2-opts:
host:
- http.example.com
@ -248,6 +251,7 @@ proxies:
cipher: auto
network: grpc
tls: true
# fingerprint: xxxx
servername: example.com
# skip-cert-verify: true
grpc-opts:
@ -261,6 +265,7 @@ proxies:
# username: username
# password: password
# tls: true
# fingerprint: xxxx
# skip-cert-verify: true
# udp: true
@ -274,6 +279,7 @@ proxies:
# tls: true # https
# skip-cert-verify: true
# sni: custom.com
# fingerprint: xxxx
# Snell
# Beware that there's currently no UDP support yet
@ -293,6 +299,7 @@ proxies:
server: server
port: 443
password: yourpsk
# fingerprint: xxxx
# udp: true
# sni: example.com # aka server name
# alpn:
@ -308,6 +315,7 @@ proxies:
network: grpc
sni: example.com
# skip-cert-verify: true
# fingerprint: xxxx
udp: true
grpc-opts:
grpc-service-name: "example"
@ -320,6 +328,7 @@ proxies:
network: ws
sni: example.com
# skip-cert-verify: true
# fingerprint: xxxx
udp: true
# ws-opts:
# path: /path
@ -336,6 +345,7 @@ proxies:
# udp: true
# sni: example.com # aka server name
# skip-cert-verify: true
# fingerprint: xxxx
# vless
- name: "vless-tcp"
@ -347,6 +357,7 @@ proxies:
servername: example.com # AKA SNI
# flow: xtls-rprx-direct # xtls-rprx-origin # enable XTLS
# skip-cert-verify: true
# fingerprint: xxxx
- name: "vless-ws"
type: vless
@ -358,6 +369,7 @@ proxies:
network: ws
servername: example.com # priority over wss host
# skip-cert-verify: true
# fingerprint: xxxx
ws-opts:
path: "/"
headers:
@ -379,6 +391,7 @@ proxies:
#ca: "./my.ca"
#ca_str: "xyz"
#disable_mtu_discovery: false
# fingerprint: xxxx
# ShadowsocksR
# The supported ciphers (encryption methods): all stream ciphers in ss

View file

@ -8,6 +8,7 @@ import (
"encoding/hex"
"errors"
"fmt"
tlsC "github.com/Dreamacro/clash/component/tls"
"io"
"net"
"net/http"
@ -50,6 +51,7 @@ type Option struct {
ALPN []string
ServerName string
SkipCertVerify bool
Fingerprint string
Flow string
FlowShow bool
}
@ -80,6 +82,15 @@ func (t *Trojan) StreamConn(conn net.Conn) (net.Conn, error) {
ServerName: t.option.ServerName,
}
if len(t.option.Fingerprint) == 0 {
xtlsConfig = tlsC.GetGlobalFingerprintXTLCConfig(xtlsConfig)
} else {
var err error
if xtlsConfig, err = tlsC.GetSpecifiedFingerprintXTLSConfig(xtlsConfig, t.option.Fingerprint); err != nil {
return nil, err
}
}
xtlsConn := xtls.Client(conn, xtlsConfig)
ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout)
@ -95,6 +106,16 @@ func (t *Trojan) StreamConn(conn net.Conn) (net.Conn, error) {
InsecureSkipVerify: t.option.SkipCertVerify,
ServerName: t.option.ServerName,
}
if len(t.option.Fingerprint) == 0 {
tlsConfig = tlsC.GetGlobalFingerprintTLCConfig(tlsConfig)
} else {
var err error
if tlsConfig, err = tlsC.GetSpecifiedFingerprintTLSConfig(tlsConfig, t.option.Fingerprint); err != nil {
return nil, err
}
}
tlsConn := tls.Client(conn, tlsConfig)
if err := tlsConn.Handshake(); err != nil {
return nil, err

View file

@ -2,6 +2,7 @@ package obfs
import (
"crypto/tls"
tlsC "github.com/Dreamacro/clash/component/tls"
"net"
"net/http"
@ -16,6 +17,7 @@ type Option struct {
Headers map[string]string
TLS bool
SkipCertVerify bool
Fingerprint string
Mux bool
}
@ -35,11 +37,20 @@ func NewV2rayObfs(conn net.Conn, option *Option) (net.Conn, error) {
if option.TLS {
config.TLS = true
config.TLSConfig = &tls.Config{
tlsConfig := &tls.Config{
ServerName: option.Host,
InsecureSkipVerify: option.SkipCertVerify,
NextProtos: []string{"http/1.1"},
}
if len(option.Fingerprint) == 0 {
config.TLSConfig = tlsC.GetGlobalFingerprintTLCConfig(tlsConfig)
} else {
var err error
if config.TLSConfig, err = tlsC.GetSpecifiedFingerprintTLSConfig(tlsConfig, option.Fingerprint); err != nil {
return nil, err
}
}
if host := config.Headers.Get("Host"); host != "" {
config.TLSConfig.ServerName = host
}

View file

@ -2,6 +2,7 @@ package vless
import (
"context"
tlsC "github.com/Dreamacro/clash/component/tls"
"net"
C "github.com/Dreamacro/clash/constant"
@ -11,6 +12,7 @@ import (
type XTLSConfig struct {
Host string
SkipCertVerify bool
FingerPrint string
NextProtos []string
}
@ -20,6 +22,14 @@ func StreamXTLSConn(conn net.Conn, cfg *XTLSConfig) (net.Conn, error) {
InsecureSkipVerify: cfg.SkipCertVerify,
NextProtos: cfg.NextProtos,
}
if len(cfg.FingerPrint) == 0 {
xtlsConfig = tlsC.GetGlobalFingerprintXTLCConfig(xtlsConfig)
} else {
var err error
if xtlsConfig, err = tlsC.GetSpecifiedFingerprintXTLSConfig(xtlsConfig, cfg.FingerPrint); err != nil {
return nil, err
}
}
xtlsConn := xtls.Client(conn, xtlsConfig)

View file

@ -12,15 +12,25 @@ import (
type TLSConfig struct {
Host string
SkipCertVerify bool
FingerPrint string
NextProtos []string
}
func StreamTLSConn(conn net.Conn, cfg *TLSConfig) (net.Conn, error) {
tlsConfig := tlsC.MixinTLSConfig(&tls.Config{
tlsConfig := &tls.Config{
ServerName: cfg.Host,
InsecureSkipVerify: cfg.SkipCertVerify,
NextProtos: cfg.NextProtos,
})
}
if len(cfg.FingerPrint) == 0 {
tlsConfig = tlsC.GetGlobalFingerprintTLCConfig(tlsConfig)
} else {
var err error
if tlsConfig, err = tlsC.GetSpecifiedFingerprintTLSConfig(tlsConfig, cfg.FingerPrint); err != nil {
return nil, err
}
}
tlsConn := tls.Client(conn, tlsConfig)