Compare commits

..

33 Commits

Author SHA1 Message Date
sergeevms
c638ec66f0 salesforce: add salesforce-setup.com (#3239) 2026-02-02 23:31:35 +08:00
susaninz
4c8b1438f8 kinopub: add cdn-service.space (#3220)
This domain is used by the Kinopub Android TV app for version checking.
Without it, the app hangs on startup when accessed from regions where
this domain is blocked.

Discovered during network traffic analysis on 2026-01-27.
2026-02-02 23:15:54 +08:00
Emik
3399285ea9 add pjsekai.sega.jp to projectsekai (#3236) 2026-02-01 21:35:51 +08:00
⑨bingyin
62346cf6b7 Add bsappapi.com to Binance (#3235) 2026-02-01 21:30:20 +08:00
jinqiang zhang
8dee321846 qcloud: add edgeone.cool (#3237) 2026-02-01 21:28:10 +08:00
fernvenue
b117cf851f Add packages.microsoft.com to microsoft-dev. (#3234) 2026-02-01 11:58:25 +08:00
jinqiang zhang
0b6606758d add louisvuitton (#3233) 2026-01-31 18:04:21 +08:00
Blackteahamburger
fcf9c67d83 category-education-cn: add zjzs.net (#3232) 2026-01-30 19:20:41 +08:00
MkQtS
56e0b47c73 Clean up ad lists (#3231)
* category-ads-all: include adjust

* category-ads-all: include clearbit

* category-ads-all: include ogury

* category-ads-all: include openx

* category-ads-all: include pubmatic

and remove pubmatic-ads

* category-ads-all: include segment

* category-ads-all: include supersonic

* geolocation-cn: remove the inclusion of umeng

it's included in alibaba

* add unitychina

* remove unity-ads

use unity@ads or unitychina@ads instead
2026-01-30 12:10:37 +08:00
Signaliks
4f45866be4 Update cloudflare (#3229) 2026-01-29 13:34:52 +08:00
sergeevms
40d763daca Update atlassian (#3228)
* Update atlassian

* Supplement and sort

data source: https://support.atlassian.com/organization-administration/docs/ip-addresses-and-domains-for-atlassian-cloud-products/

---------

Co-authored-by: MkQtS <81752398+MkQtS@users.noreply.github.com>
2026-01-29 13:33:48 +08:00
MkQtS
6c91898557 Cleanup ad lists (#3227)
Merge ad lists containing too few rules.

merged/removed lists:

adcolony-ads applovin-ads atom-data-ads emogi-ads flurry-ads
growingio-ads hiido-ads hotjar-ads inner-active-ads mopub-ads
mxplayer-ads newrelic-ads pocoiq-ads tagtic-ads tappx-ads uberads-ads
2026-01-28 17:43:54 +08:00
MkQtS
91da593233 apple: add aod-ssl.itunes.apple.com with cn attr (#3226) 2026-01-28 16:51:48 +08:00
TripleA
9f1c6b6922 Add Bohemia Interactive and Battleye domains (#3223) 2026-01-28 16:41:32 +08:00
MkQtS
b3bae7de8f Update category-ads (#3222)
* remove ads attr from openaicom.imgix.net

imgix.net is serving for pictures, not ads/tracking

* category-ads: include more ad domains
2026-01-28 13:07:34 +08:00
Jinzhe Zeng
4e9b28f951 add crixet.com to openai (#3221)
Crixet has been acquired by OpenAI, per https://crixet.com
2026-01-28 11:49:57 +08:00
xiyao
3c0a538219 samsung: add ospserver.net (#3219)
Samsung OneUI update server
2026-01-27 16:52:46 +08:00
MkQtS
2160230ef9 terabox: add more domains (#3218) 2026-01-27 15:24:47 +08:00
MkQtS
5c38f34456 Add cmd/datdump/main.go (#3213)
* Feat: add a new datdump tool

* Refactor: address code review comments

* Refactor: remove export all from main program

use datdump instead

* Refactor: allow spaces in exportlists

e.g. `--exportlists="lista, listb"`

* all: cleanup

* apply review suggestion

---------

Co-authored-by: database64128 <free122448@hotmail.com>
2026-01-24 23:11:35 +08:00
Zeehan2005
8e62b9b541 Enhance README with additional attribute @cn details (#3212)
Expanded the explanation of attributes in the README to include domains available in China mainland.

[skip ci]
2026-01-24 16:05:37 +08:00
EpLiar
85edae7ba1 Add new Binance API endpoint 'binanceru.net' (#3210) 2026-01-23 14:57:08 +08:00
MkQtS
1bd07b2e76 Support to export all lists to a plain yml (#3211)
* Refactor: improve deduplicate

* Feat: support to export all lists to a plain yml

use: `--exportlists=_all_`

* Docs: add links for dlc plain yml

[skip ci]
2026-01-23 14:56:35 +08:00
Kusu
614a880a55 okx: add okx.cab (#3209) 2026-01-22 22:14:50 +08:00
MkQtS
676832d14a Improve value checkers and docs (#3208)
* Refactor: improve value checkers

* Docs: small improvements

[skip ci]
2026-01-22 18:46:53 +08:00
MkQtS
a2f08a142c Docs: update for selective inclusion and affiliations (#3207)
[skip ci]
2026-01-22 14:30:10 +08:00
MkQtS
2359ad7f8e Add eneba (#3205) 2026-01-22 11:40:44 +08:00
MkQtS
330592feff xiaohongshu: add rednotecdn.com (#3204) 2026-01-22 11:30:18 +08:00
blackyau
f44fbc801d category-hospital-cn: add cd120.com (#3203) 2026-01-22 10:57:38 +08:00
深鸣
03c5e05305 Add more !cn domains (#3200) 2026-01-21 09:44:07 +08:00
深鸣
bd21f84381 Add more cn domains (#3199)
* category-games-cn: add arcaea.cn
* geolocation-cn: add baimiao
2026-01-21 09:42:31 +08:00
MkQtS
912c689da3 Cast out !cn rules from cn lists (#3198)
* Cast out !cn rules from cn lists

* Docs: add notice about !cn rules in cn lists
2026-01-20 21:36:27 +08:00
Luo Chen
d1addde6f7 xiaomi: add xiaomi-iot domains (#3097)
- Add account.xiaomi.com for OAuth2 authentication
- Add ha.api.io.mi.com for HTTP API
- Add miot-spec.org for MIoT specification API
- Add cn-ha.mqtt.io.mi.com for MQTT broker
2026-01-20 21:06:11 +08:00
MkQtS
ec95fedc45 Refactor main.go (#3119)
* Refactor: reduce the use of strings.TrimSpace

* Refactor: use string attr before toProto

* Refactor parseEntry

- add value/attribute checker(check missing space)
- allow multiple spaces
- sort attributes
- improve readablity

* Refactor exportPlainTextList

- remove unnecessary variable
- improve readablity

* Remove support for partial include

This reverts e640ac2783

It is problematic and I will implement a new one

* Refactor: promote refMap

* Feat: add support for partial include

- refactor inclusion logic
- add basic deduplicate

* Refactor exporting plaintext list

* Feat: add support for affiliation

A domain rule is always added to the list corresponding to the filename
it resides in. Additionally, you can now add affiliations to a domain
rule, and the rule will be added to the list specified by the
affiliation. Each affiliation begins with `&` and followed by the name
of the affiliation.

This helps us to reduce the number of data files without compromising
functionality, and avoid writing a same rule in different files.

* Feat: add advanced deduplicate for subdomains

only for domain/full subdomains without attr

* Refactor: import and use slices

* Refactor: improve code
2026-01-20 20:58:32 +08:00
65 changed files with 724 additions and 389 deletions

View File

@@ -33,15 +33,17 @@ jobs:
echo "TAG_NAME=$(date +%Y%m%d%H%M%S)" >> $GITHUB_ENV echo "TAG_NAME=$(date +%Y%m%d%H%M%S)" >> $GITHUB_ENV
shell: bash shell: bash
- name: Build dlc.dat file - name: Build dlc.dat and plain lists
run: | run: |
cd code || exit 1 cd code || exit 1
go run ./ --outputdir=../ --exportlists=category-ads-all,tld-cn,cn,tld-\!cn,geolocation-\!cn,apple,icloud go run ./ --outputdir=../ --exportlists=category-ads-all,tld-cn,cn,tld-\!cn,geolocation-\!cn,apple,icloud
go run ./cmd/datdump/main.go --inputdata=../dlc.dat --outputdir=../ --exportlists=_all_
cd ../ && rm -rf code cd ../ && rm -rf code
- name: Generate dlc.dat sha256 hash - name: Generate dlc.dat sha256 hash
run: | run: |
sha256sum dlc.dat > dlc.dat.sha256sum sha256sum dlc.dat > dlc.dat.sha256sum
sha256sum dlc.dat_plain.yml > dlc.dat_plain.yml.sha256sum
- name: Generate Zip - name: Generate Zip
run: | run: |
@@ -66,6 +68,6 @@ jobs:
- name: Release and upload assets - name: Release and upload assets
run: | run: |
gh release create ${{ env.TAG_NAME }} --generate-notes --latest --title ${{ env.RELEASE_NAME }} ./dlc.dat ./dlc.dat.* gh release create ${{ env.TAG_NAME }} --generate-notes --latest --title ${{ env.RELEASE_NAME }} ./dlc.dat ./dlc.dat.* ./dlc.dat_plain.yml ./dlc.dat_plain.yml.*
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

1
.gitignore vendored
View File

@@ -8,4 +8,5 @@
dlc.dat dlc.dat
# Exported plaintext lists. # Exported plaintext lists.
/*.yml
/*.txt /*.txt

View File

@@ -10,6 +10,14 @@ This project is not opinionated. In other words, it does NOT endorse, claim or i
- **dlc.dat**[https://github.com/v2fly/domain-list-community/releases/latest/download/dlc.dat](https://github.com/v2fly/domain-list-community/releases/latest/download/dlc.dat) - **dlc.dat**[https://github.com/v2fly/domain-list-community/releases/latest/download/dlc.dat](https://github.com/v2fly/domain-list-community/releases/latest/download/dlc.dat)
- **dlc.dat.sha256sum**[https://github.com/v2fly/domain-list-community/releases/latest/download/dlc.dat.sha256sum](https://github.com/v2fly/domain-list-community/releases/latest/download/dlc.dat.sha256sum) - **dlc.dat.sha256sum**[https://github.com/v2fly/domain-list-community/releases/latest/download/dlc.dat.sha256sum](https://github.com/v2fly/domain-list-community/releases/latest/download/dlc.dat.sha256sum)
- **dlc.dat_plain.yml**[https://github.com/v2fly/domain-list-community/releases/latest/download/dlc.dat_plain.yml](https://github.com/v2fly/domain-list-community/releases/latest/download/dlc.dat_plain.yml)
- **dlc.dat_plain.yml.sha256sum**[https://github.com/v2fly/domain-list-community/releases/latest/download/dlc.dat_plain.yml.sha256sum](https://github.com/v2fly/domain-list-community/releases/latest/download/dlc.dat_plain.yml.sha256sum)
## Notice
Rules with `@!cn` attribute has been cast out from cn lists. `geosite:geolocation-cn@!cn` is no longer available. Check [#390](https://github.com/v2fly/domain-list-community/issues/390), [#3119](https://github.com/v2fly/domain-list-community/pull/3119) and [#3198](https://github.com/v2fly/domain-list-community/pull/3198) for more information.
Please report if you have any problems or questions.
## Usage example ## Usage example
@@ -85,38 +93,45 @@ All data are under `data` directory. Each file in the directory represents a sub
# comments # comments
include:another-file include:another-file
domain:google.com @attr1 @attr2 domain:google.com @attr1 @attr2
full:analytics.google.com @ads
keyword:google keyword:google
regexp:www\.google\.com$ regexp:^odd[1-7]\.example\.org(\.[a-z]{2})?$
full:www.google.com
``` ```
**Syntax:** **Syntax:**
> [!NOTE]
> Adding new `regexp` and `keyword` rules is discouraged because it is easy to use them incorrectly, and proxy software cannot efficiently match these types of rules.
> [!NOTE]
> The following types of rules are **NOT** fully compatible with the ones that defined by user in V2Ray config file. Do **Not** copy and paste directly. > The following types of rules are **NOT** fully compatible with the ones that defined by user in V2Ray config file. Do **Not** copy and paste directly.
- Comment begins with `#`. It may begin anywhere in the file. The content in the line after `#` is treated as comment and ignored in production. - Comment begins with `#`. It may begin anywhere in the file. The content in the line after `#` is treated as comment and ignored in production.
- Inclusion begins with `include:`, followed by the file name of an existing file in the same directory.
- Subdomain begins with `domain:`, followed by a valid domain name. The prefix `domain:` may be omitted. - Subdomain begins with `domain:`, followed by a valid domain name. The prefix `domain:` may be omitted.
- Keyword begins with `keyword:`, followed by a string.
- Regular expression begins with `regexp:`, followed by a valid regular expression (per Golang's standard).
- Full domain begins with `full:`, followed by a complete and valid domain name. - Full domain begins with `full:`, followed by a complete and valid domain name.
- Domains (including `domain`, `keyword`, `regexp` and `full`) may have one or more attributes. Each attribute begins with `@` and followed by the name of the attribute. - Keyword begins with `keyword:`, followed by a substring of a valid domain name.
- Regular expression begins with `regexp:`, followed by a valid regular expression (per Golang's standard).
> **Note:** Adding new `regexp` and `keyword` rules is discouraged because it is easy to use them incorrectly, and proxy software cannot efficiently match these types of rules. - Domain rules (including `domain`, `full`, `keyword`, and `regexp`) may have none, one or more attributes. Each attribute begins with `@` and followed by the name of the attribute. Attributes will remain available in final lists and `dlc.dat`.
- Domain rules may have none, one or more affiliations, which additionally adds the domain rule into the affiliated target list. Each affiliation begins with `&` and followed by the name of the target list (nomatter whether the target has a dedicated file in data path). This is a method for data management, and will not remain in the final lists or `dlc.dat`.
- Inclusion begins with `include:`, followed by the name of another valid domain list. A simple `include:listb` in file `lista` means adding all domain rules of `listb` into `lista`. Inclusions with attributes stands for selective inclusion. `include:listb @attr1 @-attr2` means only adding those domain rules *with* `@attr1` **and** *without* `@attr2`. This is a special type for data management, and will not remain in the final lists or `dlc.dat`.
## How it works ## How it works
The entire `data` directory will be built into an external `geosite` file for Project V. Each file in the directory represents a section in the generated file. The entire `data` directory will be built into an external `geosite` file for Project V. Each file in the directory represents a section in the generated file.
To generate a section: **General steps:**
1. Remove all the comments in the file. 1. Read files in the data path (ignore all comments and empty lines).
2. Replace `include:` lines with the actual content of the file. 2. Parse and resolve source data, turn affiliations and inclusions into actual domain rules in proper lists.
3. Omit all empty lines. 3. Deduplicate and sort rules in every list.
4. Generate each `domain:` line into a [sub-domain routing rule](https://github.com/v2fly/v2ray-core/blob/master/app/router/routercommon/common.proto#L21). 4. Export desired plain text lists.
5. Generate each `full:` line into a [full domain routing rule](https://github.com/v2fly/v2ray-core/blob/master/app/router/routercommon/common.proto#L23). 5. Generate `dlc.dat`:
6. Generate each `keyword:` line into a [plain domain routing rule](https://github.com/v2fly/v2ray-core/blob/master/app/router/routercommon/common.proto#L17). - turn each `domain:` line into a [sub-domain routing rule](https://github.com/v2fly/v2ray-core/blob/master/app/router/routercommon/common.proto#L21).
7. Generate each `regexp:` line into a [regex domain routing rule](https://github.com/v2fly/v2ray-core/blob/master/app/router/routercommon/common.proto#L19). - turn each `full:` line into a [full domain routing rule](https://github.com/v2fly/v2ray-core/blob/master/app/router/routercommon/common.proto#L23).
- turn each `keyword:` line into a [plain domain routing rule](https://github.com/v2fly/v2ray-core/blob/master/app/router/routercommon/common.proto#L17).
- turn each `regexp:` line into a [regex domain routing rule](https://github.com/v2fly/v2ray-core/blob/master/app/router/routercommon/common.proto#L19).
Read [main.go](./main.go) for details.
## How to organize domains ## How to organize domains
@@ -126,7 +141,7 @@ Theoretically any string can be used as the name, as long as it is a valid file
### Attributes ### Attributes
Attribute is useful for sub-group of domains, especially for filtering purpose. For example, the list of `google` domains may contains its main domains, as well as domains that serve ads. The ads domains may be marked by attribute `@ads`, and can be used as `geosite:google@ads` in V2Ray routing. Attribute is useful for sub-group of domains, especially for filtering purpose. For example, the list of `google` may contains its main domains, as well as domains that serve ads. The ads domains may be marked by attribute `@ads`, and can be used as `geosite:google@ads` in V2Ray routing. Domains and services that originate from outside China mainland but have access point in China mainland, may be marked by attribute `@cn`.
## Contribution guideline ## Contribution guideline

164
cmd/datdump/main.go Normal file
View File

@@ -0,0 +1,164 @@
package main
import (
"bufio"
"flag"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/v2fly/domain-list-community/internal/dlc"
router "github.com/v2fly/v2ray-core/v5/app/router/routercommon"
"google.golang.org/protobuf/proto"
)
var (
inputData = flag.String("inputdata", "dlc.dat", "Name of the geosite dat file")
outputDir = flag.String("outputdir", "./", "Directory to place all generated files")
exportLists = flag.String("exportlists", "", "Lists to be exported, separated by ',' (empty for _all_)")
)
type DomainRule struct {
Type string
Value string
Attrs []string
}
type DomainList struct {
Name string
Rules []DomainRule
}
func (d *DomainRule) domain2String() string {
dstring := d.Type + ":" + d.Value
if len(d.Attrs) != 0 {
dstring += ":@" + strings.Join(d.Attrs, ",@")
}
return dstring
}
func loadGeosite(path string) ([]DomainList, map[string]*DomainList, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, nil, fmt.Errorf("failed to read geosite file: %w", err)
}
vgeositeList := new(router.GeoSiteList)
if err := proto.Unmarshal(data, vgeositeList); err != nil {
return nil, nil, fmt.Errorf("failed to unmarshal: %w", err)
}
domainLists := make([]DomainList, len(vgeositeList.Entry))
domainListByName := make(map[string]*DomainList, len(vgeositeList.Entry))
for i, vsite := range vgeositeList.Entry {
rules := make([]DomainRule, 0, len(vsite.Domain))
for _, vdomain := range vsite.Domain {
rule := DomainRule{Value: vdomain.Value}
switch vdomain.Type {
case router.Domain_RootDomain:
rule.Type = dlc.RuleTypeDomain
case router.Domain_Regex:
rule.Type = dlc.RuleTypeRegexp
case router.Domain_Plain:
rule.Type = dlc.RuleTypeKeyword
case router.Domain_Full:
rule.Type = dlc.RuleTypeFullDomain
default:
return nil, nil, fmt.Errorf("invalid rule type: %+v", vdomain.Type)
}
for _, vattr := range vdomain.Attribute {
rule.Attrs = append(rule.Attrs, vattr.Key)
}
rules = append(rules, rule)
}
domainLists[i] = DomainList{
Name: strings.ToUpper(vsite.CountryCode),
Rules: rules,
}
domainListByName[domainLists[i].Name] = &domainLists[i]
}
return domainLists, domainListByName, nil
}
func exportSite(name string, domainListByName map[string]*DomainList) error {
domainList, ok := domainListByName[strings.ToUpper(name)]
if !ok {
return fmt.Errorf("list '%s' does not exist", name)
}
if len(domainList.Rules) == 0 {
return fmt.Errorf("list '%s' is empty", name)
}
file, err := os.Create(filepath.Join(*outputDir, name+".yml"))
if err != nil {
return err
}
defer file.Close()
w := bufio.NewWriter(file)
fmt.Fprintf(w, "%s:\n", name)
for _, domain := range domainList.Rules {
fmt.Fprintf(w, " - %q\n", domain.domain2String())
}
return w.Flush()
}
func exportAll(filename string, domainLists []DomainList) error {
file, err := os.Create(filepath.Join(*outputDir, filename))
if err != nil {
return err
}
defer file.Close()
w := bufio.NewWriter(file)
w.WriteString("lists:\n")
for _, domainList := range domainLists {
fmt.Fprintf(w, " - name: %s\n", strings.ToLower(domainList.Name))
fmt.Fprintf(w, " length: %d\n", len(domainList.Rules))
w.WriteString(" rules:\n")
for _, domain := range domainList.Rules {
fmt.Fprintf(w, " - %q\n", domain.domain2String())
}
}
return w.Flush()
}
func main() {
flag.Parse()
// Create output directory if not exist
if _, err := os.Stat(*outputDir); os.IsNotExist(err) {
if mkErr := os.MkdirAll(*outputDir, 0755); mkErr != nil {
fmt.Println("Failed to create output directory:", mkErr)
os.Exit(1)
}
}
fmt.Printf("Loading %s...\n", *inputData)
domainLists, domainListByName, err := loadGeosite(*inputData)
if err != nil {
fmt.Println("Failed to loadGeosite:", err)
os.Exit(1)
}
var exportListSlice []string
for raw := range strings.SplitSeq(*exportLists, ",") {
if trimmed := strings.TrimSpace(raw); trimmed != "" {
exportListSlice = append(exportListSlice, trimmed)
}
}
if len(exportListSlice) == 0 {
exportListSlice = []string{"_all_"}
}
for _, eplistname := range exportListSlice {
if strings.EqualFold(eplistname, "_all_") {
if err := exportAll(filepath.Base(*inputData)+"_plain.yml", domainLists); err != nil {
fmt.Println("Failed to exportAll:", err)
continue
}
} else {
if err := exportSite(eplistname, domainListByName); err != nil {
fmt.Println("Failed to exportSite:", err)
continue
}
}
fmt.Printf("list: '%s' has been exported successfully.\n", eplistname)
}
}

View File

@@ -1 +0,0 @@
adcolony.com @ads

View File

@@ -1,4 +1,4 @@
adjust.com @ads adjust.com @ads
adjust.net.in @ads
adjust.io @ads adjust.io @ads
adjust.net.in @ads
adjust.world @ads adjust.world @ads

View File

@@ -756,6 +756,7 @@ full:amp-api-edge.apps.apple.com @cn
full:amp-api-search-edge.apps.apple.com @cn full:amp-api-search-edge.apps.apple.com @cn
full:amp-api.apps.apple.com @cn full:amp-api.apps.apple.com @cn
full:amp-api.music.apple.com @cn full:amp-api.music.apple.com @cn
full:aod-ssl.itunes.apple.com @cn
full:aod.itunes.apple.com @cn full:aod.itunes.apple.com @cn
full:api-edge.apps.apple.com @cn full:api-edge.apps.apple.com @cn
full:apptrailers.itunes.apple.com @cn full:apptrailers.itunes.apple.com @cn

View File

@@ -1,2 +0,0 @@
applovin.com @ads
applvn.com @ads

View File

@@ -1,7 +1,11 @@
include:trello
atl-paas.net
atlassian-dev.net
atlassian.com atlassian.com
atlassian.net atlassian.net
bitbucket.io bitbucket.io
bitbucket.org bitbucket.org
jira.com
ss-inf.net
statuspage.io statuspage.io
include:trello

View File

@@ -1,3 +0,0 @@
atom-data.io @ads
analytics-data.io @ads
ironbeast.io @ads

View File

@@ -28,8 +28,10 @@ binancezh.top
# API # API
binanceapi.com binanceapi.com
binanceru.net
bnbstatic.com bnbstatic.com
bntrace.com bntrace.com
bsappapi.com
nftstatic.com nftstatic.com
# saas # saas

9
data/bohemia Normal file
View File

@@ -0,0 +1,9 @@
arma3.com
armaplatform.com
bistudio.com
bohemia.net
dayz.com
makearmanotwar.com
silicagame.com
vigorgame.com
ylands.com

View File

@@ -1,29 +1,21 @@
# This file contains domains that clearly serving ads # This file contains domains that clearly serving ads
include:acfun-ads include:acfun-ads
include:adcolony-ads
include:adjust-ads
include:adobe-ads include:adobe-ads
include:alibaba-ads include:alibaba-ads
include:amazon-ads include:amazon-ads
include:apple-ads include:apple-ads
include:applovin-ads
include:atom-data-ads
include:baidu-ads include:baidu-ads
include:bytedance-ads include:bytedance-ads
include:category-ads-ir include:category-ads-ir
include:cctv @ads include:cctv @ads
include:clearbit-ads include:disney @ads
include:dmm-ads include:dmm-ads
include:duolingo-ads include:duolingo-ads
include:emogi-ads include:gamersky @ads
include:flurry-ads
include:google-ads include:google-ads
include:growingio-ads include:hetzner @ads
include:hiido-ads
include:hotjar-ads
include:hunantv-ads include:hunantv-ads
include:inner-active-ads
include:iqiyi-ads include:iqiyi-ads
include:jd-ads include:jd-ads
include:kuaishou-ads include:kuaishou-ads
@@ -31,30 +23,25 @@ include:kugou-ads
include:letv-ads include:letv-ads
include:meta-ads include:meta-ads
include:microsoft-ads include:microsoft-ads
include:mopub-ads
include:mxplayer-ads
include:netease-ads include:netease-ads
include:newrelic-ads
include:ogury-ads
include:ookla-speedtest-ads include:ookla-speedtest-ads
include:openx-ads include:openai @ads
include:picacg @ads include:picacg @ads
include:pocoiq-ads include:pikpak @ads
include:pubmatic-ads include:pixiv @ads
include:qihoo360-ads include:qihoo360-ads
include:segment-ads include:samsung @ads
include:sina-ads include:sina-ads
include:snap @ads
include:sohu-ads include:sohu-ads
include:spotify-ads include:spotify-ads
include:supersonic-ads
include:tagtic-ads
include:tappx-ads
include:television-ads include:television-ads
include:tencent-ads include:tencent-ads
include:tendcloud @ads include:tendcloud @ads
include:uberads-ads include:twitter @ads
include:umeng-ads include:umeng-ads
include:unity-ads include:unity @ads
include:unitychina @ads
include:xhamster-ads include:xhamster-ads
include:xiaomi-ads include:xiaomi-ads
include:ximalaya-ads include:ximalaya-ads
@@ -77,26 +64,32 @@ adservice.sigmob.cn
adtechus.com adtechus.com
adtrue.com adtrue.com
adxprtz.com adxprtz.com
assets.growingio.com
cdn.advertserve.com cdn.advertserve.com
cdn.banclip.com cdn.banclip.com
cfts1tifqr.com cfts1tifqr.com
contentabc.com contentabc.com
cretgate.com cretgate.com
data.flurry.com
decide.mixpanel.com decide.mixpanel.com
emogi.com
ero-advertising.com ero-advertising.com
eroadvertising.com eroadvertising.com
evt.mxplay.com
exoclick.com exoclick.com
exosrv.com exosrv.com
go2.global go2.global
gozendata.com gozendata.com
gzads.com
gz-data.com gz-data.com
gzads.com
img-bss.csdn.net img-bss.csdn.net
imglnkc.com imglnkc.com
imglnkd.com imglnkd.com
inner-active.mobi
innovid.com innovid.com
jads.co jads.co
jl3.yjaxa.top jl3.yjaxa.top
js-agent.newrelic.com
juicyads.com juicyads.com
kepler-37b.com kepler-37b.com
leanplum.com leanplum.com
@@ -104,22 +97,26 @@ lqc006.com
moat.com moat.com
moatads.com moatads.com
mobwithad.com mobwithad.com
mopub.com
onesignal.com onesignal.com
realsrv.com realsrv.com
s4yxaqyq95.com s4yxaqyq95.com
shhs-ydd8x2.yjrmss.cn shhs-ydd8x2.yjrmss.cn
ssp.api.tappx.com
static.hotjar.com
static.javhd.com static.javhd.com
tm-banners.gamingadult.com tm-banners.gamingadult.com
trafficfactory.biz trafficfactory.biz
tsyndicate.com tsyndicate.com
uberads.com
wwads.cn wwads.cn
# 36Kr
adx.36kr.com
# 12306 # 12306
ad.12306.cn ad.12306.cn
# 36Kr
adx.36kr.com
# AdHub # AdHub
hubcloud.com.cn hubcloud.com.cn
@@ -130,6 +127,10 @@ beizi.biz
click.ali213.net click.ali213.net
pbmp.ali213.net pbmp.ali213.net
# AppLovin
applovin.com
applvn.com
# Caixin # Caixin
# regexp:^pinggai\d\.caixin\.com$ # regexp:^pinggai\d\.caixin\.com$
full:pinggai0.caixin.com full:pinggai0.caixin.com
@@ -147,12 +148,29 @@ full:pinggai9.caixin.com
adq.chinaso.com adq.chinaso.com
stat.chinaso.com stat.chinaso.com
# hiido
mlog.hiido.com
ylog.hiido.com
# Httpool # Httpool
toboads.com toboads.com
# ironSource Atom
analytics-data.io
atom-data.io
ironbeast.io
# pocoiq
cdn.pocoiq.cn
oct.pocoiq.cn
# Qiniu # Qiniu
dn-growing.qbox.me dn-growing.qbox.me
# tagtic
g1.tagtic.cn
xy-log.tagtic.cn
# UNI Marketing # UNI Marketing
ad.unimhk.com ad.unimhk.com

View File

@@ -1,30 +1,34 @@
# This file contains domains of all ads providers, including both the domains that serves ads, and the domains of providers themselves. # This file contains domains of all ads providers, including both the domains that serves ads, and the domains of providers themselves.
include:category-ads
include:adjust
include:clearbit
include:ogury
include:openx
include:pubmatic
include:segment
include:supersonic
include:taboola
1rx.io @ads
7box.vip @ads 7box.vip @ads
ad-delivery.net @ads ad-delivery.net @ads
adcolony.com @ads
adinplay.com @ads adinplay.com @ads
adnxs.com @ads adnxs.com @ads
adview.cn @ads
ads.trafficjunky.net @ads ads.trafficjunky.net @ads
advertserve.com @ads advertserve.com @ads
adview.cn @ads
casalemedia.com @ads casalemedia.com @ads
contextual.media.net @ads contextual.media.net @ads
cpmstar.com @ads cpmstar.com @ads
demdex.net @ads demdex.net @ads
httpool.com @ads httpool.com @ads
lijit.com @ads lijit.com @ads
1rx.io @ads
mfadsrvr.com @ads mfadsrvr.com @ads
mgid.com @ads mgid.com @ads
ns1p.net @ads ns1p.net @ads
pubmatic.com @ads
sigmob.com @ads sigmob.com @ads
snapads.com @ads
spotxchange.com @ads spotxchange.com @ads
unimhk.com @ads unimhk.com @ads
upapi.net @ads upapi.net @ads
include:taboola
include:category-ads

View File

@@ -1,9 +1,9 @@
include:boc include:boc @-!cn
include:ccb include:ccb @-!cn
include:citic include:citic @-!cn
include:cmb include:cmb @-!cn
include:hsbc-cn include:hsbc-cn
include:icbc include:icbc @-!cn
include:unionpay include:unionpay
abchina.com abchina.com

View File

@@ -48,6 +48,7 @@ include:kakao
include:kaspersky include:kaspersky
include:lg include:lg
include:logitech include:logitech
include:louisvuitton
include:mailru-group include:mailru-group
include:meta include:meta
include:microsoft include:microsoft

View File

@@ -4,7 +4,7 @@ include:apipost
include:baltamatica include:baltamatica
include:cnblogs include:cnblogs
include:csdn include:csdn
include:deepin include:deepin @-!cn
include:gitee include:gitee
include:goproxy include:goproxy
include:huawei-dev include:huawei-dev
@@ -18,6 +18,7 @@ include:segmentfault
include:sxl include:sxl
include:tencent-dev include:tencent-dev
include:ubuntukylin include:ubuntukylin
include:unitychina
jinrishici.com jinrishici.com
openvela.com openvela.com

View File

@@ -142,3 +142,5 @@ ystbds.com
zhan.com zhan.com
# 智慧树 # 智慧树
zhihuishu.com zhihuishu.com
# 浙江省教育考试院
zjzs.net

View File

@@ -2,6 +2,9 @@ include:playcover
include:fflogs include:fflogs
include:trackernetwork include:trackernetwork
# Anti-Cheat
battleye.com
# Android Emulator # Android Emulator
bluestacks.com bluestacks.com
ldmnq.com @cn ldmnq.com @cn
@@ -16,5 +19,5 @@ prts.plus
heavenlywind.cc @cn heavenlywind.cc @cn
poi.moe poi.moe
# Steam++ / Watt Toolkit
steampp.net @cn steampp.net @cn

View File

@@ -6,7 +6,7 @@ include:aamgame
include:acfun include:acfun
include:acplay include:acplay
include:bestv include:bestv
include:bilibili include:bilibili @-!cn
include:ciweimao include:ciweimao
include:dedao include:dedao
include:douyin include:douyin
@@ -18,7 +18,7 @@ include:gamersky
include:gitv include:gitv
include:hunantv include:hunantv
include:huya include:huya
include:iqiyi include:iqiyi @-!cn
include:ku6 include:ku6
include:kuaikan include:kuaikan
include:kuaishou include:kuaishou

View File

@@ -1,10 +1,12 @@
include:2kgames include:2kgames
include:blizzard include:blizzard
include:bluearchive include:bluearchive
include:bohemia
include:curseforge include:curseforge
include:cygames include:cygames
include:ea include:ea
include:embark include:embark
include:eneba
include:epicgames include:epicgames
include:escapefromtarkov include:escapefromtarkov
include:faceit include:faceit

View File

@@ -9,13 +9,15 @@ include:mihoyo-cn
include:tencent-games include:tencent-games
include:tiancity include:tiancity
include:vrzwk include:vrzwk
include:xd include:xd @-!cn
include:yokaverse include:yokaverse
# 北京奇客创想科技有限公司 # 北京奇客创想科技有限公司
7k7k.com 7k7k.com
# 刀锋盒子 皖B2-20190103-4 # 刀锋盒子 皖B2-20190103-4
9xgame.com 9xgame.com
# 韵律谱面研究站 桂ICP备20001846号-3
arcaea.cn
# 《异象回声》游戏官网 沪ICP备2023010411号-1 # 《异象回声》游戏官网 沪ICP备2023010411号-1
astral-vector.com astral-vector.com
# 九九互动 粤ICP备19068416号 # 九九互动 粤ICP备19068416号

View File

@@ -10,3 +10,6 @@ yctdyy.com
# 南方医科大学深圳医院 # 南方医科大学深圳医院
smuszh.com smuszh.com
# 四川大学华西医院
cd120.com

View File

@@ -6,7 +6,7 @@ include:gracg
include:hupu include:hupu
include:meipian include:meipian
include:okjike include:okjike
include:sina include:sina @-!cn
include:xiaohongshu include:xiaohongshu
include:yy include:yy
include:zhihu include:zhihu

View File

@@ -41,6 +41,9 @@ cloudflarewarp.com
cloudflareworkers.com cloudflareworkers.com
encryptedsni.com encryptedsni.com
every1dns.net every1dns.net
foundationdns.com
foundationdns.net
foundationdns.org
imagedelivery.net imagedelivery.net
isbgpsafeyet.com isbgpsafeyet.com
one.one.one one.one.one

View File

@@ -1 +0,0 @@
emogi.com @ads

2
data/eneba Normal file
View File

@@ -0,0 +1,2 @@
eneba.com
eneba.games

View File

@@ -1 +0,0 @@
data.flurry.com @ads

View File

@@ -102,6 +102,8 @@ include:w3schools
include:zotero include:zotero
chemequations.com # 线上化学方程式! chemequations.com # 线上化学方程式!
geogebra.org
wolframalpha.com
# Entertainment & Games & Music & Podcasts & Videos # Entertainment & Games & Music & Podcasts & Videos
include:category-entertainment include:category-entertainment
@@ -300,6 +302,7 @@ include:wikimedia
atwiki.jp atwiki.jp
touhouwiki.net touhouwiki.net
wiki.gg
# Others # Others
include:avaxhome include:avaxhome

View File

@@ -24,7 +24,6 @@ include:category-social-media-cn
# Advertisment & Analytics # Advertisment & Analytics
include:getui include:getui
include:jiguang include:jiguang
include:umeng
# 神策数据 # 神策数据
sensorsdata.cn sensorsdata.cn
@@ -38,9 +37,9 @@ include:category-httpdns-cn
# Bank & Finance & Insurance & Securities # Bank & Finance & Insurance & Securities
include:category-bank-cn include:category-bank-cn
include:category-securities-cn include:category-securities-cn
include:eastmoney include:eastmoney @-!cn
include:everbright include:everbright
include:pingan include:pingan @-!cn
include:taikang include:taikang
## 航财通·校园付 ## 航财通·校园付
@@ -92,11 +91,11 @@ pkoplink.com
# E-commerce # E-commerce
include:58tongcheng include:58tongcheng
include:ctrip include:ctrip @-!cn
include:dangdang include:dangdang
include:dewu include:dewu @-!cn
include:dongjiao include:dongjiao
include:jd include:jd @-!cn
include:lianjia include:lianjia
include:meituan include:meituan
include:miaomiaozhe include:miaomiaozhe
@@ -411,19 +410,19 @@ zhaopin.cn
# Tech companies & Orginations # Tech companies & Orginations
include:aisiku # 北京艾斯酷科技有限公司 include:aisiku # 北京艾斯酷科技有限公司
include:akiko # 秋子酱科技 include:akiko # 秋子酱科技
include:alibaba include:alibaba @-!cn
include:baidu include:baidu
include:beisen include:beisen
include:bluepoch include:bluepoch
include:bytedance include:bytedance @-!cn
include:didi include:didi @-!cn
include:dingdatech # 叮哒出行(杭州金通互联科技有限公司) include:dingdatech # 叮哒出行(杭州金通互联科技有限公司)
include:dji include:dji
include:gree include:gree
include:haier include:haier
include:hikvision include:hikvision
include:honor include:honor
include:huawei include:huawei @-!cn
include:hupun # 杭州湖畔网络技术有限公司 include:hupun # 杭州湖畔网络技术有限公司
include:iflytek include:iflytek
include:ishumei # 北京数美时代科技有限公司 include:ishumei # 北京数美时代科技有限公司
@@ -435,14 +434,14 @@ include:meizu
include:midea include:midea
include:narwal # 云鲸科技 include:narwal # 云鲸科技
include:netease include:netease
include:oppo include:oppo @-!cn
include:qihoo360 include:qihoo360
include:sumkoo #北京尚古创新科技有限公司 include:sumkoo #北京尚古创新科技有限公司
include:tcl include:tcl
include:tencent include:tencent @-!cn
include:tongfang include:tongfang
include:vivo include:vivo @-!cn
include:xiaomi include:xiaomi @-!cn
include:xunlei include:xunlei
include:youquan # 祐全科技 include:youquan # 祐全科技
include:yuanbei # 上海圆贝信息科技有限公司 include:yuanbei # 上海圆贝信息科技有限公司
@@ -479,13 +478,20 @@ xsbapp.cn
# Telecommunication # Telecommunication
include:chinabroadnet include:chinabroadnet
include:chinamobile include:chinamobile @-!cn
include:chinatelecom include:chinatelecom @-!cn
include:chinatower include:chinatower
include:chinaunicom include:chinaunicom @-!cn
# 在线工具 # 在线工具
include:ipip # IPIP ip地理位置数据库 ## IPIP ip地理位置数据库
include:ipip @-!cn
## 白描
baimiao.tech
baimiaoapp.com
shinescan.tech
uzero.cn
chaziyu.com # 滇ICP备2024035496号 chaziyu.com # 滇ICP备2024035496号
fofa.info # Fofa网站测绘华顺信安 fofa.info # Fofa网站测绘华顺信安

View File

@@ -1 +0,0 @@
assets.growingio.com @ads

View File

@@ -1,2 +0,0 @@
mlog.hiido.com @ads
ylog.hiido.com @ads

View File

@@ -1 +0,0 @@
static.hotjar.com @ads

View File

@@ -1 +0,0 @@
inner-active.mobi @ads

View File

@@ -1,3 +1,4 @@
cdn-service.space
kino.pub kino.pub
kinopub.online kinopub.online

5
data/louisvuitton Normal file
View File

@@ -0,0 +1,5 @@
louisvuitton.cn @cn
louisvuitton.com
lvcampaign.com @cn
full:tp.louisvuitton.com @cn

View File

@@ -60,6 +60,7 @@ full:default.exp-tas.com
full:developer.microsoft.com full:developer.microsoft.com
full:download.visualstudio.microsoft.com full:download.visualstudio.microsoft.com
full:dtlgalleryint.cloudapp.net full:dtlgalleryint.cloudapp.net
full:packages.microsoft.com
full:poshtestgallery.cloudapp.net full:poshtestgallery.cloudapp.net
full:psg-int-centralus.cloudapp.net full:psg-int-centralus.cloudapp.net
full:psg-int-eastus.cloudapp.net full:psg-int-eastus.cloudapp.net

View File

@@ -1 +0,0 @@
mopub.com @ads

View File

@@ -1 +0,0 @@
evt.mxplay.com @ads

View File

@@ -1 +0,0 @@
js-agent.newrelic.com @ads

View File

@@ -1,3 +1,3 @@
ogury.co @ads
ogury.com @ads ogury.com @ads
presage.io @ads presage.io @ads
ogury.co @ads

View File

@@ -1,8 +1,9 @@
okex.com okex.com
okx.com
okx-dns.com okx-dns.com
okx-dns1.com okx-dns1.com
okx-dns2.com okx-dns2.com
okx.cab
okx.com
# OKC Browser # OKC Browser
oklink.com @cn oklink.com @cn

View File

@@ -1,6 +1,7 @@
# Main domain # Main domain
chatgpt.com
chat.com chat.com
chatgpt.com
crixet.com
oaistatic.com oaistatic.com
oaiusercontent.com oaiusercontent.com
openai.com openai.com
@@ -10,13 +11,13 @@ sora.com
openai.com.cdn.cloudflare.net openai.com.cdn.cloudflare.net
full:openaiapi-site.azureedge.net full:openaiapi-site.azureedge.net
full:openaicom-api-bdcpf8c6d2e9atf6.z01.azurefd.net full:openaicom-api-bdcpf8c6d2e9atf6.z01.azurefd.net
full:openaicom.imgix.net
full:openaicomproductionae4b.blob.core.windows.net full:openaicomproductionae4b.blob.core.windows.net
full:production-openaicom-storage.azureedge.net full:production-openaicom-storage.azureedge.net
regexp:^chatgpt-async-webps-prod-\S+-\d+\.webpubsub\.azure\.com$ regexp:^chatgpt-async-webps-prod-\S+-\d+\.webpubsub\.azure\.com$
# tracking # tracking
full:o33249.ingest.sentry.io @ads full:o33249.ingest.sentry.io @ads
full:openaicom.imgix.net @ads
full:browser-intake-datadoghq.com @ads full:browser-intake-datadoghq.com @ads
# Advanced Voice # Advanced Voice

View File

@@ -1,2 +0,0 @@
cdn.pocoiq.cn @ads
oct.pocoiq.cn @ads

View File

@@ -1 +1,2 @@
sekai.colorfulpalette.org sekai.colorfulpalette.org
pjsekai.sega.jp

View File

@@ -2,5 +2,3 @@
pubmatic.com pubmatic.com
pubmatic.co.jp pubmatic.co.jp
include:pubmatic-ads

View File

@@ -1 +0,0 @@
ads.pubmatic.com @ads

View File

@@ -44,6 +44,7 @@ dnsv1.com.cn
dothework.cn dothework.cn
ectencent.cn ectencent.cn
ectencent.com.cn ectencent.com.cn
edgeone.cool
edgeonedy1.com edgeonedy1.com
essurl.com essurl.com
exmailgz.com exmailgz.com

View File

@@ -24,6 +24,7 @@ pardot.com
quotable.com quotable.com
radian6.com radian6.com
relateiq.com relateiq.com
salesforce-setup.com
salesforce.com salesforce.com
salesforce.org salesforce.org
salesforceiq.com salesforceiq.com

View File

@@ -8,6 +8,7 @@ galaxyappstore.com
galaxymobile.jp galaxymobile.jp
game-platform.net game-platform.net
knoxemm.com knoxemm.com
ospserver.net
samsung.com samsung.com
samsungads.com @ads samsungads.com @ads
samsungapps.com samsungapps.com

View File

@@ -1,4 +1,5 @@
ssacdn.com @ads
supersonic.com @ads supersonic.com @ads
supersonicads.com @ads supersonicads.com @ads
ssacdn.com @ads
supersonicads-a.akamaihd.net @ads supersonicads-a.akamaihd.net @ads

View File

@@ -1,2 +0,0 @@
g1.tagtic.cn @ads
xy-log.tagtic.cn @ads

View File

@@ -1 +0,0 @@
ssp.api.tappx.com @ads

View File

@@ -1,2 +1,7 @@
1024terabox.com
bestclouddrive.com
freeterabox.com
nephobox.com
terabox.com terabox.com
terabox1024.com
teraboxcdn.com teraboxcdn.com

View File

@@ -1 +0,0 @@
uberads.com @ads

View File

@@ -1,4 +1,6 @@
unity.com unity.com
unity3d.com unity3d.com
include:unity-ads # Ads/tracking
iads.unity3d.com @ads
unityads.unity3d.com @ads

View File

@@ -1,6 +1,11 @@
# 优三缔 / 优美缔 / 团结引擎
u3d.cn
unity.cn
unitychina.cn
# Ads/tracking
ads.unitychina.cn @ads ads.unitychina.cn @ads
splash-ads.cdn.unity.cn @ads splash-ads.cdn.unity.cn @ads
splash-ads.unitychina.cn @ads splash-ads.unitychina.cn @ads
unityads.unity.cn @ads unityads.unity.cn @ads
unityads.unity3d.com @ads
unityads.unitychina.cn @ads unityads.unitychina.cn @ads

View File

@@ -1,5 +1,6 @@
include:askdiandian include:askdiandian
rednotecdn.com
xhscdn.com xhscdn.com
xhscdn.net xhscdn.net
xhslink.com xhslink.com

View File

@@ -1,5 +1,6 @@
include:xiaomi-ads include:xiaomi-ads
include:xiaomi-ai include:xiaomi-ai
include:xiaomi-iot
mgslb.com mgslb.com
mi-idc.com mi-idc.com

5
data/xiaomi-iot Normal file
View File

@@ -0,0 +1,5 @@
# Xiaomi IoT Services
account.xiaomi.com
cn-ha.mqtt.io.mi.com
ha.api.io.mi.com
miot-spec.org

9
internal/dlc/dlc.go Normal file
View File

@@ -0,0 +1,9 @@
package dlc
const (
RuleTypeDomain string = "domain"
RuleTypeFullDomain string = "full"
RuleTypeKeyword string = "keyword"
RuleTypeRegexp string = "regexp"
RuleTypeInclude string = "include"
)

610
main.go
View File

@@ -7,9 +7,10 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"regexp" "regexp"
"sort" "slices"
"strings" "strings"
"github.com/v2fly/domain-list-community/internal/dlc"
router "github.com/v2fly/v2ray-core/v5/app/router/routercommon" router "github.com/v2fly/v2ray-core/v5/app/router/routercommon"
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
) )
@@ -21,294 +22,361 @@ var (
exportLists = flag.String("exportlists", "", "Lists to be flattened and exported in plaintext format, separated by ',' comma") exportLists = flag.String("exportlists", "", "Lists to be flattened and exported in plaintext format, separated by ',' comma")
) )
const ( var (
RuleTypeDomain string = "domain" refMap = make(map[string][]*Entry)
RuleTypeFullDomain string = "full" plMap = make(map[string]*ParsedList)
RuleTypeKeyword string = "keyword" finalMap = make(map[string][]*Entry)
RuleTypeRegexp string = "regexp" cirIncMap = make(map[string]bool) // Used for circular inclusion detection
RuleTypeInclude string = "include"
) )
type Entry struct { type Entry struct {
Type string Type string
Value string Value string
Attrs []*router.Domain_Attribute Attrs []string
Plain string
Affs []string
} }
type List struct { type Inclusion struct {
Name string Source string
Entry []Entry MustAttrs []string
BanAttrs []string
} }
type ParsedList struct { type ParsedList struct {
Name string Name string
Inclusion map[string]bool Inclusions []*Inclusion
Entry []Entry Entries []*Entry
} }
func (l *ParsedList) toPlainText(listName string) error { func makeProtoList(listName string, entries []*Entry) (*router.GeoSite, error) {
var entryBytes []byte
for _, entry := range l.Entry {
var attrString string
if entry.Attrs != nil {
for _, attr := range entry.Attrs {
attrString += "@" + attr.GetKey() + ","
}
attrString = strings.TrimRight(":"+attrString, ",")
}
// Entry output format is: type:domain.tld:@attr1,@attr2
entryBytes = append(entryBytes, []byte(entry.Type+":"+entry.Value+attrString+"\n")...)
}
if err := os.WriteFile(filepath.Join(*outputDir, listName+".txt"), entryBytes, 0644); err != nil {
return err
}
return nil
}
func (l *ParsedList) toProto() (*router.GeoSite, error) {
site := &router.GeoSite{ site := &router.GeoSite{
CountryCode: l.Name, CountryCode: listName,
Domain: make([]*router.Domain, 0, len(entries)),
} }
for _, entry := range l.Entry { for _, entry := range entries {
switch entry.Type { pdomain := &router.Domain{Value: entry.Value}
case RuleTypeDomain: for _, attr := range entry.Attrs {
site.Domain = append(site.Domain, &router.Domain{ pdomain.Attribute = append(pdomain.Attribute, &router.Domain_Attribute{
Type: router.Domain_RootDomain, Key: attr,
Value: entry.Value, TypedValue: &router.Domain_Attribute_BoolValue{BoolValue: true},
Attribute: entry.Attrs,
}) })
case RuleTypeRegexp:
// check regexp validity to avoid runtime error
_, err := regexp.Compile(entry.Value)
if err != nil {
return nil, fmt.Errorf("invalid regexp in list %s: %s", l.Name, entry.Value)
}
site.Domain = append(site.Domain, &router.Domain{
Type: router.Domain_Regex,
Value: entry.Value,
Attribute: entry.Attrs,
})
case RuleTypeKeyword:
site.Domain = append(site.Domain, &router.Domain{
Type: router.Domain_Plain,
Value: entry.Value,
Attribute: entry.Attrs,
})
case RuleTypeFullDomain:
site.Domain = append(site.Domain, &router.Domain{
Type: router.Domain_Full,
Value: entry.Value,
Attribute: entry.Attrs,
})
default:
return nil, fmt.Errorf("unknown domain type: %s", entry.Type)
} }
switch entry.Type {
case dlc.RuleTypeDomain:
pdomain.Type = router.Domain_RootDomain
case dlc.RuleTypeRegexp:
pdomain.Type = router.Domain_Regex
case dlc.RuleTypeKeyword:
pdomain.Type = router.Domain_Plain
case dlc.RuleTypeFullDomain:
pdomain.Type = router.Domain_Full
}
site.Domain = append(site.Domain, pdomain)
} }
return site, nil return site, nil
} }
func exportPlainTextList(list []string, refName string, pl *ParsedList) { func writePlainList(exportedName string) error {
for _, listName := range list { targetList, exist := finalMap[strings.ToUpper(exportedName)]
if strings.EqualFold(refName, listName) { if !exist || len(targetList) == 0 {
if err := pl.toPlainText(strings.ToLower(refName)); err != nil { return fmt.Errorf("list %q does not exist or is empty.", exportedName)
fmt.Println("Failed:", err)
continue
}
fmt.Printf("'%s' has been generated successfully.\n", listName)
}
} }
} file, err := os.Create(filepath.Join(*outputDir, strings.ToLower(exportedName)+".txt"))
if err != nil {
func removeComment(line string) string { return err
idx := strings.Index(line, "#")
if idx == -1 {
return line
} }
return strings.TrimSpace(line[:idx]) defer file.Close()
} w := bufio.NewWriter(file)
for _, entry := range targetList {
func parseDomain(domain string, entry *Entry) error { fmt.Fprintln(w, entry.Plain)
kv := strings.Split(domain, ":")
if len(kv) == 1 {
entry.Type = RuleTypeDomain
entry.Value = strings.ToLower(kv[0])
return nil
} }
return w.Flush()
if len(kv) == 2 {
entry.Type = strings.ToLower(kv[0])
if strings.EqualFold(entry.Type, RuleTypeRegexp) {
entry.Value = kv[1]
} else {
entry.Value = strings.ToLower(kv[1])
}
return nil
}
return fmt.Errorf("invalid format: %s", domain)
}
func parseAttribute(attr string) (*router.Domain_Attribute, error) {
var attribute router.Domain_Attribute
if len(attr) == 0 || attr[0] != '@' {
return &attribute, fmt.Errorf("invalid attribute: %s", attr)
}
attribute.Key = strings.ToLower(attr[1:]) // Trim attribute prefix `@` character
attribute.TypedValue = &router.Domain_Attribute_BoolValue{BoolValue: true}
return &attribute, nil
} }
func parseEntry(line string) (Entry, error) { func parseEntry(line string) (Entry, error) {
line = strings.TrimSpace(line)
parts := strings.Split(line, " ")
var entry Entry var entry Entry
parts := strings.Fields(line)
if len(parts) == 0 { if len(parts) == 0 {
return entry, fmt.Errorf("empty entry") return entry, fmt.Errorf("empty line: %q", line)
} }
if err := parseDomain(parts[0], &entry); err != nil { // Parse type and value
return entry, err v := parts[0]
} colonIndex := strings.Index(v, ":")
if colonIndex == -1 {
for i := 1; i < len(parts); i++ { entry.Type = dlc.RuleTypeDomain // Default type
attr, err := parseAttribute(parts[i]) entry.Value = strings.ToLower(v)
if err != nil { if !validateDomainChars(entry.Value) {
return entry, err return entry, fmt.Errorf("invalid domain: %q", entry.Value)
} }
entry.Attrs = append(entry.Attrs, attr) } else {
typ := strings.ToLower(v[:colonIndex])
val := v[colonIndex+1:]
switch typ {
case dlc.RuleTypeRegexp:
if _, err := regexp.Compile(val); err != nil {
return entry, fmt.Errorf("invalid regexp %q: %w", val, err)
}
entry.Type = dlc.RuleTypeRegexp
entry.Value = val
case dlc.RuleTypeInclude:
entry.Type = dlc.RuleTypeInclude
entry.Value = strings.ToUpper(val)
if !validateSiteName(entry.Value) {
return entry, fmt.Errorf("invalid include list name: %q", entry.Value)
}
case dlc.RuleTypeDomain, dlc.RuleTypeFullDomain, dlc.RuleTypeKeyword:
entry.Type = typ
entry.Value = strings.ToLower(val)
if !validateDomainChars(entry.Value) {
return entry, fmt.Errorf("invalid domain: %q", entry.Value)
}
default:
return entry, fmt.Errorf("invalid type: %q", typ)
}
}
// Parse/Check attributes and affiliations
for _, part := range parts[1:] {
if strings.HasPrefix(part, "@") {
attr := strings.ToLower(part[1:]) // Trim attribute prefix `@` character
if !validateAttrChars(attr) {
return entry, fmt.Errorf("invalid attribute: %q", attr)
}
entry.Attrs = append(entry.Attrs, attr)
} else if strings.HasPrefix(part, "&") {
aff := strings.ToUpper(part[1:]) // Trim affiliation prefix `&` character
if !validateSiteName(aff) {
return entry, fmt.Errorf("invalid affiliation: %q", aff)
}
entry.Affs = append(entry.Affs, aff)
} else {
return entry, fmt.Errorf("invalid attribute/affiliation: %q", part)
}
}
// Sort attributes
slices.Sort(entry.Attrs)
// Formated plain entry: type:domain.tld:@attr1,@attr2
entry.Plain = entry.Type + ":" + entry.Value
if len(entry.Attrs) != 0 {
entry.Plain = entry.Plain + ":@" + strings.Join(entry.Attrs, ",@")
} }
return entry, nil return entry, nil
} }
func Load(path string) (*List, error) { func validateDomainChars(domain string) bool {
for i := range domain {
c := domain[i]
if (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '.' || c == '-' {
continue
}
return false
}
return true
}
func validateAttrChars(attr string) bool {
for i := range attr {
c := attr[i]
if (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '!' || c == '-' {
continue
}
return false
}
return true
}
func validateSiteName(name string) bool {
for i := range name {
c := name[i]
if (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '!' || c == '-' {
continue
}
return false
}
return true
}
func loadData(path string) error {
file, err := os.Open(path) file, err := os.Open(path)
if err != nil { if err != nil {
return nil, err return err
} }
defer file.Close() defer file.Close()
list := &List{ listName := strings.ToUpper(filepath.Base(path))
Name: strings.ToUpper(filepath.Base(path)), if !validateSiteName(listName) {
return fmt.Errorf("invalid list name: %s", listName)
} }
scanner := bufio.NewScanner(file) scanner := bufio.NewScanner(file)
lineIdx := 0
for scanner.Scan() { for scanner.Scan() {
line := strings.TrimSpace(scanner.Text()) line := scanner.Text()
line = removeComment(line) lineIdx++
if len(line) == 0 { // Remove comments
if idx := strings.Index(line, "#"); idx != -1 {
line = line[:idx]
}
line = strings.TrimSpace(line)
if line == "" {
continue continue
} }
entry, err := parseEntry(line) entry, err := parseEntry(line)
if err != nil { if err != nil {
return nil, err return fmt.Errorf("error in %s at line %d: %v", path, lineIdx, err)
} }
list.Entry = append(list.Entry, entry) refMap[listName] = append(refMap[listName], &entry)
} }
return nil
return list, nil
} }
func isMatchAttr(Attrs []*router.Domain_Attribute, includeKey string) bool { func parseList(refName string, refList []*Entry) error {
isMatch := false pl, _ := plMap[refName]
mustMatch := true if pl == nil {
matchName := includeKey pl = &ParsedList{Name: refName}
if strings.HasPrefix(includeKey, "!") { plMap[refName] = pl
isMatch = true
mustMatch = false
matchName = strings.TrimLeft(includeKey, "!")
} }
for _, entry := range refList {
for _, Attr := range Attrs { if entry.Type == dlc.RuleTypeInclude {
attrName := Attr.Key if len(entry.Affs) != 0 {
if mustMatch { return fmt.Errorf("affiliation is not allowed for include:%s", entry.Value)
if matchName == attrName {
isMatch = true
break
} }
} else { inc := &Inclusion{Source: entry.Value}
if matchName == attrName { for _, attr := range entry.Attrs {
isMatch = false if strings.HasPrefix(attr, "-") {
break inc.BanAttrs = append(inc.BanAttrs, attr[1:]) // Trim attribute prefix `-` character
}
}
}
return isMatch
}
func createIncludeAttrEntrys(list *List, matchAttr *router.Domain_Attribute) []Entry {
newEntryList := make([]Entry, 0, len(list.Entry))
matchName := matchAttr.Key
for _, entry := range list.Entry {
matched := isMatchAttr(entry.Attrs, matchName)
if matched {
newEntryList = append(newEntryList, entry)
}
}
return newEntryList
}
func ParseList(list *List, ref map[string]*List) (*ParsedList, error) {
pl := &ParsedList{
Name: list.Name,
Inclusion: make(map[string]bool),
}
entryList := list.Entry
for {
newEntryList := make([]Entry, 0, len(entryList))
hasInclude := false
for _, entry := range entryList {
if entry.Type == RuleTypeInclude {
refName := strings.ToUpper(entry.Value)
if entry.Attrs != nil {
for _, attr := range entry.Attrs {
InclusionName := strings.ToUpper(refName + "@" + attr.Key)
if pl.Inclusion[InclusionName] {
continue
}
pl.Inclusion[InclusionName] = true
refList := ref[refName]
if refList == nil {
return nil, fmt.Errorf("list not found: %s", entry.Value)
}
attrEntrys := createIncludeAttrEntrys(refList, attr)
if len(attrEntrys) != 0 {
newEntryList = append(newEntryList, attrEntrys...)
}
}
} else { } else {
InclusionName := refName inc.MustAttrs = append(inc.MustAttrs, attr)
if pl.Inclusion[InclusionName] {
continue
}
pl.Inclusion[InclusionName] = true
refList := ref[refName]
if refList == nil {
return nil, fmt.Errorf("list not found: %s", entry.Value)
}
newEntryList = append(newEntryList, refList.Entry...)
} }
hasInclude = true
} else {
newEntryList = append(newEntryList, entry)
} }
} pl.Inclusions = append(pl.Inclusions, inc)
entryList = newEntryList } else {
if !hasInclude { for _, aff := range entry.Affs {
break apl, _ := plMap[aff]
if apl == nil {
apl = &ParsedList{Name: aff}
plMap[aff] = apl
}
apl.Entries = append(apl.Entries, entry)
}
pl.Entries = append(pl.Entries, entry)
} }
} }
pl.Entry = entryList return nil
}
return pl, nil func polishList(roughMap *map[string]*Entry) []*Entry {
finalList := make([]*Entry, 0, len(*roughMap))
queuingList := make([]*Entry, 0, len(*roughMap)) // Domain/full entries without attr
domainsMap := make(map[string]bool)
for _, entry := range *roughMap {
switch entry.Type { // Bypass regexp, keyword and "full/domain with attr"
case dlc.RuleTypeRegexp:
finalList = append(finalList, entry)
case dlc.RuleTypeKeyword:
finalList = append(finalList, entry)
case dlc.RuleTypeDomain:
domainsMap[entry.Value] = true
if len(entry.Attrs) != 0 {
finalList = append(finalList, entry)
} else {
queuingList = append(queuingList, entry)
}
case dlc.RuleTypeFullDomain:
if len(entry.Attrs) != 0 {
finalList = append(finalList, entry)
} else {
queuingList = append(queuingList, entry)
}
}
}
// Remove redundant subdomains for full/domain without attr
for _, qentry := range queuingList {
isRedundant := false
pd := qentry.Value // To be parent domain
if qentry.Type == dlc.RuleTypeFullDomain {
pd = "." + pd // So that `domain:example.org` overrides `full:example.org`
}
for {
idx := strings.Index(pd, ".")
if idx == -1 {
break
}
pd = pd[idx+1:] // Go for next parent
if !strings.Contains(pd, ".") {
break
} // Not allow tld to be a parent
if domainsMap[pd] {
isRedundant = true
break
}
}
if !isRedundant {
finalList = append(finalList, qentry)
}
}
// Sort final entries
slices.SortFunc(finalList, func(a, b *Entry) int {
return strings.Compare(a.Plain, b.Plain)
})
return finalList
}
func resolveList(pl *ParsedList) error {
if _, pldone := finalMap[pl.Name]; pldone {
return nil
}
if cirIncMap[pl.Name] {
return fmt.Errorf("circular inclusion in: %s", pl.Name)
}
cirIncMap[pl.Name] = true
defer delete(cirIncMap, pl.Name)
isMatchAttrFilters := func(entry *Entry, incFilter *Inclusion) bool {
if len(incFilter.MustAttrs) == 0 && len(incFilter.BanAttrs) == 0 {
return true
}
if len(entry.Attrs) == 0 {
return len(incFilter.MustAttrs) == 0
}
for _, m := range incFilter.MustAttrs {
if !slices.Contains(entry.Attrs, m) {
return false
}
}
for _, b := range incFilter.BanAttrs {
if slices.Contains(entry.Attrs, b) {
return false
}
}
return true
}
roughMap := make(map[string]*Entry) // Avoid basic duplicates
for _, dentry := range pl.Entries { // Add direct entries
roughMap[dentry.Plain] = dentry
}
for _, inc := range pl.Inclusions {
incPl, exist := plMap[inc.Source]
if !exist {
return fmt.Errorf("list %q includes a non-existent list: %q", pl.Name, inc.Source)
}
if err := resolveList(incPl); err != nil {
return err
}
for _, ientry := range finalMap[inc.Source] {
if isMatchAttrFilters(ientry, inc) { // Add included entries
roughMap[ientry.Plain] = ientry
}
}
}
finalMap[pl.Name] = polishList(&roughMap)
return nil
} }
func main() { func main() {
@@ -317,7 +385,7 @@ func main() {
dir := *dataPath dir := *dataPath
fmt.Println("Use domain lists in", dir) fmt.Println("Use domain lists in", dir)
ref := make(map[string]*List) // Generate refMap
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if err != nil { if err != nil {
return err return err
@@ -325,75 +393,77 @@ func main() {
if info.IsDir() { if info.IsDir() {
return nil return nil
} }
list, err := Load(path) if err := loadData(path); err != nil {
if err != nil {
return err return err
} }
ref[list.Name] = list
return nil return nil
}) })
if err != nil { if err != nil {
fmt.Println("Failed:", err) fmt.Println("Failed to loadData:", err)
os.Exit(1) os.Exit(1)
} }
// Generate plMap
for refName, refList := range refMap {
if err := parseList(refName, refList); err != nil {
fmt.Println("Failed to parseList:", err)
os.Exit(1)
}
}
// Generate finalMap
for _, pl := range plMap {
if err := resolveList(pl); err != nil {
fmt.Println("Failed to resolveList:", err)
os.Exit(1)
}
}
// Create output directory if not exist // Create output directory if not exist
if _, err := os.Stat(*outputDir); os.IsNotExist(err) { if _, err := os.Stat(*outputDir); os.IsNotExist(err) {
if mkErr := os.MkdirAll(*outputDir, 0755); mkErr != nil { if mkErr := os.MkdirAll(*outputDir, 0755); mkErr != nil {
fmt.Println("Failed:", mkErr) fmt.Println("Failed to create output directory:", mkErr)
os.Exit(1) os.Exit(1)
} }
} }
protoList := new(router.GeoSiteList) // Export plaintext list
var existList []string var exportListSlice []string
for refName, list := range ref { for raw := range strings.SplitSeq(*exportLists, ",") {
pl, err := ParseList(list, ref) if trimmed := strings.TrimSpace(raw); trimmed != "" {
if err != nil { exportListSlice = append(exportListSlice, trimmed)
fmt.Println("Failed:", err)
os.Exit(1)
} }
site, err := pl.toProto() }
for _, exportList := range exportListSlice {
if err := writePlainList(exportList); err != nil {
fmt.Println("Failed to write list:", err)
continue
}
fmt.Printf("list %q has been generated successfully.\n", exportList)
}
// Generate dat file
protoList := new(router.GeoSiteList)
for siteName, siteEntries := range finalMap {
site, err := makeProtoList(siteName, siteEntries)
if err != nil { if err != nil {
fmt.Println("Failed:", err) fmt.Println("Failed to makeProtoList:", err)
os.Exit(1) os.Exit(1)
} }
protoList.Entry = append(protoList.Entry, site) protoList.Entry = append(protoList.Entry, site)
// Flatten and export plaintext list
if *exportLists != "" {
if existList != nil {
exportPlainTextList(existList, refName, pl)
} else {
exportedListSlice := strings.Split(*exportLists, ",")
for _, exportedListName := range exportedListSlice {
fileName := filepath.Join(dir, exportedListName)
_, err := os.Stat(fileName)
if err == nil || os.IsExist(err) {
existList = append(existList, exportedListName)
} else {
fmt.Printf("'%s' list does not exist in '%s' directory.\n", exportedListName, dir)
}
}
if existList != nil {
exportPlainTextList(existList, refName, pl)
}
}
}
} }
// Sort protoList so the marshaled list is reproducible // Sort protoList so the marshaled list is reproducible
sort.SliceStable(protoList.Entry, func(i, j int) bool { slices.SortFunc(protoList.Entry, func(a, b *router.GeoSite) int {
return protoList.Entry[i].CountryCode < protoList.Entry[j].CountryCode return strings.Compare(a.CountryCode, b.CountryCode)
}) })
protoBytes, err := proto.Marshal(protoList) protoBytes, err := proto.Marshal(protoList)
if err != nil { if err != nil {
fmt.Println("Failed:", err) fmt.Println("Failed to marshal:", err)
os.Exit(1) os.Exit(1)
} }
if err := os.WriteFile(filepath.Join(*outputDir, *outputName), protoBytes, 0644); err != nil { if err := os.WriteFile(filepath.Join(*outputDir, *outputName), protoBytes, 0644); err != nil {
fmt.Println("Failed:", err) fmt.Println("Failed to write output:", err)
os.Exit(1) os.Exit(1)
} else { } else {
fmt.Println(*outputName, "has been generated successfully.") fmt.Println(*outputName, "has been generated successfully.")