3636)
3737
3838var (
39- refMap = make (map [string ]* List )
39+ refMap = make (map [string ]* List )
40+ plMap = make (map [string ]* ParsedList )
41+ finalMap = make (map [string ]* List )
42+ cirIncMap = make (map [string ]bool ) // Used for circular inclusion detection
4043)
4144
4245type Entry struct {
@@ -45,18 +48,24 @@ type Entry struct {
4548 Attrs []string
4649}
4750
51+ type Inclusion struct {
52+ Source string
53+ MustAttrs []string
54+ BannedAttrs []string
55+ }
56+
4857type List struct {
4958 Name string
5059 Entry []Entry
5160}
5261
5362type ParsedList struct {
54- Name string
55- Inclusion map [ string ] bool
56- Entry []Entry
63+ Name string
64+ Inclusions [] Inclusion
65+ Entry []Entry
5766}
5867
59- func (l * ParsedList ) toPlainText () error {
68+ func (l * List ) toPlainText () error {
6069 var entryBytes []byte
6170 for _ , entry := range l .Entry {
6271 var attrString string
@@ -72,7 +81,7 @@ func (l *ParsedList) toPlainText() error {
7281 return nil
7382}
7483
75- func (l * ParsedList ) toProto () (* router.GeoSite , error ) {
84+ func (l * List ) toProto () (* router.GeoSite , error ) {
7685 site := & router.GeoSite {
7786 CountryCode : l .Name ,
7887 }
@@ -101,7 +110,7 @@ func (l *ParsedList) toProto() (*router.GeoSite, error) {
101110 return site , nil
102111}
103112
104- func exportPlainTextList (exportFiles []string , entryList * ParsedList ) {
113+ func exportPlainTextList (exportFiles []string , entryList * List ) {
105114 for _ , exportfilename := range exportFiles {
106115 if strings .EqualFold (entryList .Name , exportfilename ) {
107116 if err := entryList .toPlainText (); err != nil {
@@ -196,39 +205,77 @@ func Load(path string) (*List, error) {
196205}
197206
198207func ParseList (refList * List ) (* ParsedList , error ) {
199- pl := & ParsedList {
200- Name : refList .Name ,
201- Inclusion : make (map [string ]bool ),
202- }
203- entryList := refList .Entry
204- for {
205- newEntryList := make ([]Entry , 0 , len (entryList ))
206- hasInclude := false
207- for _ , entry := range entryList {
208- if entry .Type == RuleTypeInclude {
209- refName := strings .ToUpper (entry .Value )
210- if pl .Inclusion [refName ] {
211- continue
208+ //TODO: one Entry -> multiple ParsedLists
209+ pl := & ParsedList {Name : refList .Name }
210+ for _ , entry := range refList .Entry {
211+ if entry .Type == RuleTypeInclude {
212+ inc := Inclusion {Source : strings .ToUpper (entry .Value )}
213+ for _ , attr := range entry .Attrs {
214+ if strings .HasPrefix (attr , "-" ) {
215+ inc .BannedAttrs = append (inc .BannedAttrs , attr [1 :]) // Trim attribute prefix `-` character
216+ } else {
217+ inc .MustAttrs = append (inc .MustAttrs , attr )
212218 }
213- pl .Inclusion [refName ] = true
214- refList := refMap [refName ]
215- if refList == nil {
216- return nil , fmt .Errorf ("list not found: %s" , entry .Value )
217- }
218- newEntryList = append (newEntryList , refList .Entry ... )
219- hasInclude = true
220- } else {
221- newEntryList = append (newEntryList , entry )
222219 }
220+ pl .Inclusions = append (pl .Inclusions , inc )
221+ } else {
222+ pl .Entry = append (pl .Entry , entry )
223223 }
224- entryList = newEntryList
225- if ! hasInclude {
226- break
224+ }
225+ return pl , nil
226+ }
227+
228+ func isMatchAttrFilters (entry Entry , incFilter Inclusion ) bool {
229+ attrMap := make (map [string ]bool )
230+ for _ , attr := range entry .Attrs {
231+ attrMap [attr ] = true
232+ }
233+ for _ , m := range incFilter .MustAttrs {
234+ if ! attrMap [m ] { return false }
235+ }
236+ for _ , b := range incFilter .BannedAttrs {
237+ if attrMap [b ] { return false }
238+ }
239+ return true
240+ }
241+
242+ func ResolveList (pl * ParsedList ) error {
243+ if _ , pldone := finalMap [pl .Name ]; pldone { return nil }
244+
245+ if cirIncMap [pl .Name ] {
246+ return fmt .Errorf ("circular inclusion in: %s" , pl .Name )
247+ }
248+ cirIncMap [pl .Name ] = true
249+ defer delete (cirIncMap , pl .Name )
250+
251+ entry2String := func (e Entry ) string { // Attributes already sorted
252+ return e .Type + ":" + e .Value + "@" + strings .Join (e .Attrs , "@" )
253+ }
254+ bscDupMap := make (map [string ]bool ) // Used for basic duplicates detection
255+ finalList := & List {Name : pl .Name }
256+
257+ for _ , dentry := range pl .Entry {
258+ if dstring := entry2String (dentry ); ! bscDupMap [dstring ] {
259+ bscDupMap [dstring ] = true
260+ finalList .Entry = append (finalList .Entry , dentry )
227261 }
228262 }
229- pl .Entry = entryList
230263
231- return pl , nil
264+ for _ , inc := range pl .Inclusions {
265+ if err := ResolveList (plMap [inc .Source ]); err != nil {
266+ return err
267+ }
268+ for _ , ientry := range finalMap [inc .Source ].Entry {
269+ if isMatchAttrFilters (ientry , inc ) {
270+ if istring := entry2String (ientry ); ! bscDupMap [istring ] {
271+ bscDupMap [istring ] = true
272+ finalList .Entry = append (finalList .Entry , ientry )
273+ }
274+ }
275+ }
276+ }
277+ finalMap [pl .Name ] = finalList
278+ return nil
232279}
233280
234281func main () {
@@ -237,6 +284,7 @@ func main() {
237284 dir := * dataPath
238285 fmt .Println ("Use domain lists in" , dir )
239286
287+ // Generate refMap
240288 err := filepath .Walk (dir , func (path string , info os.FileInfo , err error ) error {
241289 if err != nil {
242290 return err
@@ -256,6 +304,24 @@ func main() {
256304 os .Exit (1 )
257305 }
258306
307+ // Generate plMap
308+ for refName , refList := range refMap {
309+ pl , err := ParseList (refList )
310+ if err != nil {
311+ fmt .Println ("Failed to ParseList:" , err )
312+ os .Exit (1 )
313+ }
314+ plMap [refName ] = pl
315+ }
316+
317+ // Generate finalMap
318+ for _ , pl := range plMap {
319+ if err := ResolveList (pl ); err != nil {
320+ fmt .Println ("Failed to ResolveList:" , err )
321+ os .Exit (1 )
322+ }
323+ }
324+
259325 // Create output directory if not exist
260326 if _ , err := os .Stat (* outputDir ); os .IsNotExist (err ) {
261327 if mkErr := os .MkdirAll (* outputDir , 0755 ); mkErr != nil {
@@ -266,13 +332,8 @@ func main() {
266332
267333 protoList := new (router.GeoSiteList )
268334 var existList []string
269- for _ , refList := range refMap {
270- pl , err := ParseList (refList )
271- if err != nil {
272- fmt .Println ("Failed:" , err )
273- os .Exit (1 )
274- }
275- site , err := pl .toProto ()
335+ for _ , siteEntries := range finalMap {
336+ site , err := siteEntries .toProto ()
276337 if err != nil {
277338 fmt .Println ("Failed:" , err )
278339 os .Exit (1 )
@@ -282,7 +343,7 @@ func main() {
282343 // Flatten and export plaintext list
283344 if * exportLists != "" {
284345 if existList != nil {
285- exportPlainTextList (existList , pl )
346+ exportPlainTextList (existList , siteEntries )
286347 } else {
287348 exportedListSlice := strings .Split (* exportLists , "," )
288349 for _ , exportedListName := range exportedListSlice {
@@ -295,7 +356,7 @@ func main() {
295356 }
296357 }
297358 if existList != nil {
298- exportPlainTextList (existList , pl )
359+ exportPlainTextList (existList , siteEntries )
299360 }
300361 }
301362 }
@@ -308,11 +369,11 @@ func main() {
308369
309370 protoBytes , err := proto .Marshal (protoList )
310371 if err != nil {
311- fmt .Println ("Failed:" , err )
372+ fmt .Println ("Failed to marshal :" , err )
312373 os .Exit (1 )
313374 }
314375 if err := os .WriteFile (filepath .Join (* outputDir , * outputName ), protoBytes , 0644 ); err != nil {
315- fmt .Println ("Failed:" , err )
376+ fmt .Println ("Failed to write output :" , err )
316377 os .Exit (1 )
317378 } else {
318379 fmt .Println (* outputName , "has been generated successfully." )
0 commit comments