From 3cbaca666ec471f8e366c22bddf064459f8ccc7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Mon, 2 Jan 2023 14:09:40 +0800 Subject: [PATCH] Add JA3 fingerprint rule item --- option/dns.go | 1 + option/route.go | 1 + route/rule.go | 5 ++++ route/rule_dns.go | 5 ++++ route/rule_ja3_fingerprint.go | 43 +++++++++++++++++++++++++++++++++++ 5 files changed, 55 insertions(+) create mode 100644 route/rule_ja3_fingerprint.go diff --git a/option/dns.go b/option/dns.go index b232baea..1a8a7af5 100644 --- a/option/dns.go +++ b/option/dns.go @@ -98,6 +98,7 @@ type DefaultDNSRule struct { PackageName Listable[string] `json:"package_name,omitempty"` User Listable[string] `json:"user,omitempty"` UserID Listable[int32] `json:"user_id,omitempty"` + JA3Fingerprint Listable[string] `json:"ja3_fingerprint,omitempty"` Outbound Listable[string] `json:"outbound,omitempty"` ClashMode string `json:"clash_mode,omitempty"` Invert bool `json:"invert,omitempty"` diff --git a/option/route.go b/option/route.go index 308c4802..f37ecfcc 100644 --- a/option/route.go +++ b/option/route.go @@ -101,6 +101,7 @@ type DefaultRule struct { PackageName Listable[string] `json:"package_name,omitempty"` User Listable[string] `json:"user,omitempty"` UserID Listable[int32] `json:"user_id,omitempty"` + JA3Fingerprint Listable[string] `json:"ja3_fingerprint,omitempty"` ClashMode string `json:"clash_mode,omitempty"` Invert bool `json:"invert,omitempty"` Outbound string `json:"outbound,omitempty"` diff --git a/route/rule.go b/route/rule.go index 6f2f3baa..0acff56b 100644 --- a/route/rule.go +++ b/route/rule.go @@ -194,6 +194,11 @@ func NewDefaultRule(router adapter.Router, logger log.ContextLogger, options opt rule.items = append(rule.items, item) rule.allItems = append(rule.allItems, item) } + if len(options.JA3Fingerprint) > 0 { + item := NewJA3FingerprintItem(options.JA3Fingerprint) + 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) diff --git a/route/rule_dns.go b/route/rule_dns.go index 0f072750..550352fa 100644 --- a/route/rule_dns.go +++ b/route/rule_dns.go @@ -178,6 +178,11 @@ func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options rule.items = append(rule.items, item) rule.allItems = append(rule.allItems, item) } + if len(options.JA3Fingerprint) > 0 { + item := NewJA3FingerprintItem(options.JA3Fingerprint) + rule.items = append(rule.items, item) + rule.allItems = append(rule.allItems, item) + } if len(options.Outbound) > 0 { item := NewOutboundRule(options.Outbound) rule.items = append(rule.items, item) diff --git a/route/rule_ja3_fingerprint.go b/route/rule_ja3_fingerprint.go new file mode 100644 index 00000000..3ac3e763 --- /dev/null +++ b/route/rule_ja3_fingerprint.go @@ -0,0 +1,43 @@ +package route + +import ( + "strings" + + "github.com/sagernet/sing-box/adapter" +) + +var _ RuleItem = (*JA3FingerprintItem)(nil) + +type JA3FingerprintItem struct { + fingerprints []string + fingerprintMap map[string]bool +} + +func NewJA3FingerprintItem(hashList []string) *JA3FingerprintItem { + rule := &JA3FingerprintItem{ + fingerprints: hashList, + fingerprintMap: make(map[string]bool), + } + for _, hash := range hashList { + rule.fingerprintMap[strings.ToLower(hash)] = true + } + return rule +} + +func (r *JA3FingerprintItem) Match(metadata *adapter.InboundContext) bool { + if metadata.JA3Fingerprint == "" { + return false + } + return r.fingerprintMap[metadata.JA3Fingerprint] +} + +func (r *JA3FingerprintItem) String() string { + var description string + pLen := len(r.fingerprints) + if pLen == 1 { + description = "ja3_fingerprint=" + r.fingerprints[0] + } else { + description = "ja3_fingerprint=[" + strings.Join(r.fingerprints, " ") + "]" + } + return description +}