@@ -9,10 +9,14 @@ import (
99 "fmt"
1010 "hash/crc32"
1111 "math/rand"
12+ "net"
13+ "strconv"
1214 "strings"
15+ "sync"
1316 "sync/atomic"
1417 "time"
1518
19+ "github.com/dlclark/regexp2"
1620 "github.com/tidwall/jsonc"
1721)
1822
@@ -80,11 +84,11 @@ type BTN_BanInfo struct {
8084}
8185
8286type BTN_RulesStruct struct {
83- Version string `json:"version"`
84- PeerID map [string ][]RuleEntry `json:"peer_id"`
85- ClientName map [string ][]RuleEntry `json:"client_name"`
86- IP map [string ][]string `json:"ip"`
87- Port map [string ][]string `json:"port"`
87+ Version string `json:"version"`
88+ PeerID map [string ][]string `json:"peer_id"`
89+ ClientName map [string ][]string `json:"client_name"`
90+ IP map [string ][]string `json:"ip"`
91+ Port map [string ][]string `json:"port"`
8892}
8993
9094type RuleEntry struct {
@@ -93,11 +97,11 @@ type RuleEntry struct {
9397}
9498
9599type BTN_ExceptionStruct struct {
96- Version string `json:"version"`
97- PeerID map [string ][]RuleEntry `json:"peer_id"`
98- ClientName map [string ][]RuleEntry `json:"client_name"`
99- IP map [string ][]string `json:"ip"`
100- Port map [string ][]string `json:"port"`
100+ Version string `json:"version"`
101+ PeerID map [string ][]string `json:"peer_id"`
102+ ClientName map [string ][]string `json:"client_name"`
103+ IP map [string ][]string `json:"ip"`
104+ Port map [string ][]string `json:"port"`
101105}
102106
103107var btnProtocol = "BTN-Protocol/3.0.0"
@@ -131,6 +135,124 @@ var btn_isTaskRunning atomic.Bool
131135
132136var btnRules BTN_RulesStruct
133137var btnExceptions BTN_ExceptionStruct
138+ var btn_regexCache sync.Map // map[string]*regexp2.Regexp
139+
140+ func BTN_MatchEntry (value string , ruleRaw string ) bool {
141+ var rule RuleEntry
142+ if err := json .Unmarshal ([]byte (ruleRaw ), & rule ); err != nil {
143+ // 某些规则可能直接是字符串内容 (Legacy).
144+ return strings .Contains (value , ruleRaw )
145+ }
146+
147+ switch strings .ToUpper (rule .Method ) {
148+ case "EQUALS" :
149+ return value == rule .Content
150+ case "STARTS_WITH" :
151+ return strings .HasPrefix (value , rule .Content )
152+ case "ENDS_WITH" :
153+ return strings .HasSuffix (value , rule .Content )
154+ case "CONTAINS" :
155+ return strings .Contains (value , rule .Content )
156+ case "REGEX" :
157+ var re * regexp2.Regexp
158+ if val , ok := btn_regexCache .Load (rule .Content ); ok {
159+ re = val .(* regexp2.Regexp )
160+ } else {
161+ var err error
162+ re , err = regexp2 .Compile (rule .Content , regexp2 .IgnoreCase )
163+ if err != nil {
164+ Log ("BTN_MatchEntry" , "Invalid regex: %s" , true , rule .Content )
165+ return false
166+ }
167+ btn_regexCache .Store (rule .Content , re )
168+ }
169+ match , _ := re .MatchString (value )
170+ return match
171+ }
172+ return false
173+ }
174+
175+ func BTN_CheckPeer (peerIP , peerID , peerClient string , peerPort int ) (bool , int , string ) {
176+ if btnConfig == nil {
177+ return false , 0 , ""
178+ }
179+
180+ ipObj := net .ParseIP (peerIP )
181+ peerPortStr := strconv .Itoa (peerPort )
182+
183+ // 1. 检查例外规则 (WhiteList).
184+ for _ , rules := range btnExceptions .IP {
185+ for _ , rule := range rules {
186+ _ , subnet , err := net .ParseCIDR (rule )
187+ if err == nil {
188+ if subnet .Contains (ipObj ) {
189+ return false , 0 , ""
190+ }
191+ } else if rule == peerIP {
192+ return false , 0 , ""
193+ }
194+ }
195+ }
196+ for _ , rules := range btnExceptions .Port {
197+ for _ , rule := range rules {
198+ if rule == peerPortStr || rule == "ALL" {
199+ return false , 0 , ""
200+ }
201+ }
202+ }
203+ for _ , rules := range btnExceptions .PeerID {
204+ for _ , rule := range rules {
205+ if BTN_MatchEntry (peerID , rule ) {
206+ return false , 0 , ""
207+ }
208+ }
209+ }
210+ for _ , rules := range btnExceptions .ClientName {
211+ for _ , rule := range rules {
212+ if BTN_MatchEntry (peerClient , rule ) {
213+ return false , 0 , ""
214+ }
215+ }
216+ }
217+
218+ // 2. 检查封禁规则 (BlockList).
219+ // 处理顺序: IP -> Port -> PeerID -> ClientName.
220+ for reason , rules := range btnRules .IP {
221+ for _ , rule := range rules {
222+ _ , subnet , err := net .ParseCIDR (rule )
223+ if err == nil {
224+ if subnet .Contains (ipObj ) {
225+ return true , - 1 , "Bad-IP_FromBTN (" + reason + ")"
226+ }
227+ } else if rule == peerIP {
228+ return true , - 1 , "Bad-IP_FromBTN (" + reason + ")"
229+ }
230+ }
231+ }
232+ for reason , rules := range btnRules .Port {
233+ for _ , rule := range rules {
234+ if rule == peerPortStr || rule == "ALL" {
235+ return true , peerPort , "Bad-IP_FromBTN (" + reason + ")"
236+ }
237+ }
238+ }
239+ for reason , rules := range btnRules .PeerID {
240+ for _ , rule := range rules {
241+ if BTN_MatchEntry (peerID , rule ) {
242+ return true , peerPort , "Bad-IP_FromBTN (" + reason + ")"
243+ }
244+ }
245+ }
246+ for reason , rules := range btnRules .ClientName {
247+ for _ , rule := range rules {
248+ if BTN_MatchEntry (peerClient , rule ) {
249+ return true , peerPort , "Bad-IP_FromBTN (" + reason + ")"
250+ }
251+ }
252+ }
253+
254+ return false , 0 , ""
255+ }
134256
135257func BTN_GetConfig () {
136258 if config .BTNConfigureURL == "" || (atomic .LoadInt64 (& btn_lastGetConfig )+ int64 (btn_configureInterval )) > atomic .LoadInt64 (& currentTimestamp ) {
0 commit comments